RecyclerView中的单选

我知道recyclerview类中没有缺省的select方法,但是我尝试了以下方法,

public void onBindViewHolder(ViewHolder holder, final int position) { holder.mTextView.setText(fonts.get(position).getName()); holder.checkBox.setChecked(fonts.get(position).isSelected()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked) { for (int i = 0; i < fonts.size(); i++) { fonts.get(i).setSelected(false); } fonts.get(position).setSelected(isChecked); } } }); } 

在尝试这些代码时,我得到了预期的输出,但是完全没有。

我会用图像来解释你。

默认情况下,第一个项目是从我的适配器中select的

在这里输入图像描述

然后我试图select第二,然后第三,然后第四,最后第五,

在这里输入图像描述

这里只有第五个select,但是所有五个都被选中了。

如果我将列表滚动到底部并再次返回顶部,

在这里输入图像描述

我得到了我的预期,

我怎样才能克服这个问题? 有些时候如果我滚动列表非常快一些其他项目被选中。 如何克服这个问题呢?

更新

虽然我试图使用notifyDataSetChanged()fonts.get(position).setSelected(isChecked); 我有以下例外,

 java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:1462) at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:2982) at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:7493) at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:4338) at com.app.myapp.screens.RecycleAdapter.onRowSelect(RecycleAdapter.java:111) 

任何解决scheme将不胜感激。

谢谢。

问题的解决scheme:

 public class yourRecyclerViewAdapter extends RecyclerView.Adapter<yourRecyclerViewAdapter.yourViewHolder> { private static CheckBox lastChecked = null; private static int lastCheckedPos = 0; ... ... public void onBindViewHolder(ViewHolder holder, final int position) { holder.mTextView.setText(fonts.get(position).getName()); holder.checkBox.setChecked(fonts.get(position).isSelected()); holder.checkBox.setTag(new Integer(position)); //for default check in first item if(position == 0 && fonts.get(0).isSelected() && holder.checkBox.isChecked()) { lastChecked = holder.checkBox; lastCheckedPos = 0; } holder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { CheckBox cb = (CheckBox)v; int clickedPos = ((Integer)cb.getTag()).intValue(); if(cb.isChecked) { if(lastChecked != null) { lastChecked.setChecked(false); fonts.get(lastCheckedPos).setSelected(false); } lastChecked = cb; lastCheckedPos = clickedPos; } else lastChecked = null; fonts.get(clickedPos).setSelected(cb.isChecked); } }); } ... ... } 

希望这个帮助!

它太晚了,仍然发布可能有助于其他人使用下面的代码作为参考检查RecyclerView中的单个项目

 /** * Created by subrahmanyam on 28-01-2016, 04:02 PM. */ public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.ViewHolder> { private final String[] list; private int lastCheckedPosition = -1; public SampleAdapter(String[] list) { this.list = list; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = View.inflate(parent.getContext(), R.layout.sample_layout, null); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.choiceName.setText(list[position]); holder.radioButton.setChecked(position == lastCheckedPosition); } @Override public int getItemCount() { return list.length; } public class ViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.choice_name) TextView choiceName; @Bind(R.id.choice_select) RadioButton radioButton; public ViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); radioButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { lastCheckedPosition = getAdapterPosition(); //because of this blinking problem occurs so //i have a suggestion to add notifyDataSetChanged(); // notifyItemRangeChanged(0, list.length);//blink list problem notifyDataSetChanged(); } }); } } } 

看起来这里有两件事情:

(1)意见被重用,所以老的听众仍然在场。

(2)您正在更改数据而不通知更改的适配器。

我将分别解决每个问题。

(1)查看重用

基本上,在onBindViewHolder你给一个已经初始化的ViewHolder ,它已经包含一个视图。 该ViewHolder可能已经或可能不会被绑定到某些数据!

请注意这一点的代码在这里:

 holder.checkBox.setChecked(fonts.get(position).isSelected()); 

如果持有者先前已被绑定,那么checkbox已经有一个侦听器,用于检查状态改变! 这个监听器正在被触发,这就是导致你的IllegalStateException

一个简单的解决scheme是在调用setChecked之前删除监听器。 一个优雅的解决scheme将需要更多的知识你的意见 – 我鼓励你寻找一个更好的方式来处理这个问题。

(2)数据改变时通知适配器

代码中的侦听器正在更改数据的状态,而不通知适配器后续的更改。 我不知道你的意见是如何工作的,所以这可能会也可能不是问题。 通常,当数据状态发生变化时,您需要让适配器了解它。

RecyclerView.Adapter有许多选项可供select,包括notifyItemChanged ,它告诉它一个特定的项目已经改变了状态。 这可能对你的使用很好

 if(isChecked) { for (int i = 0; i < fonts.size(); i++) { if (i == position) continue; Font f = fonts.get(i); if (f.isSelected()) { f.setSelected(false); notifyItemChanged(i); // Tell the adapter this item is updated } } fonts.get(position).setSelected(isChecked); notifyItemChanged(position); } 

在设置setChecked()之前,您需要清除OnCheckedChangeListener

 @Override public void onBindViewHolder(final ViewHolder holder, int position) { holder.mRadioButton.setOnCheckedChangeListener(null); holder.mRadioButton.setChecked(position == mCheckedPosition); holder.mRadioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mCheckedPosition = position; notifyDataSetChanged(); } }); } 

这样就不会触发java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling错误java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

只需使用mCheckedPosition保存状态

 @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.checkBox.setChecked(position == mCheckedPostion); holder.checkBox.setOnClickListener(v -> { if (position == mCheckedPostion) { holder.checkBox.setChecked(false); mCheckedPostion = -1; } else { mCheckedPostion = position; notifyDataSetChanged(); } }); } 

这可能帮助那些想单个单选button的人工作 – > 单选buttonRecycleView – 要点

如果不支持lamdaexpression式请改用此方法

  View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { mSelectedItem = getAdapterPosition(); notifyItemRangeChanged(0, mItems.size()); } }; 

