RecyclerView 中的 CheckBox 不断检查不同的项目
CheckBox in RecyclerView keeps on checking different items
这是我在 RecyclerView
中的项目的 XML
<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>
下面是 RecyclerView 适配器,它为上面的每个项目扩充布局:
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));
}
}
问题是,假设我在 RecyclerView 中有 10 个项目。当我选中项目 1、2、3 上的复选框然后向下滚动 RecyclerView 时,突然选中了其他一些项目,例如项目 8、9。当我再次向上滚动时,第 1 项和第 3 项被选中,但未选中第 2 项。知道为什么会这样吗?
这是预期的行为。您没有将复选框设置为选中或未选中。您正在选择一个并且 View holder 保持选中状态。您可以将布尔变量添加到 ObjectIncome 对象中并保持项目的选择状态。
你可以看看我的例子。你可以这样做:
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);
}
});
}
}
总之,是因为回收了views,再次使用了!
如何避免这种情况:
1.In onBindViewHolder
检查您是否应该选中或取消选中复选框。
不要忘记把 if 和 else
if (...)
holder.cbSelect.setChecked(true);
else
holder.cbSelect.setChecked(false);
- 为复选框设置监听器!每当其选中的雕像发生变化时,也要在
myItems
数组中更新相应的对象!因此,每当显示新视图时,它都会读取对象的最新雕像。
我在带有开关的 RecyclerView 列表中遇到了同样的问题,并使用@oguzhand 答案解决了它,但是在 checkedChangeListener 中使用了这段代码:
if (buttonView.isPressed()) {
if (isChecked) {
group.setSelected(true);
} else {
group.setSelected(false);
}
}else{
if (isChecked) {
buttonView.setChecked(false);
} else {
buttonView.setChecked(true);
}
}
(其中 'group' 是我想要 select/deselect 的实体)
仅当您的回收站视图中的项目数量有限时才使用此选项。
我尝试在模型中使用布尔值并保持复选框状态,但这对我没有帮助。
对我有用的是 this.setIsRecyclable(false);
public class ComponentViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
....
this.setIsRecyclable(false);
}
注意: 这是一种解决方法。要正确使用它,您可以参考说明 "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." 的文档,如果有人可以提供更多代码,我不知道如何在代码中执行此操作。
您需要将 onBindViewHolder(logic) 与 CheckBox 的交互和用户与复选框的交互分开。我使用 OnCheckedChangeListener 进行用户交互(很明显),使用 ViewHolder.bind() 进行逻辑,这就是为什么您需要在设置 holder 之前和 holder 准备好之后将 checked 侦听器设置为 null - 为用户交互配置 checked 侦听器。
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);
}
}
我建议不要在 recyclerViewAdapter
中使用 checkBox.setOnCheckedChangeListener
。因为在滚动 recyclerView 时,checkBox.setOnCheckedChangeListener
将被适配器触发。 不安全。相反,使用 checkBox.setOnClickListener
与用户输入进行交互。
例如:
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
}
}
});
}
我发现这个解决方案的问题是,通过创建一个静态全局
数组并在 "onBindViewHolder" 中使用它
ADAPER CLASS ,我在其中创建了所有需要的全局 varaibles/objects。
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);
}
}
}
这里(在 RVADAPTER 的构造函数中 CLASS)我给数组的大小等于我将在回收器视图中显示的项目的大小/数量
List<Person> persons;
RVAdapter(List<Person> persons){
this.persons = persons;
PersonViewHolder.check=new int[persons.size()];
PersonViewHolder.k=persons.size();
}
BindViewHolder,我,将这个概念应用到一个按钮上,当我点击一个按钮时,按钮的背景图像会改变。
我使用的按钮对象名称为“upvote”,因为 "i" 在回收站视图中保存每个项目的位置,我将它用作数组的索引,该数组用作标志并跟踪状态元素。
@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);
}
}
您可以使用模型 class 来跟踪每个 recyclerView 项目的复选框。
完整参考来自:RecyclerView Checkbox Android
setTag 和 getTag 用于跟踪复选框状态。查看完整参考 link 了解更多信息。它还教导如何将选中的项目发送到 NEXTACTIVITY。
制作模型
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;
}
}
创建integer.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="btnplusview">1</integer>
<integer name="btnpluspos">2</integer>
</resources>
最后适配器看起来像这样:
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);
}
}
}
对我有用的是在要回收视图时使 viewHolder 上的侦听器无效 (onViewRecycled
):
override fun onViewRecycled(holder: AttendeeViewHolder) {
super.onViewRecycled(holder)
holder.itemView.hasArrived.setOnCheckedChangeListener(null);
holder.itemView.edit.setOnClickListener { null }
}
只需添加两个RecyclerView
的覆盖方法
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return position;
}
我遇到了同样的问题。当我在我的 recyclerView 中单击项目的切换按钮时,选中的切换按钮出现在每 10 个项目中(例如,如果在索引为 0 的项目中单击它,则索引为 9、18、27 的项目也会被单击)。首先,我在 onBindViewHolder 中的代码是:
if (newsItems.get(position).getBookmark() == 1) {
holder.getToggleButtonBookmark().setChecked(true);
}
但后来我添加了Else语句
if (newsItems.get(position).getBookmark() == 1) {
holder.getToggleButtonBookmark().setChecked(true);
//else statement prevents auto toggling
} else{
holder.getToggleButtonBookmark().setChecked(false);
}
问题解决了
好的,这里有很多答案这里我会post我的代码,我会简单地解释我做了什么......它可能会帮助像我这样的年轻人: D.
1- Objective:
我们将创建一个包含 RecyclerView
的列表,其中包含 CheckBox
和 RadioButton
,如下所示:
2-型号Class
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-我的神奇适配器
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- 在 Activity 中你会得到这样的结果:
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();
}
});
public class TagYourDiseaseAdapter 扩展 RecyclerView.Adapter {
私人 ReCyclerViewItemClickListener mRecyclerViewItemClickListener;
私有上下文 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);
}
}
}
使用 Kotlin 为我解决这个问题的唯一方法是在设置变量之前清除 OnCheckedChangeListener
,然后在设置 checked
之后创建一个新的 OnCheckedChangeListener
。
我在 RecyclerView.ViewHolder
中执行以下操作
task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
item.status = checked
...
do more stuff
...
}
如上所述,对象的选中状态应包含在对象属性中。在某些情况下,您可能还需要通过单击对象本身来更改对象选择状态,并让 CheckBox 告知实际状态(选中或未选中)。然后复选框将使用对象在给定适配器实际位置的状态,这是(default/in 大多数情况下)列表中元素的位置。
检查下面的代码片段,它可能会有用。
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<TakePicImageAdapter.ViewHolder>{
private Context context;
private List<Image> imageList;
public TakePicImageAdapter(Context context, List<Image> 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);
}
}
}
在我的例子中,这是有效的。
@Override
public void onViewRecycled(MyViewHolder holder) {
holder.checkbox.setChecked(false); // - this line do the trick
super.onViewRecycled(holder);
}
当使用 setOnCheckedChangeListener
而不是使用 setObClickListener
时会发生这种情况,而在其中只需执行以下简单操作:
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
希望它对某些人有所帮助 +投票给这个答案
使用数组来保存项目的状态
在适配器中使用 Map 或 SparseBooleanArray(类似于 map 但它是 int 和 boolean 的键值对)来存储项目列表中所有项目的状态然后在切换选中状态时使用键和值进行比较
在适配器中创建一个 SparseBooleanArray
// sparse boolean array for checking the state of the items
private SparseBooleanArray itemStateArray= new SparseBooleanArray();
然后在项目点击处理程序中 onClick()
在切换之前使用 itemStateArray 中项目的状态来检查,这里是一个例子
@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);
}
}
另外,使用稀疏布尔数组设置视图绑定时的选中状态
@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);
}
}
最终适配器会像 this
将 setItemViewCacheSize(int size) 添加到 recyclerview 并传递列表大小解决了我的问题。
我的代码:
mrecyclerview.setItemViewCacheSize(mOrderList.size());
mBinding.mrecyclerview.setAdapter(mAdapter);
来源:
这是因为一次又一次创建视图,最好的选择是在设置适配器之前清除缓存
recyclerview.setItemViewCacheSize(your array.size());
完整示例
public class ChildAddressAdapter 扩展 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);
}
}
}
解决方法是选中复选框。需要存储这个单独的列表。并使用该列表填充回收站视图中的复选框。
你可以参考这个link。
https://blog.oziomaogbe.com/2017/10/18/android-handling-checkbox-state-in-recycler-views.html
这是对我有用的 Kotlin 解决方案
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()
}
}
如果不晚;这实际上是 recyclerView 的普遍问题。您可以将您的 recyclerView 放入嵌套的滚动视图中,然后将一行代码添加到您的适配器中。一切都完成了。
在您的activity或片段中;
<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>
并在设置适配器的 activity 中添加此内容。
ViewCompat.setNestedScrollingEnabled(recyclerView, false);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
//your adapter code...
recyclerView.setAdapter(textSearchAdapter);
可能已经很晚了,但最简单的答案是在绑定视图持有者中分配检查状态。回收站视图将在重复使用时检查并应用该状态。
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
binding.checkbox.isChecked=item.isChecked
}
在 class 中保持该状态。 (分配初始默认值)
class MyItem{
val isChecked:Boolean=false
}
在点击侦听器上,执行您的操作并将状态分配给 class 变量。在我的例子中,我有委托点击监听器。所以,在 Adapter
中是这样的
binding.checkbox.setOnClickListener {
onClickListener.invoke(item)
}
那么在视图中,我是这样做的:
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)
}
}
}
这是我在 RecyclerView
中的项目的 XML<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>
下面是 RecyclerView 适配器,它为上面的每个项目扩充布局:
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));
}
}
问题是,假设我在 RecyclerView 中有 10 个项目。当我选中项目 1、2、3 上的复选框然后向下滚动 RecyclerView 时,突然选中了其他一些项目,例如项目 8、9。当我再次向上滚动时,第 1 项和第 3 项被选中,但未选中第 2 项。知道为什么会这样吗?
这是预期的行为。您没有将复选框设置为选中或未选中。您正在选择一个并且 View holder 保持选中状态。您可以将布尔变量添加到 ObjectIncome 对象中并保持项目的选择状态。
你可以看看我的例子。你可以这样做:
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);
}
});
}
}
总之,是因为回收了views,再次使用了!
如何避免这种情况:
1.In onBindViewHolder
检查您是否应该选中或取消选中复选框。
不要忘记把 if 和 else
if (...)
holder.cbSelect.setChecked(true);
else
holder.cbSelect.setChecked(false);
- 为复选框设置监听器!每当其选中的雕像发生变化时,也要在
myItems
数组中更新相应的对象!因此,每当显示新视图时,它都会读取对象的最新雕像。
我在带有开关的 RecyclerView 列表中遇到了同样的问题,并使用@oguzhand 答案解决了它,但是在 checkedChangeListener 中使用了这段代码:
if (buttonView.isPressed()) {
if (isChecked) {
group.setSelected(true);
} else {
group.setSelected(false);
}
}else{
if (isChecked) {
buttonView.setChecked(false);
} else {
buttonView.setChecked(true);
}
}
(其中 'group' 是我想要 select/deselect 的实体)
仅当您的回收站视图中的项目数量有限时才使用此选项。
我尝试在模型中使用布尔值并保持复选框状态,但这对我没有帮助。
对我有用的是 this.setIsRecyclable(false);
public class ComponentViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
....
this.setIsRecyclable(false);
}
注意: 这是一种解决方法。要正确使用它,您可以参考说明 "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." 的文档,如果有人可以提供更多代码,我不知道如何在代码中执行此操作。
您需要将 onBindViewHolder(logic) 与 CheckBox 的交互和用户与复选框的交互分开。我使用 OnCheckedChangeListener 进行用户交互(很明显),使用 ViewHolder.bind() 进行逻辑,这就是为什么您需要在设置 holder 之前和 holder 准备好之后将 checked 侦听器设置为 null - 为用户交互配置 checked 侦听器。
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);
}
}
我建议不要在 recyclerViewAdapter
中使用 checkBox.setOnCheckedChangeListener
。因为在滚动 recyclerView 时,checkBox.setOnCheckedChangeListener
将被适配器触发。 不安全。相反,使用 checkBox.setOnClickListener
与用户输入进行交互。
例如:
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
}
}
});
}
我发现这个解决方案的问题是,通过创建一个静态全局 数组并在 "onBindViewHolder" 中使用它 ADAPER CLASS ,我在其中创建了所有需要的全局 varaibles/objects。
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);
}
}
}
这里(在 RVADAPTER 的构造函数中 CLASS)我给数组的大小等于我将在回收器视图中显示的项目的大小/数量
List<Person> persons;
RVAdapter(List<Person> persons){
this.persons = persons;
PersonViewHolder.check=new int[persons.size()];
PersonViewHolder.k=persons.size();
}
BindViewHolder,我,将这个概念应用到一个按钮上,当我点击一个按钮时,按钮的背景图像会改变。 我使用的按钮对象名称为“upvote”,因为 "i" 在回收站视图中保存每个项目的位置,我将它用作数组的索引,该数组用作标志并跟踪状态元素。
@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);
}
}
您可以使用模型 class 来跟踪每个 recyclerView 项目的复选框。 完整参考来自:RecyclerView Checkbox Android
setTag 和 getTag 用于跟踪复选框状态。查看完整参考 link 了解更多信息。它还教导如何将选中的项目发送到 NEXTACTIVITY。
制作模型
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;
}
}
创建integer.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="btnplusview">1</integer>
<integer name="btnpluspos">2</integer>
</resources>
最后适配器看起来像这样:
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);
}
}
}
对我有用的是在要回收视图时使 viewHolder 上的侦听器无效 (onViewRecycled
):
override fun onViewRecycled(holder: AttendeeViewHolder) {
super.onViewRecycled(holder)
holder.itemView.hasArrived.setOnCheckedChangeListener(null);
holder.itemView.edit.setOnClickListener { null }
}
只需添加两个RecyclerView
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemViewType(int position) {
return position;
}
我遇到了同样的问题。当我在我的 recyclerView 中单击项目的切换按钮时,选中的切换按钮出现在每 10 个项目中(例如,如果在索引为 0 的项目中单击它,则索引为 9、18、27 的项目也会被单击)。首先,我在 onBindViewHolder 中的代码是:
if (newsItems.get(position).getBookmark() == 1) {
holder.getToggleButtonBookmark().setChecked(true);
}
但后来我添加了Else语句
if (newsItems.get(position).getBookmark() == 1) {
holder.getToggleButtonBookmark().setChecked(true);
//else statement prevents auto toggling
} else{
holder.getToggleButtonBookmark().setChecked(false);
}
问题解决了
好的,这里有很多答案这里我会post我的代码,我会简单地解释我做了什么......它可能会帮助像我这样的年轻人: D.
1- Objective:
我们将创建一个包含 RecyclerView
的列表,其中包含 CheckBox
和 RadioButton
,如下所示:
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-我的神奇适配器
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- 在 Activity 中你会得到这样的结果:
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();
}
});
public class TagYourDiseaseAdapter 扩展 RecyclerView.Adapter { 私人 ReCyclerViewItemClickListener mRecyclerViewItemClickListener; 私有上下文 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);
}
}
}
使用 Kotlin 为我解决这个问题的唯一方法是在设置变量之前清除 OnCheckedChangeListener
,然后在设置 checked
之后创建一个新的 OnCheckedChangeListener
。
我在 RecyclerView.ViewHolder
task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
item.status = checked
...
do more stuff
...
}
如上所述,对象的选中状态应包含在对象属性中。在某些情况下,您可能还需要通过单击对象本身来更改对象选择状态,并让 CheckBox 告知实际状态(选中或未选中)。然后复选框将使用对象在给定适配器实际位置的状态,这是(default/in 大多数情况下)列表中元素的位置。
检查下面的代码片段,它可能会有用。
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<TakePicImageAdapter.ViewHolder>{
private Context context;
private List<Image> imageList;
public TakePicImageAdapter(Context context, List<Image> 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);
}
}
}
在我的例子中,这是有效的。
@Override
public void onViewRecycled(MyViewHolder holder) {
holder.checkbox.setChecked(false); // - this line do the trick
super.onViewRecycled(holder);
}
当使用 setOnCheckedChangeListener
而不是使用 setObClickListener
时会发生这种情况,而在其中只需执行以下简单操作:
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
希望它对某些人有所帮助 +投票给这个答案
使用数组来保存项目的状态
在适配器中使用 Map 或 SparseBooleanArray(类似于 map 但它是 int 和 boolean 的键值对)来存储项目列表中所有项目的状态然后在切换选中状态时使用键和值进行比较
在适配器中创建一个 SparseBooleanArray
// sparse boolean array for checking the state of the items
private SparseBooleanArray itemStateArray= new SparseBooleanArray();
然后在项目点击处理程序中 onClick()
在切换之前使用 itemStateArray 中项目的状态来检查,这里是一个例子
@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);
}
}
另外,使用稀疏布尔数组设置视图绑定时的选中状态
@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);
}
}
最终适配器会像 this
将 setItemViewCacheSize(int size) 添加到 recyclerview 并传递列表大小解决了我的问题。
我的代码:
mrecyclerview.setItemViewCacheSize(mOrderList.size());
mBinding.mrecyclerview.setAdapter(mAdapter);
来源:
这是因为一次又一次创建视图,最好的选择是在设置适配器之前清除缓存
recyclerview.setItemViewCacheSize(your array.size());
完整示例
public class ChildAddressAdapter 扩展 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);
}
}
}
解决方法是选中复选框。需要存储这个单独的列表。并使用该列表填充回收站视图中的复选框。
你可以参考这个link。
https://blog.oziomaogbe.com/2017/10/18/android-handling-checkbox-state-in-recycler-views.html
这是对我有用的 Kotlin 解决方案
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()
}
}
如果不晚;这实际上是 recyclerView 的普遍问题。您可以将您的 recyclerView 放入嵌套的滚动视图中,然后将一行代码添加到您的适配器中。一切都完成了。
在您的activity或片段中;
<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>
并在设置适配器的 activity 中添加此内容。
ViewCompat.setNestedScrollingEnabled(recyclerView, false);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
//your adapter code...
recyclerView.setAdapter(textSearchAdapter);
可能已经很晚了,但最简单的答案是在绑定视图持有者中分配检查状态。回收站视图将在重复使用时检查并应用该状态。
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
binding.checkbox.isChecked=item.isChecked
}
在 class 中保持该状态。 (分配初始默认值)
class MyItem{
val isChecked:Boolean=false
}
在点击侦听器上,执行您的操作并将状态分配给 class 变量。在我的例子中,我有委托点击监听器。所以,在 Adapter
中是这样的binding.checkbox.setOnClickListener {
onClickListener.invoke(item)
}
那么在视图中,我是这样做的:
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)
}
}
}