Copyright notice: This article is the blogger’s original article, shall not be reproduced without the permission of the blogger.

Reprint please indicate the source: blog.csdn.net/zxt0601/art… [1] This article is from: [Zhang Xutong’s blog] [2]

The source of this article is relatively simple, but there is still something to be found in its elegance.

Source of demand: An e-commerce coupon selection interface similar to Ele. me: in fact, it is a common list with radio selection function. The effect is shown in the figure:

General method: Add a Boolean isSelected field to the Javabean and set the CheckBox status according to the value of this field in the Adapter. Each time a new coupon isSelected, change the isSelected field in the data source and notifyDataSetChanged() refreshes the entire list. This is simple to implement and requires very little code. The only downside is that performance is wasted, not the most elegant. So as a programmer with a more leisurely pursuit today, I am determined to share a wave of elegant solutions.

This paper will enumerate and analyze several schemes of list radio selection in ListView and RecyclerView, and recommend the scheme of directional refresh partial binding, because it is more efficient and elegant.


RecyclerView program overview:

RecyclerView is my favorite, so I’ll start with that.

1 Routine scheme:

Conventional scheme please read at the speed of light, direct code: Bean structure:

public class TestBean extends SelectedBean { private String name; public TestBean(String name,boolean isSelected) { .name = name; setSelected(isSelected); }}Copy the code

There are so many radio requirements in my project that I didn’t want to write the isSelected field, so I created a parent class for subclasses to inherit.

public class SelectedBean { private boolean isSelected; public boolean isSelected() { return isSelected; } public setSelected(boolean selected) { isSelected = selected; }}Copy the code

Acitivity and Adapter the other methods are the most common. The Adapter onBindViewHolder() reads as follows:

