ChatGPT解决这个技术问题 Extra ChatGPT

CheckBox in RecyclerView keeps on checking different items

Here's the XML for my items inside the RecyclerView

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cvItems"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:layout_margin="2dp"
    card_view:cardElevation="0dp"
    card_view:contentPadding="0dp"
    card_view:cardBackgroundColor="#FFFFFF"
    >

    <LinearLayout
        android:orientation="horizontal"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent">
        <TextView
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="0.8"
            android:id="@+id/tvContent"
            android:textSize="15dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp" />
        <CheckBox
            android:id="@+id/cbSelect"
            android:layout_width="0dip"
            android:layout_weight="0.2"
            android:layout_height="match_parent"
            android:button="@drawable/cb_checked"
            android:gravity="center_horizontal"
            android:textAlignment="center"
            android:layout_gravity="center_horizontal" />
    </LinearLayout>
</android.support.v7.widget.CardView>

And here's the RecyclerView adapter that inflate the layout above for each of its items:

public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {

    private ArrayList<ObjectIncome> myItems = new ArrayList<>();

    public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
        try {
            mContext = context;
            myItems = getItems;
        }catch (Exception e){
            Log.e(FILE_NAME, "51: " + e.toString());
            e.printStackTrace();
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tvContent;
        public CheckBox cbSelect;

        public ViewHolder(View v) {
            super(v);
            tvContent = (TextView) v.findViewById(R.id.tvContent);
            cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        final ObjectIncome objIncome = myItems.get(position);
        String content = "<b>lalalla</b>";
        holder.tvContent.setText(Html.fromHtml(content));
    }
}

The problem is, let's say I have 10 items inside the RecyclerView. When I checked the checkbox on item 1,2,3 then I scroll down the RecyclerView, suddenly some of the other items eg items 8,9 is checked. And when I scroll up again, item 1 and 3 is checked but not item 2. Any idea why this happen?

Try to use this library, see ViewStates. It helps to save a state when scroll.

O
Oğuzhan Döngül

That's an expected behavior. You are not setting your checkbox selected or not. You are selecting one and View holder keeps it selected. You can add a boolean variable into your ObjectIncome object and keep your item's selection status.

You may look at my example. You can do something like that:

public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {

    private ArrayList<ObjectIncome> myItems = new ArrayList<>();

    public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
        try {
            mContext = context;
            myItems = getItems;
            }catch (Exception e){
            Log.e(FILE_NAME, "51: " + e.toString());
            e.printStackTrace();
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tvContent;
        public CheckBox cbSelect;

        public ViewHolder(View v) {
            super(v);
            tvContent = (TextView) v.findViewById(R.id.tvContent);
            cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        final ObjectIncome objIncome = myItems.get(position);
        String content = "<b>lalalla</b>";
        holder.tvContent.setText(Html.fromHtml(content));

        //in some cases, it will prevent unwanted situations
        holder.cbSelect.setOnCheckedChangeListener(null);

        //if true, your checkbox will be selected, else unselected
        holder.cbSelect.setChecked(objIncome.isSelected());

        holder.cbSelect.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    //set your object's last status
                    objIncome.setSelected(isChecked);
            }
        });

    }
}

