RecyclerView中的单一选择

Gun*_*lan 60 android android-recyclerview

我知道在recyclerview类中没有默认的选择方法,但是我试过以下方式,

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);
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

尝试这段代码时,我得到了预期的输出,但完全没有.

我将用图像解释这个.

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

在此输入图像描述

然后我试图选择第二个然后是第三个,然后是第四个,最后是第五个,

在此输入图像描述

这里仅应选择第5个,但所有5个都被选中.

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

在此输入图像描述

我得到了我的预期,

我怎样才能克服这个问题?还有一段时间如果我快速滚动列表,其他一些项目就会被选中.如何克服这个问题呢?

更新

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)
Run Code Online (Sandbox Code Playgroud)

Xci*_*egn 97

该问题的解决方案:

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);
       }
   });
}
}
Run Code Online (Sandbox Code Playgroud)

希望这有帮助!

  • 我认为值得一提的是(至少在我的情况下)如果你想点击整个视图进行操作,最好将'android:focusable ="false"和`android:clickable ="false"`添加到单选按钮本身就像点击单选按钮一样. (7认同)
  • 没有兄弟,你不能在`onBindViewHolder`里面调用`notifyDataSetChanged();`. (3认同)
  • @Xcihnegn我想在其活动中获得所选单选按钮的值.我怎么能实现这个任何建议? (3认同)

sub*_*ati 75

它太晚了仍然发布它可能有助于其他人使用下面的代码作为参考检查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();

                }
            });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这绝对是正确的答案.这也适用于无线电组和一系列单选按钮. (2认同)

Wil*_*iam 13

看起来有两件事在这里发挥作用:

(1)视图被重用,所以老听众仍然存在.

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

我将分别解决每个问题.

(1)查看重用

基本上,在onBindViewHolder你给出一个已经初始化的ViewHolder,已经包含一个视图.这ViewHolder可能会或可能不会被以前绑定的一些数据!

在这里注意这段代码:

holder.checkBox.setChecked(fonts.get(position).isSelected());
Run Code Online (Sandbox Code Playgroud)

如果持有者先前已被绑定,则复选框已经有一个监听器,用于检查状态是否发生变化!这个听众正在被触发,这正是导致你的IllegalStateException.

一个简单的解决方案是在调用之前删除监听器setChecked.优雅的解决方案需要更多的视图知识 - 我鼓励您寻找一种更好的方法来处理它.

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

代码中的侦听器正在更改数据的状态,而不通知适配器任何后续更改.我不知道您的观点是如何运作的,所以这可能是也可能不是问题.通常,当数据状态发生变化时,您需要让适配器知道它.

RecyclerView.Adapter有很多选项可供选择,包括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);
}
Run Code Online (Sandbox Code Playgroud)


Akl*_*ngh 12

这是它的样子

单选回收者查看最佳方式

适配器内部

private int selectedPosition = -1;
Run Code Online (Sandbox Code Playgroud)

和 onBindViewHolder

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

    if (selectedPosition == position) {
        holder.itemView.setSelected(true); //using selector drawable
        holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.white));
    } else {
        holder.itemView.setSelected(false);
        holder.tvText.setTextColor(ContextCompat.getColor(holder.tvText.getContext(),R.color.black));
    }

    holder.itemView.setOnClickListener(v -> {
        if (selectedPosition >= 0)
            notifyItemChanged(selectedPosition);
        selectedPosition = holder.getAdapterPosition();
        notifyItemChanged(selectedPosition);
    });
}
Run Code Online (Sandbox Code Playgroud)

就是这样!如您所见,我只是在通知(更新)先前选择的项目和新选择的项目

我的 Drawable 将其设置为 recyclerview 子视图的背景

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_selected="true">
    <shape android:shape="rectangle">
        <solid android:color="@color/blue" />
    </shape>
</item>
Run Code Online (Sandbox Code Playgroud)


小智 9

只需使用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();
                }
            });
        }
Run Code Online (Sandbox Code Playgroud)


Ism*_*bal 8

这可能对那些想要单个无线电按钮工作的人有用 - > 单选按钮RecycleView - Gist

如果不支持lambda表达式,请改用:

View.OnClickListener listener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        notifyItemChanged(mSelectedItem); // to update last selected item.
        mSelectedItem = getAdapterPosition();
    }
};
Run Code Online (Sandbox Code Playgroud)

干杯


Hen*_*usa 7

您需要清除OnCheckedChangeListener之前的设置setChecked():

@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();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

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