Android—RecyclerView相关内容
Recycleview四級緩存
- mAttachedScrap(屏幕內),用于屏幕內itemview快速重用,不需要重新createView和bindView
- mCacheViews(屏幕外),保存最近移出屏幕的ViewHolder,包含數據和position信息,復用時必須是相同位置的ViewHolder才能復用,應用場景在那些需要來回滑動的列表中,當往回滑動時,能直接復用ViewHolder數據,不需要重新bindView。
- mViewCacheExtension(自定義緩存),不直接使用,需要用戶自定義實現,默認不實現。
- mRecyclerPool(緩存池),當cacheView滿了后或者adapter被更換,將cacheView中移出的ViewHolder放到Pool中,放之前會把ViewHolder數據清除掉,所以復用時需要重新bindView。
如果多個RecyclerView之間用setRecycledViewPool(RecycledViewPool)設置同一個RecycledViewPool,他們就可以共享Item。其實RecycledViewPool的內部維護了一個Map,里面以不同的viewType為Key存儲了各自對應的ViewHolder集合。可以通過提供的方法來修改內部緩存的Viewholder。
四級緩存按照順序需要依次讀取。所以完整緩存流程是:
保存緩存流程:
- 插入或是刪除itemView時,先把屏幕內的ViewHolder保存至AttachedScrap中
- 滑動屏幕的時候,先消失的itemview會保存到CacheView,CacheView大小默認是2,超過數量的話按照先入先出原則,移出頭部的itemview保存到RecyclerPool緩存池(如果有自定義緩存就會保存到自定義緩存里),RecyclerPool緩存池會按照itemview的itemtype進行保存,每個itemType緩存個數為5個,超過就會被回收。
獲取緩存流程:
- AttachedScrap中獲取,通過pos匹配holder——>獲取失敗,從CacheView中獲取,也是通過pos獲取holder緩存——>獲取失敗,從自定義緩存中獲取緩存——>獲取失敗,從mRecyclerPool中獲取——>獲取失敗,重新創建viewholder——createViewHolder并bindview。
預取功能(Prefetch)
這個功能是rv在版本25之后自帶的,LinearLayoutManager的setInitialItemPrefetchCount()我們可以手動控制該功能。
功能:預取接下來可能要顯示的item,在下一幀到來之前提前處理完數據,然后將得到的itemholder緩存起來,等到真正要使用的時候直接從緩存取出來即可。
刷新方法:
- notifyDataSetChanged(),刷新全部可見的item。
- notifyItemChanged(int)、notifyItemChanged(int position, @Nullable Object payload),刷新指定item。
- notifyItemRangeChanged(int,int),從指定位置開始刷新指定個item。
- notifyItemInserted(int)、notifyItemMoved(int)、notifyItemRemoved(int)。插入、移動一個并自動刷新。
- notifyItemChanged(int, Object),局部刷新。
notifyItemChanged(int position, @Nullable Object payload)
@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {Item item = mData.get(position);if (payloads.isEmpty()) {super.onBindViewHolder(holder, position, payloads);} else {holder.itemView.setSelected(item.isSelected());}}可以重寫RecyclerView的另一個onBindViewHolder方法,在里面添加payloads的判空,可以作為標記(如上),也可以用來傳值。
優化:
- 設置RecyclerView.addOnScrollListener();來在滑動過程中停止加載的操作。
- 使用diffutil進行局部刷新,少用全局刷新
- bindViewHolder方法是在UI線程進行的,此方法不能耗時操作,不然將會影響滑動流暢性。
- item高度是固定的話,可以使用RecyclerView.setHasFixedSize(true);來避免requestLayout浪費資源。3
- 對 ItemView 設置監聽器,不要對每個 Item 都new 一個 Listener,應該大家公用一個 Listener,根據 ID 來進行不同的操作,優化了對象的頻繁創建帶來的資源消耗。
Recylerview刷新圖片閃爍:
@Override public long getItemId(int position) {return position; }mAdapter.setHasStableIds(true);setHasStableIds:數據集中的每一項是否設置有一個唯一標識。
getItemId:給位置的項一個ID。
兩個方法配合使用,相當于給ImageView加了一個tag,tag不變的話,不用重新加載圖片的 ImageView?不重新加載。
也可以使用局部刷新來解決這個問題。
滑動沖突:
1、NestedScrollView嵌套RecyclerView
坑:NestScrollView嵌套RecyclerView時,事件還是會被NestScrollView消費,RecyclerView的滾動監聽無效,而且會加載所有item,其內部的緩存起不到作用。
2、內部攔截:
在繼承RecyclerView重寫方法:
requestDisallowInterceptTouchEvent方法阻止攔截。
ScrollView 套 ViewGroup?套 RV 。所以是getParent()調用該方法。
@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.e("MyListView","dispatchTouchEvent·····"+ev.getAction());switch (ev.getAction()){case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true);break;case MotionEvent.ACTION_MOVE:boolean a = !canScrollVertically(-1);boolean b = ev.getY()-firstY>0;boolean c = !canScrollVertically(1);boolean d = ev.getY()-firstY<0;if ( (b&&a) || (c&&d) )getParent().requestDisallowInterceptTouchEvent(false);break;}firstY = ev.getY();boolean result =super.dispatchTouchEvent(ev);return result;}3、外部攔截:
重寫ScrollView的onInterceptTouchEvent方法進行判斷什么時候該返回true。
RecycleView拖拽跟側滑功能:ItemTouchHelper類
https://blog.csdn.net/weixin_39706415/article/details/87929441
總結
以上是生活随笔為你收集整理的Android—RecyclerView相关内容的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不花钱就补足营养的8妙招
- 下一篇: Android—APT实践