It didn't work. You have to write holder.cbSelect.setOnCheckedChangeListener(null); before holder.cbSelect.setChecked(objIncome.isSelected())
is there any reason as to why setting holder.cbSelect.setOnCheckedChangeListener(null); works?
@oguzhand hi I tried your solution but its not working either way: with or without setting listener to null.
@oguzhand Here's the code from onBindViewHolder. @Override public void onBindViewHolder(final ItemHolder holder, int position) { holder.checkBox.setOnCheckedChangeListener(null); holder.checkBox.setSelected(list.get(position).isSelected()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { list.get(holder.getAdapterPosition()).setSelected(isChecked); } });
you dont have to call setOnCheckedChangeListener(null). just check if the button is pressed . like this: cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(final CompoundButton compoundButton, boolean checked) { if (compoundButton.isPressed()) { //do your work here}}
A
AmiNadimi

In short, its because of recycling the views and using them again!

how can you avoid that :

1.In onBindViewHolder check whether you should check or uncheck boxes. don't forget to put both if and else

if (...)
    holder.cbSelect.setChecked(true);
else
    holder.cbSelect.setChecked(false);

Put a listener for check box! whenever its checked statues changed, update the corresponding object too in your myItems array ! so whenever a new view is shown, it read the newest statue of the object.


Your second point was the key. Although it works best in a situation, when the initial dataset also has information about checked state (which is the case for me)
this is the more straight-to-the-point and correct answer. SetCheck for BOTH true and false in onBindViewHolder is the key
In my case, I have to save the data in the data model with default value isChecked false for all the data set on start, then onCheckChanged I just updated the isChecked value to true or false and as told in the answer implement that check either checked or not.
L
Lucas Sabino

Just add two override methods of RecyclerView

@Override
public long getItemId(int position) {
    return position;
}

@Override
public int getItemViewType(int position) {
    return position;
}

Don't do that!! It will bypass the recyclerView recycling mechanism and lose the whole point of using it.
No it won't, it just returns the exact position of every recycled view in the view holder.
Harish, maybe i'm missing something, but as far as I know, by doing this you actually tell the adapter that the number of items types is the items count. The meaning is that no item can be recycled because it has no simillar view. It is easy to test though. just log the viewHolder.itemView referrence inside onBindViewHolder and see if there are two viewHolders holding the same view referrence. The test should be on a long list so the recycling system will be executed.
It woked flawlessly, It saved my day.
in case you have more then 100 items in your recyclerview, this solution will load all items at once, this might cause OutOfMemoryException in case you have images means otherwise this solution is perfect @Kundan
R
Rana Ranvijay Singh

USE THIS ONLY IF YOU HAVE LIMITED NUMBER OF ITEMS IN YOUR RECYCLER VIEW. I tried using boolean value in model and keep the checkbox status, but it did not help in my case. What worked for me is this.setIsRecyclable(false);

public class ComponentViewHolder extends RecyclerView.ViewHolder {
    public MyViewHolder(View itemView) {
        super(itemView);
        ....
        this.setIsRecyclable(false);
    }

More explanation on this can be found here https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable()

NOTE: This is a workaround. To use it properly you can refer the document which states "Calls to setIsRecyclable() should always be paired (one call to setIsRecyclabe(false) should always be matched with a later call to setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally reference-counted." I don't know how to do this in code, if someone can provide more code on this.


please can you explain how to use it ?
isn't it waste of the logic behind recyclerView?
I tried this with long list which solved the random checking issue but when I scroll down and re-scroll up with long list, the checked checkboxes disappear :(
It's not a good idea to make view unrecyclable as it will drain memory and you'll lose most of benefits of recycler view.
I agree with you guys, @eren130 and Arthur. I have edited the post, and would very much appreciate if we can come up with a way to use setIsRecyclable(true / false); properly.
u
user6435056

You can use Model class to keep track of each recyclerView item's checkbox. Full reference is from : RecyclerView Checkbox Android

setTag and getTag is used to keep track of checkbox status. Check full reference link for more information. It also teaches how to send checked items to NEXTACTIVITY.

Make Model

public class Model {

    private boolean isSelected;
    private String animal;

    public String getAnimal() {
        return animal;
    }

    public void setAnimal(String animal) {
        this.animal = animal;
    }

    public boolean getSelected() {
        return isSelected;
    }

    public void setSelected(boolean selected) {
        isSelected = selected;
    }
}

create integer.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="btnplusview">1</integer>
    <integer name="btnpluspos">2</integer>
</resources>

Finally adapter looks like this:

 import android.content.Context;
 import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
    import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
 import android.widget.TextView;
 import android.widget.Toast;

 import java.util.ArrayList;


  public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

private LayoutInflater inflater;
public static ArrayList<Model> imageModelArrayList;
private Context ctx;

public CustomAdapter(Context ctx, ArrayList<Model> imageModelArrayList) {

    inflater = LayoutInflater.from(ctx);
    this.imageModelArrayList = imageModelArrayList;
    this.ctx = ctx;
}

@Override
public CustomAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = inflater.inflate(R.layout.rv_item, parent, false);
    MyViewHolder holder = new MyViewHolder(view);

    return holder;
}

@Override
public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, int position) {

    holder.checkBox.setText("Checkbox " + position);
    holder.checkBox.setChecked(imageModelArrayList.get(position).getSelected());
    holder.tvAnimal.setText(imageModelArrayList.get(position).getAnimal());

   // holder.checkBox.setTag(R.integer.btnplusview, convertView);
    holder.checkBox.setTag(position);
    holder.checkBox.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Integer pos = (Integer) holder.checkBox.getTag();
            Toast.makeText(ctx, imageModelArrayList.get(pos).getAnimal() + " clicked!", Toast.LENGTH_SHORT).show();

            if (imageModelArrayList.get(pos).getSelected()) {
                imageModelArrayList.get(pos).setSelected(false);
            } else {
                imageModelArrayList.get(pos).setSelected(true);
            }
        }
    });


}

