About RecyclerView you know don’t know all in this RecyclerView problem summary RecyclerView summary -YCBlogs
A flexible view for providing A limited window into A large data set A flexible view for providing A limited window into A large data set
- Linear, Grid, Staggered Grid layouts are supported by default
- Force ViewHolder to reduce findViewById
- Decoupled architecture
public class UnPackAdapter extends RecyclerView.Adapter<UnPackViewHolder> {
@Override
public UnPackViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(mLayoutId, parent, false);
UnPackViewHolder viewHolder = new UnPackViewHolder(itemView, mCount++);
returnviewHolder; } @Override public void onBindViewHolder(UnPackViewHolder holder, int position) { holder.tvCenter.setText(mDatas.get(position)); }} // Encapsulate the same as ListView... class UnPackViewHolder extends RecyclerView.ViewHolder{ public TextView tvCenter; public UnPackViewHolder(View itemView, int count) { tvCenter = itemView.findViewById(R.id.tv_common_recycler_center); }}Copy the code
First, the ViewHolder
Unlike a ListView, which is enforced the same way as a ListView, reuse is independent of the ViewHolder. The reuse mechanism is the algorithm mechanism inside the control
- The ViewHoler corresponds to the Item View one to one
- Reduce the findViewById
Two, use scenarios
2.1 Item Multi-layout
- Return different types in getItemViewType
- Do the corresponding processing in onCreateViewHolder and onBindViewHolder
@Override
public int getItemViewType(int position) {
if(headerHolder ! = null && position == 0)return ITEM_TYPE_HEADER;
return ITEM_TYPE_NOMAL;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == ITEM_TYPE_HEADER) return headerHolder;
View view = LayoutInflater.from(mContext).inflate(layId, null);
MyCommonHolder holder = new MyCommonHolder(view);
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(getItemViewType(position) == ITEM_TYPE_NOMAL){... }if(getItemViewType(position) == ITEM_TYPE_HEADER && headerHolder ! = null){... }}Copy the code
2.2 Nesting, sliding conflict, highly incomplete, stuck
Solve the RecyclerView nested RecyclerView displacement problem
2.2.1 Father Recycler is pushed for a distance
Because children Recycler is in focus, we can bring our father Recycler up. Solution: Give parents Recycler a focus
2.2.2 The father vertical son transverse, slide the father Recycler and then slide the father Recycler, return to the original position after the child Recycler state to recover
Because Recycler can be recycled when it is displayed again, the old state can be lost: listen for parent Recycler to slide, save the displacement of each child Item, and bind the time to restore
2.2.3 Conflicts with ScrollView nested sliding and incomplete display
RecyclerView sliding conflict
- The LayoutMana override canScrollHorizontally and the canScrollHorizontally methods prohibit sliding
- The overwrite ScrollView intercepts sliding
- Incomplete display: The parent layout is replaced by NestedScrollView
- recyclerView.setHasFixedSize(true);
- recyclerView.setNestedScrollingEnabled(false)
2.2.4 Nesting stuck
The following sections
2.3 onBinderViewHolder ()
Is it called every time you reuse an Item View?
No, some of them are fetched directly from caches and different caches call different methods
- Scrap and Cache are not called
- RecyclerViewPool, bind but not create
I want to know exactly how many times each Item is displayed
For example, if you’re doing presentation statistics, it’s not quite right to say onBinderViewHolder so onViewAttachedToWindow is going to be called every time you display an ItemView
Three, optimize
The 3.1 update
notifyItem…
Try to use local updates rather than notifyDataSetChanged() all refreshed
DiffUtils: Local/incremental update
DiffUtil calculates the minimum number of updates needed to convert one data set to another. DiffUtil also identifies the movement of a data set. Eugene’s algorithm optimizes the control to have a spatial complexity of O(N) and a time complexity of O(N+D^2) for minimum addition and subtraction operations between two data sets. Adding the recognition of data-intensive movements increases the complexity to O(N^2). So: If there is no movement in the data set, you can turn off movement recognition to improve performance. When the data set is large you should calculate the data set update in the background thread.
private class DiffCallback extends DiffUtil.Callback { private List<Item> mOldDatas; private List<Item> mNewDatas; Public DiffCallback(List<Item> oldDatas,List<Item> newDatas) {this.moldDatas = oldDatas; this.mNewDatas = newDatas; } @Override public intgetOldListSize() {
returnmOldDatas ! = null ? mOldDatas.size() : 0; } @Override public intgetNewListSize() {
returnmNewDatas ! = null ? mNewDatas.size() : 0; } /** * is called by DiffUtil to determine whether two objects are the same Item. * For example, if your Item has a unique ID field, this method determines whether the ids are equal. */ @override public Boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {returnmOldDatas.get(oldItemPosition).id == mNewDatas.get(newItemPosition).id; } /* * Called by DiffUtil to check whether two items contain the same data * This method is only returned in areItemsTheSame()trueIs invoked when. */ @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { String oldName = mOldDatas.get(oldItemPosition).getName(); String newName = mNewDatas.get(newItemPosition).getName();if(! oldName.equals(newName)) {return false;
}
return true; } /** * areItemsTheSame() returnstrueWhile areContentsTheSame () returnsfalseIn other words, the two objects represent the same data, but the content is updated. * note: Implement this method, implement local update of Item in onBindViewHolder * Nullable @override public Object getChangePayload(int oldItemPosition, int newItemPosition) { String oldItem = mOldDatas.get(oldItemPosition).getName(); String newItem = mNewDatas.get(newItemPosition).getName(); Bundle bundle = new Bundle();if(! oldItem.equals(newItem)) { bundle.putString("name",newItem);
}
returnbundle; }} //Adapter //payloads are returned by the DiffUtil.Callback method onBindViewHolder(DiffItemHolder holder, int position, List<Object> payloads) {if (payloads.isEmpty()) {
onBindViewHolder(holder,position);
} else {
Bundle bundle = (Bundle) payloads.get(0);
for(String key : bundle.keySet()) {
switch (key) {
case "name":
holder.mInfo.setText((CharSequence) bundle.get(key));
break; }}}} // If the calculation is too large, put it into the child thread to calculate DiffUtil.DiffResult DiffResult = DiffUtil. CalculateDiff (new DiffCallback(oldData, newData)); / / main thread updates diffResult. DispatchUpdatesTo (mUnPackAdapter);Copy the code
3.2 listening
- When onCreateViewHolder is set to listen on View — ViewHolder — Listener, one to one
- Reuse the same listener
3.3 setHasFixedSize (true)
Change the size of the RecyclerView to True when it is known that changes in the Adapter Item will not affect the width and height of the RecyclerView
RequestLayout () is not required according to mHasFixedSize. So these 4 methods are not redrawn; NotifyDataSetChanged (), on the other hand, redraws and resizes even when it is set
OnItemRangeChanged (), onItemRangeInserted(), onItemRangeRemoved(), onItemRangeMoved()
3.4 Prefetch
RecyclerView Prefetch function research
- Because of RenderThread, Prefetch is done
- Only LinearLayoutManager setInitialItemPrefetchCount () method
- As long as it is valid within a nesting
3.5 Multiple recyclerViews share RecycledViewPool
RecycledViewPool RecycledViewPool usage and heap memory analysis
When Item types in multiple RecyclerViews are the same, ViewHolder in the pool can be reused
- ViewPage + Fragment + ItemView of the same type
- Reduce the number of ViewHolder instances created
- Reduce processes like inflate, findviewById, and so on
RecycledViewPool pool = new RecycledViewPool();
recyclerView1.setRecycledViewPool(pool);
recyclerView2.setRecycledViewPool(pool);
recyclerView3.setRecycledViewPool(pool);
Copy the code
Fourth, performance optimization
Three aspects: time consuming, cache prefetching, and sliding conflicts
1.1 Reduce the number of creat/bind calls
Inflate and findView consume resources
- To prevent full flushing, use notifyItem…
- DiffUtils: Updates the local binding
- Sharing RecycledViewPool
- Reduce the View Type, use the same layout with little difference, and handle display differences in BIND
1.2 Shorten execution time or child thread calculation
- Prevent View layout nesting
- Large amounts of computation are put into child threads
2. Cache and preload
- Prefetch: setInitialItemPrefetchCount
3. Handle sliding conflicts
- External and internal interception
- NestedScrollView replaces and sets Recycler not to be recyclable
Five, cache – reuse
[Not to do in-depth research]
Scrap, Cache, extension (custom Cache), viewPool is different from ListView
An intuitive difference is the timing of the onBindViewHolder call, which is not called every time an Item reappears on the screen:
- By default, the cache cache has 2 holders and RecycledViewPool has 5 holders
- Scrap/Cache View Finds Cache by position, not dirty data, neither onCreate nor bind.
- ViewChacheExtension user defined policies
- RecycledViewPool is used to find cache by ViewType and is dirty, not onCreate but bind.
- If you want to show each Item know: adapter. OnViewAttachedToWindow
ListView
1. ViewHolder
If ViewHolder is not implemented, item View will be reused, and frequent findView will affect performance
- Not mandatory, just custom encapsulation
- A ViewHoler corresponds one-to-one to convertView by tag
- Reduce the findViewById
Public class ViewHolder {private ViewHolder(Context Context,ViewGroup parent, int layoutId,int position){ mCovertView = LayoutInflater.from(context).inflate(layoutId, parent,false);
mCovertView.setTag(this);
}
public static ViewHolder get(Context context,View convertView,ViewGroup parent, int layoutId, int position){
if (convertView == null) {
return new ViewHolder(context, parent, layoutId, position);
}else{
ViewHolder holder = (ViewHolder) convertView.getTag();
return holder;
}
}
public <T extends View> T getView(int viewId){
View view = mViews.get(viewId);
if (view == null) {
view = mCovertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public ViewHolder setText(int viewId,String text){
getView(viewId).setText(text);
returnthis; }}Copy the code
other
GetView call timing
- Active View is not called (the screen refreshes every 16ms and items that have been displayed are not called)
- Scrap the View call
- Do display statistics: getView