Log.d("TAG", "onBindViewHolder() called with: holder = [" + holder + "], position = [" + position + ); holder.ivSelect.setSelected(mDatas.(position).isSelected()); / / "CheckBox" holder. TvCoupon. SetText (mDatas. (position). The getName ()); / / TextView holder. IvSelect. SetOnClickListener (View. An OnClickListener () {@ Override public onClick (View View) {/ / radio, The first method, quite simple, is common to Lv Rv because they all have notifyDataSetChanged(). // On each click, set all selected to false and the currently clicked item to true. Refresh the entire view (TestBean data: mDatas) { data.setSelected(false); } mDatas.(position).setSelected(); notifyDataSetChanged(); } });Copy the code

ViewHolder:

public static class CouponVH extends RecyclerView.ViewHolder { private ImageView ivSelect; private TextView tvCoupon; public CouponVH(View itemView) { super(itemView); ivSelect = (ImageView) itemView.findViewById(R.id.ivSelect); tvCoupon = (TextView) itemView.findViewById(R.id.tvCoupon); }}Copy the code

Advantages of the scheme:

Disadvantages of the scheme:

In fact, there are only two items that need to be modified: An Item currently in the selected state -> normal state and the current finger click on the Item-> selected state but using the normal scheme, the entire screen of visible items will be refreshed, retracing their getView()/onBindViewHolder() methods. In fact, a screen can usually see 10+ items at most, so it doesn’t hurt to iterate. But we still want to have the pursuit of elegant heart, so we continue to read.

2 Use Rv notifyItemChanged() to flush directionally:

This project needs to add a new field in the Adapter:

private mSelectedPos = -; // Implement the single method two, the variable holds the currently selected positionCopy the code

⑵ when setting the data set (constructor, setData() method, etc. :), initialize the value of mSelectedPos.

Pos (I =; pos (I =; i < mDatas.size(); i++) { (mDatas.(i).isSelected()) { mSelectedPos = i; }}Copy the code

OnClick:

NotifyItemChanged () notifyItemChanged() notifyItemChanged() notifyItemChanged() notifyItemChanged() MDatas.(mSelectedPos).setSelected(false); notifyItemChanged(mSelectedPos); MSelectedPos = position; mDatas.(mSelectedPos).setSelected(); notifyItemChanged(mSelectedPos); }Copy the code

This scenario is accompanied by a “white flash” animation because notifyItemChanged() is called.

Advantages of the scheme:

Instead of revisiting the getView()/onBindViewHolder() method for a screen of visible items, it will still revisiting the getView()/onBindViewHolder() method for two items that need to be modified.

Disadvantages of the scheme:

We need to check the Partial bind (Partial bind, Partial bind, Partial bind, Partial bind, Partial bind). DiffUtil[3]) All we need is partial binding.

A suspect: Instead of just revisiting the onBindViewHolder() method of the old and new items, we also revisiting the onBindViewHolder() method of the two items that are not on the screen at all. For example, in this case, there are items 0-3 in the screen. By default, item1 is selected. After I select item0, log shows postion 4,5,0,1 executing onBindViewHolder(). But when you switch the other items again, it works as expected: just go to the getView()/onBindViewHolder() method for the two items that need to be changed. The reason is unknown, if any friends know, please inform us, thank you.

3 Rv Partial binding (recommended) :

Using RecyclerView findViewHolderForLayoutPosition () method, to obtain a postion ViewHolder, according to the source of this method in the comments, it may return null. So we need to pay attention to null, (null means not visible on the screen). Unlike method 2, which only has the code in onClick, the core uses the mSelectedPos field to do things.

// Realize the radio method three: RecyclerView another kind of directional refresh method: Don't have a white light flashing Don't repeat onBindVIewHolder CouponVH CouponVH = (CouponVH) mRv. FindViewHolderForLayoutPosition (mSelectedPos); (couponVH ! =) {/ / also in screen couponVH. IvSelect. SetSelected (false); } mDatas.(mSelectedPos).setSelected(false); MSelectedPos = position; mSelectedPos = position; mDatas.(mSelectedPos).setSelected(); holder.ivSelect.setSelected();Copy the code

Advantages of the scheme:

Flush the two items directionally, modifying only the necessary parts without retracing onBindViewHolder(). This is a manual partial binding. The amount of code is also moderate, not much.

Disadvantages of the scheme:

No flash of white light?? (If that’s a disadvantage)

Rv partial binding with payloads (not recommended):

This scheme is based on scheme 2, using payloads and notifyItemChanged(int position, Object payload) to do things. [4] onClick – loads loads from 7.0 -> DiffUtil[4]

MSelectedPos! MDatas.(mSelectedPos).setSelected(false); PayloadOld = Bundle(); payloadOld.putBoolean("KEY_BOOLEAN", false); notifyItemChanged(mSelectedPos, payloadOld); MSelectedPos = position; mDatas.(mSelectedPos).setSelected(); Bundle payloadNew = Bundle(); payloadNew.putBoolean("KEY_BOOLEAN", ); notifyItemChanged(mSelectedPos, payloadNew); }Copy the code

We need to override the three-argument onBindViewHolder() method:

@Override public onBindViewHolder(CouponVH holder, position, List payloads) { (payloads.isEmpty()) { onBindViewHolder(holder, position); } { Bundle payload = (Bundle) payloads.get(); (payload.containsKey("KEY_BOOLEAN")) { boolean aBoolean = payload.getBoolean("KEY_BOOLEAN"); holder.ivSelect.setSelected(aBoolean); }}}Copy the code

Advantages of the scheme:

Disadvantages of the scheme:

The amount of code, the implementation of the effect and method three, only do pioneering thinking, so choose method three.


Three ListView scheme overview:

Now, honestly, if you’re still using ListView, if it’s not a legacy, you need to think about it. But there are still people out there, just like there are still people out there using Android4.x, and we have to think about how they feel about it.

1 Routine scheme:

Conventional program and Rv hair, not on the code, refer to two.

Advantages of the scheme:

Disadvantages of the scheme:

2. Find the elegant way in ListView:

In this scheme, the idea is the same.3. Just a ListView did not provide findViewHolderForLayoutPosition () this way, through the ViewHolder postion for caching. This is nonsense, because it’s not designed to force us to use ViewHolder, so we can’t get a ViewHolder, so we can go the other way and get the child View directly from the ViewGroup getChildAt(), If you get the child View, you can get the ViewHolder, you can do something. The code:

// If the currently selected View is visible on the current screen, and is not its own View, you need to refresh the previous View state (position! = mSelectedPos) { firstPos = mLv.getFirstVisiblePosition() - mLv.getHeaderViewsCount(); / / here. Considering the situation of the HeaderView lastPos = mLv getLastVisiblePosition () - mLv. GetHeaderViewsCount (); (mSelectedPos >= firstPos && mSelectedPos <= lastPos)="" {="" View="" lastSelectedView="mLv.getChildAt(mSelectedPos" -="" firstPos); Getselectedview.gettag (); ="" lastVh.ivSelect.setSelected(false); (mSelectedPos).setSelected(false); (mSelectedPos).setSelected(false); = "" change now, click the View selected =" "couponVH ivSelect. SetSelected (); ="" mDatas.(position).setSelected(); ="" mSelectedPos="position;" }<="" code=""/>Copy the code

Advantages of the scheme:

It also directs the refresh + section to bind both items without revisiting getView().

Disadvantages of the scheme:

It’s a little bit too much code.


I have discussed with Guo shen before writing this article. Indeed, as he said, the number of getView and onBindViewHolder when refreshing is usually in bits (the number of ItemViews visible on the screen), so it doesn’t matter if you implement it in the most conventional way. According to Guo God said, he wrote before, the reference is gmail implementation plan, before seen Gmail multi-choice function is to use the conventional plan to do. So, if the project time is urgent, it is ok to adopt the conventional plan. (I often use the conventional solution when I’m in a rush.)

The scheme in this paper can also be used for list likes, drop-down filters and other scenarios. For example, when a list is liked, repeating onBindViewHolder() will require the image grid control to reset the data set. Some of the grids are not well written, and the views in those grids will have to be removed and reconstructed. When used, it is excellent.

In fact, you can also use RecyclerView+DiffUtil to achieve directional refresh partial binding, see my previous blog, but there is a sense of killing a pair of birds. After all, DiffUtil calculation also takes time and traverses the entire new and old data sets during calculation, so this scheme is not provided in this article so as not to mislead.

This code is no longer a separate project, available at my Github Demos: github.com/mcxtzhang/D… [5]