@Override
public int getItemCount() {
    return imageModelArrayList.size();
}

class MyViewHolder extends RecyclerView.ViewHolder {

    protected CheckBox checkBox;
    private TextView tvAnimal;

    public MyViewHolder(View itemView) {
        super(itemView);

        checkBox = (CheckBox) itemView.findViewById(R.id.cb);
        tvAnimal = (TextView) itemView.findViewById(R.id.animal);
    }

}

}


j
just_user

Using Kotlin the only thing which solved this problem for me was to clear the OnCheckedChangeListener before setting the variable and then create a new OnCheckedChangeListener after checked has been set.

I do the following in my RecyclerView.ViewHolder

task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
    item.status = checked
    ...
    do more stuff
    ...
}

This is perfectly working. I don't why but this only works when anyone using KOTLIN!
That's really working. What he has done is very clever and obvious at the same time. task.setOnCheckedChangeListener(null) just considered the checkbox as a brand new one as we are just interested in new events only. Again very clever.
E
Eren

I recommend that not to use checkBox.setOnCheckedChangeListener in recyclerViewAdapter. Because on scrolling recyclerView, checkBox.setOnCheckedChangeListener will be fired by adapter. It's not safe. Instead, use checkBox.setOnClickListener to interact with user inputs.

For example:

     public void onBindViewHolder(final ViewHolder holder, int position) {
        /*
         .
         .
         .
         .
         .
         .
        */

        holder.checkBoxAdapterTasks.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean isChecked =  holder.checkBoxAdapterTasks.isChecked();
                if(isChecked){
                    //checkBox clicked and checked
                }else{
                    //checkBox clicked and unchecked
                }

            }
        });

    }

t
tollestheanimal

In my case this worked.

@Override
public void onViewRecycled(MyViewHolder holder) {
    holder.checkbox.setChecked(false); // - this line do the trick
    super.onViewRecycled(holder);
}

For some reason, this solution works perfect !
H
Hamza Mehboob

It might be very late but the simplest of all answers is to assign the check state in bind view holder. Recycler view will check and apply that state when reusing.

override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
binding.checkbox.isChecked=item.isChecked
}

Maintain that state in your class. (Assign a initial default value)

class MyItem{
val isChecked:Boolean=false
}

On click listener, do your stuff and assign the state to class variable. In my case, I have delegate clicklistener in view. So, it is like this in Adapter

binding.checkbox.setOnClickListener {
                    onClickListener.invoke(item) 
}

Then, in view, I am doing this:

val adapter = MyItem { item->
                viewModel.checkedContactsList.value?.let {  list ->
                    if(list.contains(item)){
                        item.isChecked=false
                        list.remove(item)
                    }else{
                        item.isChecked=true
                        list.add(item)
                    }
                }
            }

G
Gratien Asimbahwe

As stated above, the checked state of the object should be included within object properties. In some cases you may need also to change the object selection state by clicking on the object itself and let the CheckBox inform about the actual state (either selected or unselected). The checkbox will then use the state of the object at the actual position of the given adapter which is (by default/in most cases) the position of the element in the list.

Check the snippet below, it may be useful.

