I have an activity with recyclerview that containts RecyclerView with CheckableTextViews. There like 15 elements in the list and it is scrollable. I use RecyclerView Adapter to fill the list, write to the textviews and listen the click event an I use the click event to toggle the checkbox state. So far everything is working as expected. But when I check one of the items and scroll down and back up. I see that element is unchecked, then one of the other elements get checked by itself. Everytime I scroll up and down some other element got checked. I cannot find anything about this issue.
Asked
Active
Viewed 62 times
0
-
1Asked bazillion times ... you need to store state in element itself and restore it after view is rebinded – Selvin Sep 25 '20 at 10:27
-
It's not a bug, those items just get recycled once they are out of the screen (hence recyclerview). You have to save their state in some variable. – einUsername Sep 25 '20 at 10:27
-
2Hello M. Toy, welcome to Stack Overflow; it's better if you provide [a minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of what you're doing. In this particular case, it's better to "search" before asking, since this question is a very very basic RecyclerView feature. If you want to see an example where an item of a recycler view has a click listener that alters its state, check [this repository](https://github.com/Gryzor/CheckBoxCounter) that contains a very simple example. Check the `withViewModel` branch if you want to see a VM working too. – Martin Marconcini Sep 25 '20 at 10:32
-
In short, the data that drives the adapter (that then gets bound to "views" in the form of ViewHolders), should be treated a "read-only" by the Adapter/Views. The views should notify of an event (and push it via the click listener) like: Hey, I've been clicked!, and *someone else* (possibly a ViewModel/Repository) will pick this, **mutate the data** and push/emit/etc. a new "state" (a new list with a value changed to signal the ViewHolder that when binding, the checkbox must be "checked") for example. Look at the above repo and you will understand. – Martin Marconcini Sep 25 '20 at 10:36
-
To put it simply, whatever data class you are populating in your recyclerview, just add a boolean variable (eg. isChecked) in it and update it's value (to true) when you check it in your recyclerview. – Nikhil Kumar Sep 25 '20 at 10:39
1 Answers
0
I'm now sending the checkbox state as a parameter to ViewHolder
and update the View with setChecked
. Problem solved.
class WorkerViewHolder extends RecyclerView.ViewHolder {
private final CheckedTextView textViewRow;
WorkerViewHolder(View v) {
super(v);
textViewRow = v.findViewById(R.id.textViewRow);
v.setOnClickListener(v1 -> {
Personnel p = (Personnel) v.getTag();
textViewRow.setChecked(!textViewRow.isChecked());
checklist[workers.indexOf(p)] = textViewRow.isChecked();
});
}
void setLine(Personnel worker, boolean check) {
textViewRow.getRootView().setTag(worker);
textViewRow.setText(worker.Nick);
textViewRow.setChecked(check);
}
}
-
1Next time please update the question, not post an answer, this doesn't answer your question, does it? :) – Martin Marconcini Sep 25 '20 at 11:39
-
1`Personnel p = (Personnel) v.getTag();` -> you're storing the data (model) in the actual view. Problem 1. `checklist[workers.indexOf(p)] = textViewRow.isChecked();` => you're modifying the model from the view directly. Problem 2. – Martin Marconcini Sep 25 '20 at 11:40
-
This violates the two principles it outlined above: "the data that drives the adapter (that then gets bound to "views" in the form of ViewHolders), should be treated a "read-only" by the Adapter/Views. The views should notify of an event" – Martin Marconcini Sep 25 '20 at 11:41
-
-
I understand the problem 1. But how can I modify the data other way. Only way to get the state of the checkbox is within ClickListener and its inside the View. – M.Toy Sep 25 '20 at 12:02
-
1Take a look at this very simple repo: https://github.com/Gryzor/CheckBoxCounter look at how I do it. :) – Martin Marconcini Sep 25 '20 at 12:04