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);