import android.content.Context; import android.graphics.Bitmap; import android.net.Uri; import android.provider.MediaStore; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; import java.io.File; import java.io.IOException; import java.util.List; public class TakePicImageAdapter extends RecyclerView.Adapter{ private Context context; private List imageList; public TakePicImageAdapter(Context context, List imageList) { this.context = context; this.imageList = imageList; } @Override public TakePicImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view= LayoutInflater.from(context).inflate(R.layout.image_item,parent,false); return new ViewHolder(view); } @Override public void onBindViewHolder(final TakePicImageAdapter.ViewHolder holder, final int position) { File file=new File(imageList.get(position).getPath()); try { Bitmap bitmap= MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.fromFile(file)); holder.image.setImageBitmap(bitmap ); } catch (IOException e) { e.printStackTrace(); } holder.selectImage.setOnCheckedChangeListener(null); holder.selectImage.setChecked(imageList.get(position).isSelected()); holder.selectImage.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { holder.selectImage.setChecked(isChecked); imageList.get(position).setSelected(isChecked); } }); holder.image.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (imageList.get(position).isSelected()) { imageList.get(position).setSelected(false); holder.selectImage.setChecked(false); }else { imageList.get(position).setSelected(true); holder.selectImage.setChecked(true); } } }); } @Override public int getItemCount() { return imageList.size(); } public class ViewHolder extends RecyclerView.ViewHolder { public ImageView image;public CheckBox selectImage; public ViewHolder(View itemView) { super(itemView); image=(ImageView)itemView.findViewById(R.id.image); selectImage=(CheckBox) itemView.findViewById(R.id.ch); } } }


C
Community

Use an array to hold the state of the items

In the adapter use a Map or a SparseBooleanArray (which is similar to a map but is a key-value pair of int and boolean) to store the state of all the items in our list of items and then use the keys and values to compare when toggling the checked state

In the Adapter create a SparseBooleanArray

// sparse boolean array for checking the state of the items

    private SparseBooleanArray itemStateArray= new SparseBooleanArray();

then in the item click handler onClick() use the state of the items in the itemStateArray to check before toggling, here is an example

        @Override
        public void onClick(View v) {
            int adapterPosition = getAdapterPosition();
            if (!itemStateArray.get(adapterPosition, false)) {
                mCheckedTextView.setChecked(true);
                itemStateArray.put(adapterPosition, true);
            }
            else  {
                mCheckedTextView.setChecked(false);
                itemStateArray.put(adapterPosition, false);
            }
        }

also, use sparse boolean array to set the checked state when the view is bound

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(position);
}

@Override
public int getItemCount() {
    if (items == null) {
        return 0;
    }
    return items.size();
}

 void loadItems(List<Model> tournaments) {
    this.items = tournaments;
    notifyDataSetChanged();
}


class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    CheckedTextView mCheckedTextView;

    ViewHolder(View itemView) {
        super(itemView);
        mCheckedTextView = (CheckedTextView) itemView.findViewById(R.id.checked_text_view);
        itemView.setOnClickListener(this);
    }

    void bind(int position) {
        // use the sparse boolean array to check
        if (!itemStateArray.get(position, false)) {
            mCheckedTextView.setChecked(false);}
        else {
            mCheckedTextView.setChecked(true);
        }
    }

and final adapter will be like this


G
Granjero

I had the same problem in a RecyclerView list with switches, and solved it using @oguzhand answer, but with this code inside the checkedChangeListener:

if (buttonView.isPressed()) {
    if (isChecked) {
        group.setSelected(true);
    } else {
        group.setSelected(false);
    }
}else{
    if (isChecked) {
        buttonView.setChecked(false);
    } else {
        buttonView.setChecked(true);
    }
}

(Where 'group' is the entity I want to select/deselect)


T
Taras Lozovyi

You need to separate onBindViewHolder(logic) interactions with CheckBox and user interactions with checkbox. I used OnCheckedChangeListener for user interactions (obviously) and ViewHolder.bind() for logic, thats why you need to set checked listener to null before setting up holder and after holder is ready - configure checked listener for user interactions.

boolean[] checkedStatus = new boolean[numberOfRows];

@Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        final ViewHolderItem itemHolder = (ViewHolderItem) holder;

        //holder.bind should not trigger onCheckedChanged, it should just update UI
        itemHolder.checkBox.setOnCheckedChangeListener(null);

        itemHolder.bind(position);

        itemHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    checkedStatus[holder.getAdapterPosition()] = true;
                    performCheckedActions(); //your logic here
                } else {
                    checkedStatus[holder.getAdapterPosition()] = false;
                    performUncheckedActions(); //your logic here
                }
            }
        });
    }

