[Android] An elegant way to achieve RecyclerView list radio

Foreword

We often encounter the need for single selection in the list. The previous approach was ItemBeanto add a booleantype isSelectedfield to our data source and Adapterdetermine the selected state based on this field. Each time you select a new one item, change the i sSelectedfield in the data source and call to notifyDataSetChanged()refresh the entire list. This is relatively simple to implement, but if there are too many entries in the list, it will get stuck slightly. This article will be implemented in another way, relatively elegant

Implementation plan

First we look at the renderings

Insert picture description here

Here we use RecyclerViewthe findViewHolderForLayoutPosition()method, which will get a postionof ViewHolder, but please note that the return value may be empty, when used require special care, it means empty screen sight

We first define the data sourceItemBean

**
 * @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;
    }
}

There is also one in it isSelected, but we will not use this field as in the preface.We just use it to indicate whether it is selected.Next
we will write our most important.The Adapterbasic definition implementation will not be explained in detail here. We only talk about the more important things, the other is to look at the code.

The first step: Adapterdefine a variable in our mSelectedPos, which represents our currently selected position, when we click on the other Item, we will mSelectedPoschange this to that Itemone position, and it is estimated that we understand the principle we want to achieve here, we The core idea is to refresh two changes directionally Item, one is originally selected, and the other is what we clicked. In this case, only the changed part is changed, and the onBindViewHolder()method will not be repeated .

The second step: we get initialized Adapterby the isSelectedfield in our constructormSelectedPos

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

Step 3: When we click Item, findViewHolderForLayoutPosition()get the mSelectedPoscorresponding through this method ViewHolder, Itemset the control in this item to unchecked, then assign positionthe value of the click mSelectedPos, and then positionset the position control to the selected state, operation At the same time we also need to change the state of the data source

 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, here we are done, paste the complete Adaptercode

/**
 * @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);
        }
    }
}

Here we operate, switch a few Itemto see if the onBindViewHolder()()method will be executed multiple times

Insert picture description here
We see that we will not execute this method many times onBindViewHolder(), and friends can try

Source address

Published 87 original articles · Like 319 · Visit 1.49 million +

Guess you like

Origin blog.csdn.net/Greathfs/article/details/103017392