【Android】 一种优雅的方式实现RecyclerView列表单选

前言

我们经常会遇到列表中需要单选这个需求,之前的做法是:在我们的数据源ItemBean里增加一个boolean 类型isSelected字段,并在Adapter里根据这个字段来判定选中状态。每次选中一个新的item时,改变数据源里的isSelected字段,并调用notifyDataSetChanged()刷新整个列表。这样做呢实现起来比较简单,但是如果列表条目过多的话会稍微卡顿下。本片文章会采用另外一种方式实现,相对来说比较优雅吧

实现方案

首先我们看下效果图

在这里插入图片描述

这里我们主要使用RecyclerViewfindViewHolderForLayoutPosition()方法,这个方法会获取某个postionViewHolder,但是请注意,它的返回值可能为空,用的时候需要特别小心,空就表示屏幕看不见了

我们首先定义数据源ItemBean

**
 * @author HuangFusheng
 * @date 2019-11-11
 * description 列表的Item数据
 */
public class ItemBean {
    private String name;
    private boolean isSelected;

    public ItemBean(String name) {
        this.name = name;
    }

    public ItemBean(String name, boolean isSelected) {
        this.name = name;
        setSelected(isSelected);
    }

    public boolean isSelected() {
        return isSelected;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

里面同样有个isSelected,但是我们不会像前言里面那样去使用这个字段,我们只是用它来表示是否选中
接下来我们就要编写我们最主要的Adapter了,基本的定义实现我们这里就不详细讲解了,我们只说比较重要的地方,其他的看下代码就行

第一步:在我们的Adapter中定义一个变量mSelectedPos,它表示我们当前选中的位置,当我们点击其他Item的时候,我们就把这个mSelectedPos改为那条Itemposition,说到这里估计明白我们要实现的原理了,我们的核心思想就是定向刷新两个变动的Item,一个是原来选中的,另一个就是我们点击的,这样的话只改动了变化的部分,不会重走onBindViewHolder()方法,属于手动部分绑定。

第二步:我们在我们Adapter的构造方法中通过isSelected字段获取初始化的mSelectedPos

 public ItemAdapter(List<ItemBean> datas, Context context, RecyclerView rv) {
        mDatas = datas;
        mContext = context;
        mInflater = LayoutInflater.from(mContext);
        mRv = rv;

        //找到默认选中的position
        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).isSelected()) {
                mSelectedPos = i;
            }
        }
    }

第三步:在我们点击Item的时候,通过findViewHolderForLayoutPosition()这个方法拿到mSelectedPos对应的ViewHolder,把这条Item里面的控件置位未选中,然后把点击的position的值赋给mSelectedPos,然后再把position位置的控件置位选中状态,操作的同时我们也要改变数据源的状态

 holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                ViewHolder viewHolder = (ViewHolder) mRv.findViewHolderForLayoutPosition(mSelectedPos);
                //表示还在屏幕里
                if (viewHolder != null) {
                    viewHolder.ivSelect.setSelected(false);
                } else {
                    notifyItemChanged(mSelectedPos);
                }
                //改变数据状态
                mDatas.get(mSelectedPos).setSelected(false);
                //设置新Item的勾选状态
                mSelectedPos = position;
                mDatas.get(mSelectedPos).setSelected(true);
                holder.ivSelect.setSelected(true);
            }
        });

Ok,到此我们就大功告成了,贴下完整的Adapter代码

/**
 * @author HuangFusheng
 * @date 2019-11-11
 * description 适配器
 */
public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> {
    private static final String TAG = "ItemAdapter";

    private List<ItemBean> mDatas;
    private Context mContext;
    private LayoutInflater mInflater;
    /**
     * 当前选中的位置
     */
    private int mSelectedPos = -1;

    private RecyclerView mRv;

    public ItemAdapter(List<ItemBean> datas, Context context, RecyclerView rv) {
        mDatas = datas;
        mContext = context;
        mInflater = LayoutInflater.from(mContext);
        mRv = rv;

        //找到默认选中的position
        for (int i = 0; i < mDatas.size(); i++) {
            if (mDatas.get(i).isSelected()) {
                mSelectedPos = i;
            }
        }
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = mInflater.inflate(R.layout.item_layout, parent, false);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        Log.e(TAG, "执行onBindViewHolder: .....");
        holder.ivSelect.setSelected(mDatas.get(position).isSelected());
        holder.tvName.setText(mDatas.get(position).getName());

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                ViewHolder viewHolder = (ViewHolder) mRv.findViewHolderForLayoutPosition(mSelectedPos);
                //表示还在屏幕里
                if (viewHolder != null) {
                    viewHolder.ivSelect.setSelected(false);
                } else {
                    notifyItemChanged(mSelectedPos);
                }
                //改变数据状态
                mDatas.get(mSelectedPos).setSelected(false);
                //设置新Item的勾选状态
                mSelectedPos = position;
                mDatas.get(mSelectedPos).setSelected(true);
                holder.ivSelect.setSelected(true);
            }
        });
    }

    @Override
    public int getItemCount() {
        return null != mDatas ? mDatas.size() : 0;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        private ImageView ivSelect;
        private TextView tvName;

        public ViewHolder(View itemView) {
            super(itemView);
            ivSelect = (ImageView) itemView.findViewById(R.id.iv_item_select);
            tvName = (TextView) itemView.findViewById(R.id.tv_item_name);
        }
    }
}

这里我们操作下,切换几个Item,看看是否会执行多次onBindViewHolder()()方法

在这里插入图片描述
我们看到我们这种方式不会多次执行onBindViewHolder(),小伙伴们可以尝试下

源码地址

发布了87 篇原创文章 · 获赞 319 · 访问量 149万+

猜你喜欢

转载自blog.csdn.net/Greathfs/article/details/103017392