public void bind(int position) {
            boolean checked = checkedStatus[position];
            if (checked) {
                checkBox.setChecked(false);
            } else {
                checkBox.setChecked(true);
            }
        }

V
Vishal kharb

The problem of this solution which i found is, by creating a static global array and using it in "onBindViewHolder" ADAPER CLASS , in which i created all global varaibles/objects needed.

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {
private Context context;
public static class PersonViewHolder extends RecyclerView.ViewHolder {

    CardView cv;
    TextView question,category;
    TextView personAge;
    ImageView upvote;
    Button b1;
    public static int k;
    private int visibleThreshold = 5;
    public static int i=0;
     static int  check[]; //Static array
    PersonViewHolder(View itemView,int i) {
        super(itemView);
        if(i==PersonViewHolder.k)
        {
            b1=(Button)itemView.findViewById(R.id.loadmore);

        }
        else
        {
            cv = (CardView)itemView.findViewById(R.id.cv);
            question = (TextView)itemView.findViewById(R.id.question);
            category = (TextView)itemView.findViewById(R.id.text_categ);
            personAge = (TextView)itemView.findViewById(R.id.text1);
            upvote = (ImageView)itemView.findViewById(R.id.upvote);

        }

    }

}

Here(IN CONSTRUCTOR of RVADAPTER CLASS) i gave size to the array equals to the size of /no of items i'm going to display in the recycler view

List<Person> persons;

RVAdapter(List<Person> persons){
    this.persons = persons;
    PersonViewHolder.check=new int[persons.size()];
    PersonViewHolder.k=persons.size();
}

BindViewHolder , I ,Applied this concept on a button ,when i click on a button ,the background image of the button changes. object of button i used is names as " upvote" ,as "i" holds the position of each item in recycler view ,i used it as an index of array which is working as a flag and which is keeping track of status of elements.

@Override
public void onBindViewHolder(final PersonViewHolder personViewHolder, final int i) {
    if(i==PersonViewHolder.k) {
        personViewHolder.b1.setText("load more");

    }
    else
     {
        personViewHolder.question.setText(persons.get(i).name);
        personViewHolder.personAge.setText(persons.get(i).age);

         if(personViewHolder.check[i]==0)
         {personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);
         }
         else
         {
             personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);

         }

         personViewHolder.upvote.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if(personViewHolder.check[i]==0)
                 {personViewHolder.check[i]=1;
                     personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);


                 }
                 else
                 {personViewHolder.check[i]=0;
                     personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);

                 }


             }
         });
        // personViewHolder.personPhoto.setImageResource(persons.get(i).photoId);
    }

}

L
LA_Homie

I've had the same issue. When I was clicking on item's toggle button in my recyclerView checked Toggle button appeared in every 10th item (for example if it was clicked in item with 0 index, items with 9, 18, 27 indexes were getting clicked too). Firstly, my code in onBindViewHolder was:

if (newsItems.get(position).getBookmark() == 1) {
            holder.getToggleButtonBookmark().setChecked(true);
        }

But then I added Else statement

if (newsItems.get(position).getBookmark() == 1) {
            holder.getToggleButtonBookmark().setChecked(true);
//else statement prevents auto toggling
        } else{
            holder.getToggleButtonBookmark().setChecked(false);
        }

And the problem was solved


Thank you. Else part will clear the checkbox if it check by default when recycling view the same view.
M
Mahmoud Ayman

okay there is a lot of answers here i will post my code and i will simply explain what i did... it maybe help juniors like me :D.

1- Objective:

we will create a list of RecyclerView that has CheckBox and RadioButton, something like this:

https://i.stack.imgur.com/fzTti.png

public class ModelClass {
private String time;
private boolean checked;
private boolean free;
private boolean paid;

public TherapistScheduleModel(String time, boolean checked, boolean free, boolean paid) {
    this.time = time;
    this.checked = checked;
    this.free = free;
    this.paid = paid;
}

public boolean isFree() {
    return free;
}

public void setFree(boolean free) {
    this.free = free;
}

public boolean isPaid() {
    return paid;
}

public void setPaid(boolean paid) {
    this.paid = paid;
}

public String getTime() {
    return time;
}

public void setTime(String time) {
    this.time = time;
}

public boolean getChecked() {
    return checked;
}

public void setChecked(boolean checked) {
    this.checked= checked;
}
}

