国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[Android] DiffUtil在RecyclerView中的使用詳解

HmyBmny / 3036人閱讀

摘要:是的計算結(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 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步驟

自定義類繼承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 payloads) {
    
    if (payloads.isEmpty()) {
      onBindViewHolder(holder,position);
    } else {
    //更新item
      Bundle bundle = (Bundle) payloads.get(0);
      for(String key : bundle.keySet()) {
        switch (key) {
          case "name":
            holder.mInfo.setText((CharSequence) bundle.get(key));
            break;
        }
      }
    }
  }

  @Override public int getItemCount() {
    return mDatas != null ? mDatas.size() : 0;
  }

  static class DiffItemHolder extends RecyclerView.ViewHolder {

    @BindView(R.id.info) TextView mInfo;

    public DiffItemHolder(View itemView) {
      super(itemView);
      ButterKnife.bind(this, itemView);
    }
  }
}

初始化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 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;
    }
  }
使用DiffUtil

下面通過兩種不同的改變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 payloads)方法。

  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

相關(guān)文章

  • Android AsyncListDiffer-RecyclerView最好的伙伴

    摘要:本文到此結(jié)束不不不,還早著呢,咱們理智分析一下首先這個方法是執(zhí)行在主線程的,如果新舊數(shù)據(jù)比較大,那么這個方法鐵定是會阻塞主線程的計算出后,咱們必須要將新數(shù)據(jù)設(shè)置給,然后才能調(diào)用刷新,然而很多人都會忘記這一步。 showImg(https://segmentfault.com/img/remote/1460000016443448); 版權(quán)聲明:本文已授權(quán)微信公眾號:Android必修...

    617035918 評論0 收藏0

發(fā)表評論

0條評論

HmyBmny

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<