RecyclerView根據(jù)不同的狀態(tài)可以分為:屏幕內(nèi)緩存杨拐、屏幕外緩存、自定義緩存褥符、緩存池鹃操。RecyclerView是通過內(nèi)部類Recycler來管理緩存况既。
一級(jí)緩存:屏幕內(nèi)緩存(mAttachedScrap)
屏幕內(nèi)緩存指在屏幕中顯示的ViewHolder,這些ViewHolder會(huì)緩存在mAttachedScrap组民、mChangedScrap中 :
mChangedScrap 表示數(shù)據(jù)已經(jīng)改變的ViewHolder列表棒仍,需要重新綁定數(shù)據(jù)(調(diào)用onBindViewHolder)
mAttachedScrap 未與RecyclerView分離的ViewHolder列表
二級(jí)緩存:屏幕外緩存(mCachedViews)
用來緩存移除屏幕之外的 ViewHolder,默認(rèn)情況下緩存容量是 2臭胜,可以通過 setViewCacheSize 方法來改變緩存的容量大小莫其。如果 mCachedViews 的容量已滿癞尚,則會(huì)優(yōu)先移除舊 ViewHolder,把舊ViewHolder移入到緩存池RecycledViewPool 中乱陡。
三級(jí)緩存:自定義緩存(ViewCacheExtension)
給用戶的自定義擴(kuò)展緩存浇揩,需要用戶自己管理 View 的創(chuàng)建和緩存,可通過Recyclerview.setViewCacheExtension()設(shè)置憨颠。
四級(jí)緩存:緩存池(RecycledViewPool )
ViewHolder 緩存池胳徽,在mCachedViews中如果緩存已滿的時(shí)候(默認(rèn)最大值為2個(gè)),先把mCachedViews中舊的ViewHolder 存入到RecyclerViewPool爽彤。如果RecyclerViewPool緩存池已滿养盗,就不會(huì)再緩存。從緩存池中取出的ViewHolder 适篙,需要重新調(diào)用bindViewHolder綁定數(shù)據(jù)往核。
按照 ViewType 來查找 ViewHolder
每個(gè) ViewType 默認(rèn)最多緩存 5 個(gè)
可以多個(gè) RecyclerView 共享 RecycledViewPool
RecyclerViewPool底層是使用了SparseArray來分開存儲(chǔ)不同ViewType的ViewHolder集
列表刷新優(yōu)化:
對(duì)于類似于從列表進(jìn)詳情關(guān)注某個(gè)用戶后,回到列表客戶端刷新列表關(guān)注狀態(tài)數(shù)據(jù)(可能不止當(dāng)前item需要改變關(guān)注狀態(tài))
本地刷新嚷节,可以通過反射修改緩存數(shù)據(jù)聂儒。
1、直接修改Adapter對(duì)應(yīng)的List數(shù)據(jù)硫痰,但不執(zhí)行notifyDataSetChanged()方法
2衩婚、屏內(nèi)緩存找到對(duì)于ViewHolder直接更新UI
3、瓶外緩存可以通過反射找到對(duì)于ViewHolder再更改UI(關(guān)鍵點(diǎn))
屏外緩存不會(huì)觸發(fā)onBindViewHolder()方法需要手動(dòng)反射修改UI
4效斑、緩存池不需做任何操作非春,當(dāng)滑動(dòng)到對(duì)應(yīng)item會(huì)回調(diào)onBindViewHolder方法,然后更改UI
列表頁收到廣播后直接修改數(shù)據(jù)和更新UI
屏內(nèi)緩存可以直接修改
// 關(guān)注狀態(tài)變化廣播
@Override
public void onFollowChange(String follow_user_id, int follow_status) {
if (!hasInit)
return;
if (videoList != null && videoList.size() > 0) {
for (int i = 0; i < videoList.size(); i++) {
if (follow_user_id.equals(videoList.get(i).getUser_id())) {
videoList.get(i).setFollow_status(follow_status);
RecyclerView.ViewHolder viewHolder = rc_video_list.findViewHolderForAdapterPosition(i);
if (viewHolder != null && viewHolder instanceof VideoAdapter.VideoViewHolder) {
VideoAdapter.VideoViewHolder itemHolder = (VideoAdapter.VideoViewHolder) viewHolder;
itemHolder.bindDataFollow(type, videoList.get(i));//更新UI
}
}
}
RecycleViewCachedViewBindViewUtils.bindView(rc_video_list, new RecycleViewCachedViewBindViewUtils.BindView() {
@Override
public void bindView(RecyclerView.ViewHolder viewHolder,int positon) {
if (viewHolder != null && viewHolder instanceof VideoAdapter.VideoViewHolder) {
VideoAdapter.VideoViewHolder itemHolder = (VideoAdapter.VideoViewHolder) viewHolder;
itemHolder.bindDataFollow(type,videoList.get(positon));//更新UI
}
}
});
} else {
if (type == Constants.FOLLOW_MV) {
initData();
return;
}
}
}
更新屏外緩存UI
package com.aimei.meiktv.util;
import android.support.v7.widget.RecyclerView;
import java.lang.reflect.Field;
import java.util.ArrayList;
/**
* 更新CachedView里的ViewHoler
* by xingchun 2020-12-18
*/
public class RecycleViewCachedViewBindViewUtils {
public static void bindView(RecyclerView rc_video_list, BindView bindView) {
try {
Field field_mRecycler = RecyclerView.class.getDeclaredField("mRecycler");
field_mRecycler.setAccessible(true);
RecyclerView.Recycler recycler = (RecyclerView.Recycler) field_mRecycler.get(rc_video_list);
Field field_mCachedViews = RecyclerView.Recycler.class.getDeclaredField("mCachedViews");//找到屏外緩存
field_mCachedViews.setAccessible(true);
ArrayList<RecyclerView.ViewHolder> mCachedViews = (ArrayList<RecyclerView.ViewHolder>) field_mCachedViews.get(recycler);
if (mCachedViews != null && mCachedViews.size() > 0) {
for (int i = 0; i < mCachedViews.size(); i++) {
RecyclerView.ViewHolder viewHolder = mCachedViews.get(i);
bindView.bindView(viewHolder, viewHolder.getAdapterPosition());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public interface BindView {
void bindView(RecyclerView.ViewHolder viewHolder, int positon);
}
}
更新UI
public void bindDataFollow(int type, NewVideoV3 video) {
if (video==null){
fv_follow_view.setVisibility(View.GONE);
return;
}
fv_follow_view.bindData(video);
if ( video.getFollow_status() == 1
|| video.getFollow_status() == 2
|| (!TextUtils.isEmpty(AppUtil.getUserId()) && AppUtil.getUserId().equals(video.getUser_id()))) {//關(guān)注 (自己或別人的)作品不顯示關(guān)注
fv_follow_view.setVisibility(View.GONE);
} else {//未關(guān)注
fv_follow_view.setVisibility(View.VISIBLE);
}
}