3-My Amazing Adapter

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private ListAllListeners listAllListeners;
private ArrayList<ModelClass> mDataList;

public MyAdapter(Context context, ArrayList<ModelClass> mDataList,
                             ListAllListeners listAllListeners) {
    this.mDataList = mDataList;
    this.listAllListeners = listAllListeners;
    this.context = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view = inflater.inflate(R.layout.single_view, parent, false);
    return new MyViewHolder(view);
}

@Override
public int getItemCount() {
    if (mDataList != null)
        return mDataList.size();
    else
        return 0;
}

@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
     //important to:
    //setOnCheckedChangeListener to 'null'
    holder.checkBoxTime.setOnCheckedChangeListener(null);
    holder.freeRB.setOnCheckedChangeListener(null);
    holder.paidRB.setOnCheckedChangeListener(null);

    //Check Box
            holder.checkBoxTime.setText(mDataList.get(holder.getAdapterPosition()).getTime());
    //here we check if the item is checked or not from the model.
    if(mDataList.get(holder.getAdapterPosition()).getChecked())
        holder.checkBoxTime.setChecked(true);
    else
        holder.checkBoxTime.setChecked(false);

    holder.checkBoxTime.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if (b) {
                mDataList.get(holder.getAdapterPosition()).setChecked(true);
                listAllListeners.onItemCheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
            }
            else {
                mDataList.get(holder.getAdapterPosition()).setChecked(false);
                listAllListeners.onItemUncheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
            }
        }
    });

    //Radio Buttons

    if(mDataList.get(holder.getAdapterPosition()).isFree())
        holder.freeRB.setChecked(true);
    else
        holder.freeRB.setChecked(false);
    holder.freeRB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if (b) {
                mDataList.get(holder.getAdapterPosition()).setFree(true);
                listAllListeners.onFreeCheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
            } else {
                mDataList.get(holder.getAdapterPosition()).setFree(false);
                listAllListeners.onFreeUncheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
            }
        }
    });

   //***and so on to paidRB***

}//end onBindViewHolder()

public interface ListAllListeners {
//here is a list of clicked listeners to use them as you want ;).
//you can get a list of checked or unChecked of all 
        void onItemCheck(String checkBoxName, int position);
        void onItemUncheck(String checkBoxName, int position);
        void onFreeCheck(String name, int pos);
        void onFreeUncheck(String name, int pos);
        void onPaidCheck(String name, int pos);
        void onPaidUncheck(String name, int pos);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        CheckBox checkBoxTime;
        RadioButton freeRB, paidRB;

        MyViewHolder(View itemView) {
            super(itemView);
            checkBoxTime = itemView.findViewById(R.id.timeCheckBox);
            freeRB = itemView.findViewById(R.id.freeRadioBtn);
            paidRB = itemView.findViewById(R.id.paidRadioBtn);
        }
    }//end class MyViewHolder

    }//end class

3- In Activity you get them something like this:

