Reputation: 53
I have a recycler view where I fetch details from my database. An ArrayList of FriendEntity is given by the room database which works fine. I want to first have an unchecked box image for the whole list, and if I tap the image, it should change into a checked box image. But when I am achieving this, when I click on 1st item of recyclerView, my 16th item of recyclerView gets changed from unchecked to checked itself and the same goes with the 2nd and 17th, 3rd, and 18th and so on. I don't know if this is a problem with a recycler view adapter or something else.
Data Class FriendEntity :
@Entity(tableName = "Friends")
data class FriendEntity (
@PrimaryKey val friend_name: String ,
@ColumnInfo(name = "debt") val debt: Float )
ExpenseActivity.xml for RecyclerView:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
ExpenseActivity.kt:
class ExpenseActivity : AppCompatActivity() {
private lateinit var binding: ActivityExpenseBinding
private lateinit var layoutManager: LinearLayoutManager
private lateinit var recyclerAdapter: FriendListAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this@ExpenseActivity,R.layout.activity_expense)
layoutManager = LinearLayoutManager(this@ExpenseActivity)
val friendsList = GetFriends(applicationContext).execute().get()
binding.txtDef.visibility = if(friendsList.size==0) View.VISIBLE else View.GONE
binding.recyclerList.visibility = if(friendsList.size!=0) View.VISIBLE else View.GONE
recyclerAdapter = FriendListAdapter(this@ExpenseActivity,friendsList)
binding.recyclerList.layoutManager = layoutManager
binding.recyclerList.adapter = recyclerAdapter
} }
I have tested my GetFriends it works fine.
FriendListAdapter.kt:
class FriendListAdapter(val context: Context, private val itemList: ArrayList<FriendEntity>):
RecyclerView.Adapter<FriendListAdapter.FriendViewHolder>() {
private var checkList: ArrayList<FriendEntity> = arrayListOf()
class FriendViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val txtFriendName: TextView = view.findViewById(R.id.txtFriendName)
val imgCheck: ImageView = view.findViewById(R.id.imgCheck)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FriendViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recycler_single_friend,parent,false)
return FriendViewHolder(view)
}
override fun getItemCount() = itemList.size
override fun onBindViewHolder(holder: FriendViewHolder, position: Int) {
val itemObject = itemList[position]
holder.txtFriendName.text = itemObject.friend_name
holder.imgCheck.setOnClickListener {
if(checkList.contains(itemObject)) {
holder.imgCheck.setImageResource(R.drawable.ic_cb_empty)
checkList.remove(itemObject)
} else {
holder.imgCheck.setImageResource(R.drawable.ic_cb_full)
checkList.add(itemObject)
}
}
} }
The problem I get is :
The 16th automatically changes
The 17th gets automatically clicked
Also, the behavior is so much problematic that when I tap on the 16th image, it doesn't respond.2nd time clicking makes it unchecked. Same with the 17th image. I guess there is some problem with the adapter.
Then I tried having a small view for individual items so that my screen gets a total of 20 items altogether. Now when I tap on the 1st image, the 24th gets selected, 2nd click changes the 25th. It means it clicks items who are not in the view right now but how?
I paste a GDriveLink for the video here if anyone doesn't understand it till now: Link to video of the problem
I want to know how to remove this error?
Upvotes: 1
Views: 687
Reputation: 53
The answer provided by Aashit Shah is a good answer surely and would do the work perfectly. But just in case, if anyone doesn't wanna change the entity class (in my case, I didn't want to do any changes in my POJO/data class because I am sending whole object of FriendEntity to Room Database) so you can have a work around.
You can add an Array of Boolean as a member variable of the adapter class like this
private val boolArray = ArrayList(Collections.nCopies(itemCount,false))
This boolArray will keep records of boolean status locally. And hence we can use it onBindViewHolder in the same manner as above:
override fun onBindViewHolder(holder: FriendViewHolder, position: Int) {
val itemObject = itemList[holder.adapterPosition]
holder.txtFriendName.text = itemObject.friend_name
holder.imgCheck.setImageResource(if(boolArray[holder.adapterPosition]) R.drawable.ic_cb_full else R.drawable.ic_cb_empty)
holder.imgCheck.setOnClickListener {
if(boolArray[holder.adapterposition]) {
holder.imgCheck.setImageResource(R.drawable.ic_cb_empty)
checkList.remove(itemObject)
boolArray[holder.adapterPosition] = false
} else {
holder.imgCheck.setImageResource(R.drawable.ic_cb_full)
checkList.add(itemObject)
boolArray[holder.adapterPosition] = true
}
}
}
The advantage of using this work around is that it won't change your previous database and this boolArray will be having a lifecycle of just an object and hence space efficiency.
Upvotes: 1
Reputation: 578
the problem you are facing is due to the reuse of view holders. The simplest solution would be to create a boolean field in you POJO class FriendEntity .
data class FriendEntity (
@PrimaryKey val friend_name: String ,
@ColumnInfo(name = "debt") val debt: Float
val isSelected //To store current status)
Now check this status while binding your data and set the checkbox according to it.
override fun onBindViewHolder(holder: FriendViewHolder, position: Int) {
val itemObject = itemList[position]
//Image selection will be done at binding.
if(itemObject.isSelected){
holder.imgCheck.setImageResource(R.drawable.ic_cb_full)
}else{
holder.imgCheck.setImageResource(R.drawable.ic_cb_empty)
}
holder.txtFriendName.text = itemObject.friend_name
holder.imgCheck.setOnClickListener {
//Just change the status and notify the change.
itemObject.isSelected = !itemObject.isSelected
notifyItemChanged(position)
}
Upvotes: 4