干杯

发生这种情况是因为RecyclerView,顾名思义,在回收 ViewHolders方面做得很好。 这意味着每一个ViewHolder,当它看起来不见了(实际上它比看起来要多一点,但是这样简化它是有意义的),它就被回收了。 这意味着RecyclerView需要这个已经膨胀的ViewHolder,并用数据集中另一个项目的元素replace它的元素。

现在,这里发生的事情是,一旦你向下滚动,你的第一个选定的ViewHolders就会被看到,它们将被回收并用于你的数据集的其他位置。 一旦你再次上升,绑定到前5个项目的ViewHolders 现在不是必须的

这就是为什么你应该在适配器中保留一个内部variables来记住每个项目的select状态 。 这样,在onBindViewHolder方法中,您可以知道是否选中了ViewHolder当前被绑定的项目,并相应地修改一个View,在这种情况下,您的RadioButton的状态(尽pipe如果您打算使用CheckBoxselect多个项目)。

如果您想了解更多关于RecyclerView及其内部工作的信息,我邀请您检查我在GitHub上开始的一个项目FancyAdapters 。 它是一组适配器,用于实现元素的select拖放轻扫以消除function。 也许通过检查代码,您可以很好地了解RecyclerView如何工作。

这简单的一个为我工作

 private RadioButton lastCheckedRB = null; ... @Override public void onBindViewHolder(final CoachListViewHolder holder, final int position) { holder.priceRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { RadioButton checked_rb = (RadioButton) group.findViewById(checkedId); if (lastCheckedRB != null) { lastCheckedRB.setChecked(false); } //store the clicked radiobutton lastCheckedRB = checked_rb; } }); 
  public class GetStudentAdapter extends RecyclerView.Adapter<GetStudentAdapter.MyViewHolder> { private List<GetStudentModel> getStudentList; Context context; RecyclerView recyclerView; public class MyViewHolder extends RecyclerView.ViewHolder { TextView textStudentName; RadioButton rbSelect; public MyViewHolder(View view) { super(view); textStudentName = (TextView) view.findViewById(R.id.textStudentName); rbSelect = (RadioButton) view.findViewById(R.id.rbSelect); } } public GetStudentAdapter(Context context, RecyclerView recyclerView, List<GetStudentModel> getStudentList) { this.getStudentList = getStudentList; this.recyclerView = recyclerView; this.context = context; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.select_student_list_item, parent, false); return new MyViewHolder(itemView); } @Override public void onBindViewHolder(final MyViewHolder holder, final int position) { holder.textStudentName.setText(getStudentList.get(position).getName()); holder.rbSelect.setChecked(getStudentList.get(position).isSelected()); holder.rbSelect.setTag(position); // This line is important. holder.rbSelect.setOnClickListener(onStateChangedListener(holder.rbSelect, position)); } @Override public int getItemCount() { return getStudentList.size(); } private View.OnClickListener onStateChangedListener(final RadioButton checkBox, final int position) { return new View.OnClickListener() { @Override public void onClick(View v) { if (checkBox.isChecked()) { for (int i = 0; i < getStudentList.size(); i++) { getStudentList.get(i).setSelected(false); } getStudentList.get(position).setSelected(checkBox.isChecked()); notifyDataSetChanged(); } else { } } }; } } 

请试试这个…对我有用..

在适配器中,取一个稀疏的布尔数组。

 SparseBooleanArray sparseBooleanArray; 

在构造函数初始化这个,

  sparseBooleanArray=new SparseBooleanArray(); 

在绑定持有人添加,

 @Override public void onBindViewHolder(DispositionViewHolder holder, final int position) { holder.tv_disposition.setText(dispList.get(position).getName()); if(sparseBooleanArray.get(position,false)) { holder.rd_disp.setChecked(true); } else { holder.rd_disp.setChecked(false); } setClickListner(holder,position); } private void setClickListner(final DispositionViewHolder holder, final int position) { holder.rd_disp.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sparseBooleanArray.clear(); sparseBooleanArray.put(position, true); notifyDataSetChanged(); } }); } 

rd_disp是xml文件中的单选button。

所以,当回收站视图加载项目时,在bindView Holder中检查sparseBooleanArray是否包含值“true”,相应的位置。

如果返回的值为true,那么我们将单选buttonselect设置为true。否则,我们将select设置为false。 在onclickHolder中,我已经清除了sparseArray,并将该值设置为对应于该位置的true。 当我打电话通知datasetChange它再次调用onBindViewHolder和条件再次检查。 这使我们select只select特定的广播。

以下可能会有助于单选的RecyclerView

三个步骤来做到这一点,1)声明一个全局整数variables,

 private int mSelectedItem = -1; 

2)在onBindViewHolder

  mRadio.setChecked(position == mSelectedItem); 

3)在onClickListener

 mSelectedItem = getAdapterPosition(); notifyItemRangeChanged(0, mSingleCheckList.size()); mAdapter.onItemHolderClick(SingleCheckViewHolder.this);