myAdapter= new MyAdapter(getActivity().getApplicationContext(), mDataList,
                new MyAdapter.ListAllListeners() {

                    @Override
                    public void onItemCheck(String checkBoxName, int position) {
                        Toast.makeText(getActivity(), "" + checkBoxName + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onItemUncheck(String checkBoxName, int position) {
                        Toast.makeText(getActivity(), "" + checkBoxName + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFreeCheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFreeUncheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPaidCheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPaidUncheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }
                });

E
Erfan

this will happened when use setOnCheckedChangeListener instead of that use setObClickListener and inside that just do this easy handle :

   if (list.get(position).isCheck())
            {
                list.get(position).setCheck(false);
            }
            else
            {
                list.get(position).setCheck(true);
            }

NOTE : in your list model add one boolean variable with name check and set getter and setter for that , in above case mine is setCheck and isCheck

hope it help some one if so +vote to this answer


D
Dharman

This is a Kotlin Solution That Worked for Me

class SpecialtyFragmentRecyclerAdapter : RecyclerView.Adapter<SpecialtyFragmentRecyclerAdapter.SpecialtyViewHolder>(){

    private var _specialtySet = mutableSetOf(
        "Yoruba Attires",
        "Hausa Attires",
        "Senator",
        "Embroidery",
        "Africa Fashion",
        "School Uniform",
        "Military and Para-Military Uniforms",
        "Igbo Attires",
        "South-South Attires",
        "Kaftans",
        "Contemporary",
        "Western Fashion",
        "Caps"
    ).toSortedSet()

    val specialtySet: Set<String> get() = _specialtySet
    val savedSpecialtySet = mutableSetOf<String>().toSortedSet()

    inner class SpecialtyViewHolder(
        var itemBinding: SpecialtyFragmentRecyclerItemBinding
    ) :
        RecyclerView.ViewHolder(itemBinding.root) {

        fun bind(specialty: String) = with(itemBinding) {

            specialtyFragmentYorubaAttiresCheckBox.text = specialty
            specialtyFragmentYorubaAttiresCheckBox.isChecked = savedSpecialtySet.contains(specialty)

     //AREA OF INTEREST

     //Either Setting the CheckBox onCheckChangeListener to works
     specialtyFragmentYorubaAttiresCheckBox.setOnCheckedChangeListener(null)

     specialtyFragmentYorubaAttiresCheckBox.setOnCheckedChangeListener(
                CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
                    if (buttonView.isPressed) { //OR this Also Works {Check if the Button is Pressed Before verifying the Checked State}
                        if (isChecked) {
                            savedSpecialtySet.add(specialty) //Perform Your Operation for Checked State
                        } else {
                            savedSpecialtySet.remove(specialty) //Perform Your Operation for unChecked State
                        }
                    }
                }
            )

        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SpecialtyViewHolder {
        val viewBinding = SpecialtyFragmentRecyclerItemBinding
            .inflate(LayoutInflater.from(parent.context), parent, false)
        return SpecialtyViewHolder(viewBinding)
    }

    override fun onBindViewHolder(holder: SpecialtyViewHolder, position: Int) {
        val specialty = _specialtySet.elementAt(position)
        holder.bind(specialty)
    }

    override fun getItemCount(): Int {
        return _specialtySet.size
    }

    fun populateList(list: MutableList<String>) {
        savedSpecialtySet.addAll(list)
        _specialtySet.addAll(list)
        notifyDataSetChanged()
    }

    fun addNewSpecialty(specialty: String) {
        _specialtySet.add(specialty.trim())
        savedSpecialtySet.add(specialty.trim())
        notifyDataSetChanged()
    }

    fun removeSpecialty(element: String) {
        _specialtySet.remove(element)
        savedSpecialtySet.remove(element)
        notifyDataSetChanged()
    }
}

v
velraj

public class TagYourDiseaseAdapter extends RecyclerView.Adapter { private ReCyclerViewItemClickListener mRecyclerViewItemClickListener; private Context mContext;

List<Datum> deviceList = Collections.emptyList();

/**
 * Initialize the values
 *
 * @param context : context reference
 * @param devices : data
 */

public TagYourDiseaseAdapter(Context context, List<Datum> devices,
                             ReCyclerViewItemClickListener mreCyclerViewItemClickListener) {
    this.mContext = context;
    this.deviceList = devices;
    this.mRecyclerViewItemClickListener = mreCyclerViewItemClickListener;
}


/**
 * @param parent   : parent ViewPgroup
 * @param viewType : viewType
 * @return ViewHolder
 * <p>
 * Inflate the Views
 * Create the each views and Hold for Reuse
 */
@Override
public TagYourDiseaseAdapter.OrderHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag_disease, parent, false);
    TagYourDiseaseAdapter.OrderHistoryViewHolder myViewHolder = new TagYourDiseaseAdapter.OrderHistoryViewHolder(view);
    return myViewHolder;
}


/**
 * @param holder   :view Holder
 * @param position : position of each Row
 *                 set the values to the views
 */
@Override
public void onBindViewHolder(final TagYourDiseaseAdapter.OrderHistoryViewHolder holder, final int position) {
    Picasso.with(mContext).load(deviceList.get(position).getIconUrl()).into(holder.document);
    holder.name.setText(deviceList.get(position).getDiseaseName());

    holder.radioButton.setOnCheckedChangeListener(null);
    holder.radioButton.setChecked(deviceList.get(position).isChecked());

    //if true, your checkbox will be selected, else unselected
    //holder.radioButton.setChecked(objIncome.isSelected());

    holder.radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            deviceList.get(position).setChecked(isChecked);
        }
    });


}

