0

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.

M.Toy
  • 41
  • 4
  • 1
    Asked 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
  • 2
    Hello 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 Answers1

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);
    }
}
Dharman
  • 21,838
  • 18
  • 57
  • 107
M.Toy
  • 41
  • 4
  • 1
    Next 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
  • Sorry, im editing this to answer :) – M.Toy Sep 25 '20 at 11:49
  • 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
  • 1
    Take 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