摘要:是的計算結(jié)果對象,通過來進行更新。在線程中調(diào)用,而后的。使用下面通過兩種不同的改變條目來介紹的使用。改變第三個位置的對象將新數(shù)據(jù)給更新條目后的效果為由圖可知,第四個位置的條目顯示變?yōu)椤J纠a在目錄下參考文章使用高效更新詳解帶來的新工具類
概述
DiffUtil是recyclerview support library v7 24.2.0版本中新增的類,根據(jù)Google官方文檔的介紹,DiffUtil的作用是比較兩個數(shù)據(jù)列表并能計算出一系列將舊數(shù)據(jù)表轉(zhuǎn)換成新數(shù)據(jù)表的操作。這個概念比較抽象,換一種方式理解,DiffUtil是一個工具類,當你的RecyclerView需要更新數(shù)據(jù)時,將新舊數(shù)據(jù)集傳給它,它就能快速告知adapter有哪些數(shù)據(jù)需要更新。
那么相比直接調(diào)用adapter.notifyDataSetChange()方法,使用DiffUtil有什么優(yōu)勢呢?它能在收到數(shù)據(jù)集后,提高UI更新的效率,而且你也不需要自己對新老數(shù)據(jù)集進行比較了。
顧名思義,凡是數(shù)據(jù)集的比較DiffUtil都能做,所以用處并不止于更新RecyclerView。DiffUtil也提供了回調(diào)讓你可以進行其他操作。本文只介紹使用DiffUtil更新RecyclerView。
DiffUtil簡介在使用DiffUtil前我們先簡單看看DiffUtil的特性。DiffUtil使用Eugene W. Myers的Difference算法來計算出將一個數(shù)據(jù)集轉(zhuǎn)換為另一個的最小更新量,也就是用最簡單的方式將一個數(shù)據(jù)集轉(zhuǎn)換為另一個。除此之外,DiffUtil還可以識別一項數(shù)據(jù)在數(shù)據(jù)集中的移動。Eugene的算法對控件進行了優(yōu)化,在查找兩個數(shù)據(jù)集間最少加減操作時的空間復(fù)雜度為O(N),時間復(fù)雜度為O(N+D^2)。而如果添加了對數(shù)據(jù)條目移動的識別,復(fù)雜度就會提高到O(N^2)。所以如果數(shù)據(jù)集中數(shù)據(jù)不存在移位情況,你可以關(guān)閉移動識別功能來提高性能。當數(shù)據(jù)集較大時,你應(yīng)該在后臺線程計算數(shù)據(jù)集的更新。
如何使用 DiffUtil類DiffUtil.Callback:這是最核心的類,你可以將它理解成比較新老數(shù)據(jù)集時的規(guī)則。
DiffUtil:通過靜態(tài)方法DiffUtil.calculateDiff(DiffUtil.Callback)來計算數(shù)據(jù)集的更新。
DiffResult:是DiffUtil的計算結(jié)果對象,通過DiffResult.dispatchUpdatesTo(RecyclerView.Adapter)來進行更新。
代碼模式為
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true); mAdapter.setDatas(newDatas); diffResult.dispatchUpdatesTo(mAdapter);
dispatchUpdatesTo()方法它會自動計算新老數(shù)據(jù)集的差異,并根據(jù)差異情況,自動調(diào)用以下四個方法
adapter.notifyItemRangeInserted(position, count); adapter.notifyItemRangeRemoved(position, count); adapter.notifyItemMoved(fromPosition, toPosition); adapter.notifyItemRangeChanged(position, count, payload);DiffUtil.Callback抽象類
public abstract static class Callback { /** * 返回舊數(shù)據(jù)集的大小 * * @return The size of the old list. */ public abstract int getOldListSize(); /** * 返回新數(shù)據(jù)集的大小 * * @return The size of the new list. */ public abstract int getNewListSize(); /** * 比較兩個Item對象是否是同一個對象 * * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list * @return True if the two items represent the same object or false if they are different. */ public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition); /** * 比較兩個Item對象的內(nèi)容是否相同 * Called by the DiffUtil when it wants to check whether two items have the same data. * DiffUtil uses this information to detect if the contents of an item has changed. *DiffUtil步驟* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)} * so that you can change its behavior depending on your UI. * For example, if you are using DiffUtil with a * {@link android.support.v7.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should * return whether the items" visual representations are the same. *
* This method is called only if {@link #areItemsTheSame(int, int)} returns * {@code true} for these items. * * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list which replaces the * oldItem * @return True if the contents of the items are the same or false if they are different. */ public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition); /** * areItemsTheSame()返回true而areContentsTheSame()返回false時調(diào)用,也就是說兩個對象代表的數(shù)據(jù)是一條,但是內(nèi)容更新了。 * When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and * {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil * calls this method to get a payload about the change. *
* For example, if you are using DiffUtil with {@link RecyclerView}, you can return the * particular field that changed in the item and your * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that * information to run the correct animation. *
* Default implementation returns {@code null}. * * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list * * @return A payload object that represents the change between the two items. */ @Nullable public Object getChangePayload(int oldItemPosition, int newItemPosition) { return null; } }
自定義類繼承DiffUtil.Callback,通過重寫特定方法給出數(shù)據(jù)比較邏輯。
調(diào)用DiffUtil.calculateDiff(DiffUtil.Callback callback,boolean detectMove)來計算更新,得到DiffResult對象。第二個參數(shù)可省,意為是否探測數(shù)據(jù)的移動,是否關(guān)閉需要根據(jù)數(shù)據(jù)集情況來權(quán)衡。當數(shù)據(jù)集很大時,此操作可能耗時較長,需要異步計算。
在UI線程中調(diào)用DiffResult.dispatchUpdatesTo(RecyclerView.Adapter),而后Adapter的onBindViewHolder(RecyclerView.ViewHolder holder, int position, Listpayloads)。注意這個方法比必須覆蓋的onBindViewHolder(RecyclerView.ViewHolder holder, int position)方法多一個參數(shù)payloads,而里面存儲了數(shù)據(jù)的更新。
示例 初始化RecyclerView新建一個Bean為Item:
package com.michael.materialdesign.bean; /** * Created by liuguoquan on 2016/10/18. */ public class Item { public int id = 0; public String name; public Item(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
新建Adapter
public class DiffUtilAdapter extends RecyclerView.Adapter{ private Context mContext; private List - mDatas; public DiffUtilAdapter(Context context, List
- datas) { this.mContext = context; this.mDatas = datas; } public void setDatas(List
- mDatas) { this.mDatas = mDatas; } @Override public DiffItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(mContext).inflate(R.layout.item_diff_util, parent, false); return new DiffItemHolder(view); } @Override public void onBindViewHolder(DiffItemHolder holder, int position) { Item info = mDatas.get(position); holder.mInfo.setText(info.getName()); Log.d("lgq","onBindViewHolder"); } //payloads就是DiffUtil.Callback中的getChangePayload方法返回的數(shù)據(jù)集 @Override public void onBindViewHolder(DiffItemHolder holder, int position, List
初始化ReyclerView
private void initView() { for(int i = 0; i < 20;i++) { Item item = new Item(i,"liu"+i); mDatas.add(item); } mAdapter = new DiffUtilAdapter(this,mDatas); mList.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)); mList.setItemAnimator(new DefaultItemAnimator()); mList.setAdapter(mAdapter); }
初始化RecyclerView后效果為:
實現(xiàn)DiffUtil.Callback新建類繼承DiffUtil.Callback
private class DiffCallback extends DiffUtil.Callback { private List使用DiffUtil- mOldDatas; private List
- mNewDatas; //傳入舊數(shù)據(jù)和新數(shù)據(jù)的集合 public DiffCallback(List
- oldDatas,List
- newDatas) { this.mOldDatas = oldDatas; this.mNewDatas = newDatas; } @Override public int getOldListSize() { return mOldDatas != null ? mOldDatas.size() : 0; } @Override public int getNewListSize() { return mNewDatas != null ? mNewDatas.size() : 0; } /** * 被DiffUtil調(diào)用,用來判斷 兩個對象是否是相同的Item。 * 例如,如果你的Item有唯一的id字段,這個方法就 判斷id是否相等。 * 本例判斷id字段是否一致 */ @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { boolean is = mOldDatas.get(oldItemPosition).id == mNewDatas.get(newItemPosition).id; Log.d("lgq","areItemsTheSame " +oldItemPosition + " " + newItemPosition + " " + is); return is; } /* * 被DiffUtil調(diào)用,用來檢查 兩個item是否含有相同的數(shù)據(jù) * 這個方法僅僅在areItemsTheSame()返回true時,才調(diào)用。 * @param oldItemPosition The position of the item in the old list * @param newItemPosition The position of the item in the new list which replaces the * oldItem * @return True if the contents of the items are the same or false if they are different. */ @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { String oldName = mOldDatas.get(oldItemPosition).getName(); String newName = mNewDatas.get(newItemPosition).getName(); Log.d("lgq","areContentsTheSame" + " " +oldName + " " + newName); if (!oldName.equals(newName)) { Log.d("lgq","false"); return false; } return true; } /** * areItemsTheSame()返回true而areContentsTheSame()返回false,也就是說兩個對象代表的數(shù)據(jù)是一條,但是內(nèi)容更新了。 * @param oldItemPosition * @param newItemPosition * @return */ @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); } if (bundle.size() == 0) { return null; } Log.d("lgq","getChangePayload"); return bundle; } }
下面通過兩種不同的改變RecyclerView條目來介紹DiffUtil的使用。
增加或刪除條目
這種情況下,數(shù)據(jù)集的大小改變,反映在RecyclerView的效果就是增加或者刪除條目
private void add() { mNewDatas.clear(); mNewDatas.addAll(mDatas); mNewDatas.add(new Item(89,"xiao")); mNewDatas.add(new Item(90,"xia")); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallback(mDatas,mNewDatas),true); mAdapter.setDatas(mNewDatas); diffResult.dispatchUpdatesTo(mAdapter); mDatas.clear(); mDatas.addAll(mNewDatas); }
增加條目后的RecyclerView的效果為:
更新具體的條目
這種情況下數(shù)據(jù)集大小不改變,改變數(shù)據(jù)集中條目的內(nèi)容,反映在RecyclerView的效果就是更新具體的條目,這回調(diào)用Callback中的getChangePayload方法,而Adapter必須要實現(xiàn)public void onBindViewHolder(DiffItemHolder holder, int position, List方法。
private void refresh() { mNewDatas.clear(); mNewDatas.addAll(mDatas); //改變第三個位置的對象 Item item = new Item(3,"zhang"); mNewDatas.remove(3); mNewDatas.add(3,item); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallback(mDatas,mNewDatas),true); //將新數(shù)據(jù)給Adapter mAdapter.setDatas(mNewDatas); diffResult.dispatchUpdatesTo(mAdapter); mDatas.clear(); mDatas.addAll(mNewDatas); }
更新條目后的RecyclerView效果為:
由圖可知,第四個位置的條目顯示變?yōu)閦hang。
結(jié)語DiffUtil可用于高效進行RecyclerView的數(shù)據(jù)更新,但DiffUtil本身的作用是計算數(shù)據(jù)集的最小更新。DiffUtil有強大的算法支撐,可以利用DiffUtil完成許多其他功能。
示例代碼在RecyclerView目錄下
參考文章:
使用DiffUtil高效更新RecyclerView
詳解7.0帶來的新工具類:DiffUtil
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/65195.html
摘要:本文到此結(jié)束不不不,還早著呢,咱們理智分析一下首先這個方法是執(zhí)行在主線程的,如果新舊數(shù)據(jù)比較大,那么這個方法鐵定是會阻塞主線程的計算出后,咱們必須要將新數(shù)據(jù)設(shè)置給,然后才能調(diào)用刷新,然而很多人都會忘記這一步。 showImg(https://segmentfault.com/img/remote/1460000016443448); 版權(quán)聲明:本文已授權(quán)微信公眾號:Android必修...
閱讀 671·2023-04-25 18:59
閱讀 1211·2021-09-22 16:00
閱讀 1889·2021-09-22 15:42
閱讀 3594·2021-09-22 15:27
閱讀 1246·2019-08-30 15:54
閱讀 1104·2019-08-30 11:16
閱讀 2445·2019-08-29 16:24
閱讀 820·2019-08-29 12:14