@Override
public int getItemCount() {
    return deviceList.size();
}


/**
 * Create The view First Time and hold for reuse
 * View Holder for Create and Hold the view for ReUse the views instead of create again
 * Initialize the views
 */

public class OrderHistoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    ImageView document;
    TextView name;
    CheckBox radioButton;

    public OrderHistoryViewHolder(View itemView) {
        super(itemView);
        document = itemView.findViewById(R.id.img_tag);
        name = itemView.findViewById(R.id.text_tag_name);
        radioButton = itemView.findViewById(R.id.rdBtn_tag_disease);
        radioButton.setOnClickListener(this);
        //this.setIsRecyclable(false);
    }


    @Override
    public void onClick(View view) {
        mRecyclerViewItemClickListener.onItemClickListener(this.getAdapterPosition(), view);
    }
}

}


S
Suraj Rao

this is due to again and again creating view ,best option is clear cache before setting adapter

recyclerview.setItemViewCacheSize(your array.size());

M
Mudassar Ashraf

Complete example public class ChildAddressAdapter extends RecyclerView.Adapter {

private Activity context;
private List<AddressDetail> addressDetailList;
private int selectedPosition = -1;

public ChildAddressAdapter(Activity context, List<AddressDetail> addressDetailList) {
    this.context = context;
    this.addressDetailList = addressDetailList;
}

@NonNull
@Override
public CartViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

    LayoutInflater inflater = LayoutInflater.from(context);
    View myView = inflater.inflate(R.layout.address_layout, parent, false);
    return new CartViewHolder(myView);
}

@Override
public void onBindViewHolder(@NonNull CartViewHolder holder, int position) {

    holder.adress_checkbox.setOnClickListener(view -> {
        selectedPosition = holder.getAdapterPosition();
        notifyDataSetChanged();
    });

    if (selectedPosition==position){
        holder.adress_checkbox.setChecked(true);
    }
    else {
        holder.adress_checkbox.setChecked(false);
    }


}

@Override
public int getItemCount() {
    return  addressDetailList.size();
}

class CartViewHolder extends RecyclerView.ViewHolder
{
    TextView address_text,address_tag;
    CheckBox adress_checkbox;

    CartViewHolder(View itemView) {
        super(itemView);
        address_text = itemView.findViewById(R.id.address_text);
        address_tag = itemView.findViewById(R.id.address_tag);
        adress_checkbox = itemView.findViewById(R.id.adress_checkbox);
    }
}

}


v
vivekanandhan muthusamy

Solution is while check box is checked. need to store this seperate list. and use that list to populate check box in recycler view.

You can refer this link.

https://blog.oziomaogbe.com/2017/10/18/android-handling-checkbox-state-in-recycler-views.html


H
Handelika

If it is not late; this is actually recyclerView general problem. You can put your recyclerView into a nested Scroll view and then add one line code to your adapter. all is done.

In your activity or fragment;

 <androidx.core.widget.NestedScrollView
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    >

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="vertical">

                        <androidx.recyclerview.widget.RecyclerView
                            android:id="@+id/recyclerView"
                            android:layout_width="match_parent"
                            android:layout_height="match_parent"
                            />
                    </LinearLayout>

                </androidx.core.widget.NestedScrollView>

and in your activity where you set adapter add this.


 ViewCompat.setNestedScrollingEnabled(recyclerView, false);

 LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
 //your adapter code...
 recyclerView.setAdapter(textSearchAdapter);


s
shannoga

What worked for me is to nullify the listeners on the viewHolder when the view is going to be recycled (onViewRecycled):

 override fun onViewRecycled(holder: AttendeeViewHolder) {
            super.onViewRecycled(holder)
            holder.itemView.hasArrived.setOnCheckedChangeListener(null);
            holder.itemView.edit.setOnClickListener { null }
        }

P
Pravin Yadav

Adding setItemViewCacheSize(int size) to recyclerview and passing size of list solved my problem.

mycode:

mrecyclerview.setItemViewCacheSize(mOrderList.size());
mBinding.mrecyclerview.setAdapter(mAdapter);

Source: https://stackoverflow.com/a/46951440/10459907