背景:RecyclerView 使用notifyDataSetChanged 會導(dǎo)致圖片閃爍
具體原因可參看:RecyclerView 體驗優(yōu)化及入坑總結(jié)? 的入坑篇第二個問題
一、RecyclerView 局部刷新不好用
? ? ? RecyclerView 除了配置動畫、布局等方便外,相比ListView ,提供了不少數(shù)據(jù)刷新方式,除了常見的notifyDataSetChanged() 全局刷新外,還提供了很多局部刷新方式,列舉如下:

? ? ? ? 上述局部刷新方式,看似好用(插入、更新、移動、刪除),但實際不太好用,甚至基本無用。不好用的原因在于,不知何時去選擇刷新方式 ,因而還是一股腦的使用notifyDataSetChanged() ,除此之外 使用較多的場景 :頁面加載更多 ,使用notifyItemRangeChanged(int ,int),替他刷新方式根本沒有使用過。
? ? ? ?可能正因如此,Google 在?Support-v7:24:2.0 提供了DiffUtils 類,讓我們正真意義上使用RecyclerView 的局部刷新。
二 、DiffUtil 介紹 及使用
? ? ? ? DiffUtil是 Support-v7:24:2.0 中,中更新的工具類,主要是為了配合RecyclerView使用,通過比對新、舊兩個數(shù)據(jù)集的差異,生成舊數(shù)據(jù)到新數(shù)據(jù)的最小變動,然后對有變動的數(shù)據(jù)項,進行局部刷新。
? ?DiffUtil 核心內(nèi)容 :
(1)DiffUtil.Callback??
DiffUtil.Callback? :具體用于限定數(shù)據(jù)集比對規(guī)則 ,內(nèi)部主要有如下5個比較方法:

?1)getOldListSize():舊數(shù)據(jù)集的長度;
?2)getNewListSize():新數(shù)據(jù)集的長度
?3)areItemsTheSame():判斷是否是同一個item;
?4)areContentsTheSame():如果item相同,此方法用于判斷是否同一個 Item 的內(nèi)容也相同;

?5)getChangePayload() :如果item相同,內(nèi)容不同,用 payLoad 記錄這個 ViewHolder 中,具體需要更新那個View
? ? 從圖2知 ,getChangePayload() 默認(rèn)返回 null ,即整個item 全部刷新。

? ? 一般在getChangePayload()方法中調(diào)用super.getChangePayload() 即可,不做精細(xì)化刷新。
? ? ?如果一個item 非常復(fù)雜,存在里面某個View 數(shù)據(jù)刷新,可以利用payLoad參數(shù)來實現(xiàn),對應(yīng)修改點 在 onBindViewHolder(HelloViewHolder holder, int position ) 的基礎(chǔ)上實現(xiàn)帶參的?onBindViewHolder ,同時在getChangedPayLoad()對應(yīng)實現(xiàn)。
從上面分析可知:areItemsTheSame()、areContentsTheSame()、getChangePayload()?分別代表了不同量級的刷新。
(2)DiffUtil.DiffResult
DiffUtil.DiffResult : 比對數(shù)據(jù)集之后,返回的差異結(jié)果 ,通過DiffUtil.calculateDiff(diffCallback)(當(dāng)數(shù)據(jù)量較大時,建議放在子線程中調(diào)用)得到。
DiffUtil.DiffResult::dispatchUpdatesTo() 根據(jù)diff 數(shù)據(jù)結(jié)果,選擇刷新方式。

?總結(jié):使用起來比較簡單 ,1)實現(xiàn)DiffUtil.Callback? ?接口 ;2)新老數(shù)據(jù)集通過DiffUtil.calculateDiff 計算得到DiffUtil.DiffResult? ;3)DiffUtil.DiffResult::dispatchUpdatesTo()? 刷新數(shù)據(jù)。
三、項目使用DiffUtil遇到的問題
? ? ?在第二節(jié)中 ,已經(jīng)基本介紹了DiffUtil 的使用,本節(jié)以精選頁、書城等頁面為例(忽略代碼中邊界值處理)說明 ,在使用過程中遇到的一些問題:
(1)item 判斷 唯一性
? ? ? ?目前 ,setData ()? 數(shù)據(jù)類型為? ArrayList<RowData>? (多ViewType類型)如下 ,對于item 來說 ,缺少唯一性判斷屬性。

? ? ? ?目前,使用mDisplayStyle 和id 來判斷 item 是否一致。 如果item一致,則會進一步判斷 item 內(nèi)容是否一致(areContentsTheSame);如果item 不一致,則item 全部刷新。

(2)?areContentsTheSame() 判斷過于麻煩?
? ?由于RowData 內(nèi) 核心數(shù)據(jù)是mData (Object型),無法直接比較 實現(xiàn)equals ,只能在?areContentsTheSame() 內(nèi),手動實現(xiàn)每種ViewType 數(shù)據(jù)的equals (對象是jce協(xié)議,只能手動實現(xiàn))。
手動實現(xiàn)的問題在于:1)jce協(xié)議新增字段,這里需要補充 ;2)新增ViewType 數(shù)據(jù)需要在這里補充;3)對應(yīng)adapter 與這個callBack 關(guān)聯(lián)性不大,易漏
上述三個問題,可能增加一定的維護成本

RowDataDiffCallback() 整體實現(xiàn)? :

(3)VM 初始化 設(shè)置pullToRefreshRecycleView.setEnableLoadMore(true),導(dǎo)致頁面滑到最下面
? ? ?目前,猜測 是加載更多模塊焦點導(dǎo)致 (猜測未找出原因)
分析一下該問題 :pullToRefreshRecycleView.setEnableLoadMore(true) 設(shè)置本不應(yīng)該在VM初始化時,設(shè)置,應(yīng)該根據(jù)回包數(shù)據(jù) 的hasMore 來設(shè)置 。
直接將pullToRefreshRecycleView.setEnableLoadMore(true)? 改成在回包時,調(diào)用?pullToRefreshRecycleView.setEnableLoadMore(hasMore) 上述問題解決