I am new to android. I am working on SQL CRUD operations. The app runs without problems but in my adapter, I just replace the bindItems method with another way of initializing Ui elements in order to use variables in the Update & Delete operation.
Unfortunately, the app stops and give me FATAL EXCEPTION: Main error.
I don't know how to fix it, please help me.
This is the Adapter with bindItems method (The app works fine)
class AdapterRecord (): RecyclerView.Adapter<AdapterRecord.MyViewHolder>() {
private var context: Context? = null
private var notesList: List<Note>? = null
constructor(context: Context?, notesList: List<Note>?) : this() {
this.context = context
this.notesList = notesList
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(note: Note) {
val tvTitle = itemView.findViewById(R.id.tvTitle) as TextView
val tvDescription = itemView.findViewById(R.id.tvDescription) as TextView
tvTitle.text = note.name
tvDescription.text = note.description
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.note_ticket, parent,false)
return MyViewHolder(itemView)
}
override fun getItemCount() : Int {
return notesList!!.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bindItems(notesList!!.get(position))
}
}
And this is the adapter without bindItems (The app stopped working with exception)
class AdapterRecord (): RecyclerView.Adapter<AdapterRecord.MyViewHolder>() {
private var context: Context? = null
private var notesList: List<Note>? = null
constructor(context: Context?, notesList: List<Note>?) : this() {
this.context = context
this.notesList = notesList
}
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvTitle = itemView.findViewById<TextView>(R.id.tvTitle)
val tvDescription = itemView.findViewById<TextView>(R.id.tvDescription)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.note_ticket, parent,false)
return MyViewHolder(itemView)
}
override fun getItemCount() : Int {
return notesList!!.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val note = notesList!!.get(position)
//get data
val id = note.id
val name = note.name
val description = note.description
//set data
holder.tvTitle.text = name
holder.tvDescription.text = description
}
}
I tried to resolve it and I succeeded.
I create a new class to declare Ui elements but before declaring there is set on content method to the layout which add notes to database then the app works fine.
This is the class :
class UiElements : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
val tvTitle = findViewById<TextView>(R.id.tvTitle)
val tvDescription = findViewById<TextView>(R.id.tvDescription)
val moreBtn = findViewById<ImageButton>(R.id.moreBtn)
}
}
Related
Could someone help me with this error?
class PokemonAdapter(
private val context: Context,
private val pokemons: MutableList<PokemonItem> = mutableListOf(),
var onItemClickListener: (pokemon: PokemonItem) -> Unit = {}
) : RecyclerView.Adapter<PokemonAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PokemonAdapter.ViewHolder {
val viewCriada = LayoutInflater.from(context).inflate(
R.layout.item_pokemon,
parent,
false
)
return ViewHolder(viewCriada)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.vincula(pokemons[position])
}
override fun getItemCount() = pokemons.size
fun add(pokemons: List<PokemonItem>) {
this.pokemons.clear()
this.pokemons.addAll(pokemons)
notifyDataSetChanged()
}
inner class ViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
private lateinit var pokemon: PokemonItem
private val campoNome by lazy { itemView.item_pokemon_nome }
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemon
campoNome.text = pokemonItem.nome
}
}
}
Here it appears you're setting pokemon to itself but it has not actually been set yet:
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemon // < -- This line
campoNome.text = pokemonItem.nome
}
Perhaps you meant this:
fun vincula(pokemonItem: PokemonItem) {
this.pokemon = pokemonItem
campoNome.text = pokemonItem.nome
}
the problem is in the function vincula
this.pokemon = pokemonItem
I am new to Android & Kotlin. I want to click my row and intent it to other activity. That's why I tried to create an interface in my Adapter but it does not work. How can I create it?
My adapter code:
class NoteAdapter(private val titleTextArray: ArrayList<String>, private val imageArray: ArrayList<String>) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
class NoteHolder (view: View) : RecyclerView.ViewHolder(view) {
var recyclerTitleText: TextView ?= null
var recyclerImageView: ImageView ?= null
init {
recyclerTitleText = view.findViewById(R.id.recyclerTitleText)
recyclerImageView = view.findViewById(R.id.recyclerImage)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.recycler_row, parent, false)
return NoteHolder(view)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.recyclerTitleText?.text = titleTextArray[position]
Picasso.get().load(imageArray[position]).into(holder.recyclerImageView)
}
override fun getItemCount(): Int {
return titleTextArray.size
}
}
With Kotlin, you could to use a lambda (higher order function) to fetch the user clicks in the holder. For example, a base approach could be the next:
class YourAdapter(
private val listener: (String) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private val yourDataList = emptyList<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.compound_checkbox_terms_layout, parent, false)
return YourViewHolder(view)
}
override fun getItemCount(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
private inner class YourViewHolder(view: View) : RecyclerView.ViewHolder(view) {
init {
if (adapterPosition != RecyclerView.NO_POSITION) {
val selectedData = yourDataList[adapterPosition]
listener.invoke(selectedData)
}
}
}
}
Pass the activity context to the adapter from Activity so it can launch intent
Activity Code :
mAdapter = new CustomAdapter(this, imageArray, ArrayList);
Adapter Code :
class NoteAdapter(private val titleTextArray: ArrayList, private val imageArray: ArrayList, private val context : Context) : RecyclerView.Adapter<NoteAdapter.NoteHolder>() {
class NoteHolder (view: View) : RecyclerView.ViewHolder(view) {
var recyclerTitleText: TextView?= null
var recyclerImageView : ImageView?= null
init {
recyclerTitleText = view.findViewById(R.id.recyclerTitleText)
recyclerImageView = view.findViewById(R.id.recyclerImage)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteHolder {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.recycler_row, parent, false)
return NoteHolder(view)
}
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
holder.recyclerTitleText?.text = titleTextArray[position]
Picasso.get().load(imageArray[position]).into(holder.recyclerImageView)
holder.recyclerImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(context, NesneTani.class);
context.startActivity(intent);
}
});
}
override fun getItemCount(): Int {
return titleTextArray.size
}
}
Create an Interface to catch the click event. (position is optional)
interface OnItemClickListener {
fun onItemClick(position: Int)
}
add OnItemClickListener as a parameter in your Adapter class
class NoteAdapter(
private val titleTextArray: ArrayList<String>,
private val imageArray: ArrayList<String>,
private val listener: OnItemClickListener
) : RecyclerView.Adapter<NoteAdapter.NoteHolder>()
Then do below inside onBindViewHolder
override fun onBindViewHolder(holder: NoteHolder, position: Int) {
listener.onItemClick(position)
}
now inside your activity, implement the OnItemClickListener
class ActivityName implements OnItemClickListener{}
and while initiating adapter, do this
mAdapter = new CustomAdapter(this, imageArray, ArrayList);
Then inside the overridden method do what you want
override fun onItemClick(position: Int) {
//start the activity you want.
}
I am trying to store the values from retrofit into a database by pressing a AddtoCartButton but app is getting crashed when I press this button. I am able to retrieve the result from retrofit without any issues but onclicklistener is the issue.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
}
}
}
fun addtocart(view: View)
{
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
print testvar
}
Error:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
Below code solves the purpose.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
val addtocart = itemView.findViewById<Button>(R.id.idCart)
addtocart!!.setOnClickListener {
print $testvar
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
startActivity(intent) }
}
}
}
I am trying to retrieve a testvar value from inner class Adapter to addtocart function but I am not getting the value. I am able to retrieve value inside Adapter class but not outside the class.
Code:
inner class MoviesAdapter : RecyclerView.Adapter<MoviesAdapter.MovieViewHolder>() {
private val movies: MutableList<Movie> = mutableListOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(layoutInflater.inflate(R.layout.item_movie_layout, parent, false))
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
holder.bindModel(movies[position])
}
fun setMovies(data: List<Movie>) {
movies.addAll(data)
notifyDataSetChanged()
}
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val movieGenreTxt : TextView = itemView.findViewById(R.id.movieGenre)
val movieYearTxt : TextView = itemView.findViewById(R.id.movieYear)
val movieAvatarImage : ImageView = itemView.findViewById(R.id.movieAvatar)
val movieDescription: TextView =itemView.findViewById(R.id.movieDescription)
fun bindModel(movie: Movie) {
// movieTitleTxt.text = movie.name
movieGenreTxt.text = movie.menu
movieYearTxt.text = movie.price
Picasso.get().load(movie.picture).into(movieAvatarImage)
movieDescription.text=movie.description
var movieName:String= movie.name!!
var testvar=movie.name.toString()
}
}
}
fun addtocart(view: View)
{
Toast.makeText(context,"Success on Click", Toast.LENGTH_SHORT).show()
print testvar
}
Think you are looking to put a button in the RecyclerView.
In that case setOnclicklistener within the
inner class MovieViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val addtocart = itemView.findViewById<Button>(R.id.viewprofile)
addtocart!!.setOnClickListener {
}}
I have RecyclerView adapter in Kotlin and when a user clicks on categoryPhoto, I want to open a new activity. How should I implement this?
class CategoryAdapter(private val categoryList: List<Category>, private val context: Context) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var categoryName = view.text_view_category_name
var categoryPhoto = view.image_view_category
var cardView = view.card_view_category
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MyViewHolder(parent.inflate(R.layout.category_list_row))
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
val displayMetrics = context.resources.displayMetrics
val finalHeight = displayMetrics.widthPixels / 2
holder.cardView.layoutParams.height = finalHeight
holder.categoryName.text = category.oc
holder.categoryPhoto.loadUrl(category.icon)
}
override fun getItemCount(): Int {
return categoryList.size
}}
Just add click listener as parameter to constructor of your adapter.
class CategoryAdapter(
private val categoryList: List<Category>,
private val context: Context,
private val onClickListener: (View, Category) -> Unit
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
...
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
...
holder.itemView.setOnClickListener { view ->
onClickListener.invoke(view, category)
}
}
...
}
Then you can use as following:
fun initList() {
adapter = CategoryAdapter(
categoryList = ...,
context = ...,
onClickListener = { view, category -> openActivity(view, category) }
}
Off-top. Some optional improvements for code above
Create typealias for lambda. Make your code more readable.
typealias MyCategoryClickListener = (View, Category) -> Unit
class CategoryAdapter(
private val categoryList: List<Category>,
private val context: Context,
private val onClickListener: MyCategoryClickListener
) : RecyclerView.Adapter<CategoryAdapter.MyViewHolder>() {
Omit invoke call of listener. Lambda can be called just like function.
holder.itemView.setOnClickListener { view ->
onClickListener(view, category)
}
Replace lambda with reference when creating adapter
fun initList() {
adapter = CategoryAdapter(
categoryList = ...,
context = ...,
onClickListener = this::openActivity)
}
fun openActivity(view: View, category: Category) {
...
}
You can do it in your onBindViewHolder(...)
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val category = categoryList[position]
// Set height of cardview based on screen width
val displayMetrics = context.resources.displayMetrics
val finalHeight = displayMetrics.widthPixels / 2
holder.cardView.layoutParams.height = finalHeight
holder.categoryName.text = category.oc
holder.categoryPhoto.loadUrl(category.icon)
holder.categoryPhoto.setOnClickListener { view ->
// categoryPhoto clicked.
// start your activity here
}
}
Do like this
class RecyclerListAdapter: RecyclerView.Adapter {
var context: Context? = null
var listData: ArrayList? = null
Step 1: Activity ref..................................
var activityref:MainActivity?=null
constructor(context: Context?, listData: ArrayList<ItemDetails>?, activityref: MainActivity?) : super() {
this.context = context
this.listData = listData
this.activityref = activityref
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewsHolder {
val view = LayoutInflater.from(context).inflate(R.layout.row_list, parent, false)
return ViewsHolder(view)
}
override fun getItemCount(): Int {
return listData!!.size
}
override fun onBindViewHolder(holder: ViewsHolder?, position: Int) {
holder?.item=listData?.get(position)
holder!!.first!!.setText(holder.item?.First)
holder.second!!.setText(holder.item?.Second)
holder.third!!.setText(holder.item?.Third)
Step 2 OnClick on item.....................
holder.third!!.setOnClickListener{
activityref?.OnItemClicked(holder.item!!)
}
}
class ViewsHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
var item:ItemDetails?=null
var first: TextView? = null;
var second: TextView? = null;
var third: TextView? = null;
init {
first = itemView?.findViewById(R.id.first)
second = itemView?.findViewById(R.id.second)
third = itemView?.findViewById(R.id.third)
}
}
}