亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專(zhuān)欄INFORMATION COLUMN

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

HmyBmny / 3119人閱讀

摘要:是的計(jì)算結(jié)果對(duì)象,通過(guò)來(lái)進(jìn)行更新。在線程中調(diào)用,而后的。使用下面通過(guò)兩種不同的改變條目來(lái)介紹的使用。改變第三個(gè)位置的對(duì)象將新數(shù)據(jù)給更新條目后的效果為由圖可知,第四個(gè)位置的條目顯示變?yōu)?。示例代碼在目錄下參考文章使用高效更新詳解帶來(lái)的新工具類(lèi)

概述

DiffUtil是recyclerview support library v7 24.2.0版本中新增的類(lèi),根據(jù)Google官方文檔的介紹,DiffUtil的作用是比較兩個(gè)數(shù)據(jù)列表并能計(jì)算出一系列將舊數(shù)據(jù)表轉(zhuǎn)換成新數(shù)據(jù)表的操作。這個(gè)概念比較抽象,換一種方式理解,DiffUtil是一個(gè)工具類(lèi),當(dāng)你的RecyclerView需要更新數(shù)據(jù)時(shí),將新舊數(shù)據(jù)集傳給它,它就能快速告知adapter有哪些數(shù)據(jù)需要更新。

那么相比直接調(diào)用adapter.notifyDataSetChange()方法,使用DiffUtil有什么優(yōu)勢(shì)呢?它能在收到數(shù)據(jù)集后,提高UI更新的效率,而且你也不需要自己對(duì)新老數(shù)據(jù)集進(jìn)行比較了。

顧名思義,凡是數(shù)據(jù)集的比較DiffUtil都能做,所以用處并不止于更新RecyclerView。DiffUtil也提供了回調(diào)讓你可以進(jìn)行其他操作。本文只介紹使用DiffUtil更新RecyclerView。

DiffUtil簡(jiǎn)介

在使用DiffUtil前我們先簡(jiǎn)單看看DiffUtil的特性。DiffUtil使用Eugene W. Myers的Difference算法來(lái)計(jì)算出將一個(gè)數(shù)據(jù)集轉(zhuǎn)換為另一個(gè)的最小更新量,也就是用最簡(jiǎn)單的方式將一個(gè)數(shù)據(jù)集轉(zhuǎn)換為另一個(gè)。除此之外,DiffUtil還可以識(shí)別一項(xiàng)數(shù)據(jù)在數(shù)據(jù)集中的移動(dòng)。Eugene的算法對(duì)控件進(jìn)行了優(yōu)化,在查找兩個(gè)數(shù)據(jù)集間最少加減操作時(shí)的空間復(fù)雜度為O(N),時(shí)間復(fù)雜度為O(N+D^2)。而如果添加了對(duì)數(shù)據(jù)條目移動(dòng)的識(shí)別,復(fù)雜度就會(huì)提高到O(N^2)。所以如果數(shù)據(jù)集中數(shù)據(jù)不存在移位情況,你可以關(guān)閉移動(dòng)識(shí)別功能來(lái)提高性能。當(dāng)數(shù)據(jù)集較大時(shí),你應(yīng)該在后臺(tái)線程計(jì)算數(shù)據(jù)集的更新。

如何使用 DiffUtil類(lèi)

DiffUtil.Callback:這是最核心的類(lèi),你可以將它理解成比較新老數(shù)據(jù)集時(shí)的規(guī)則。

DiffUtil:通過(guò)靜態(tài)方法DiffUtil.calculateDiff(DiffUtil.Callback)來(lái)計(jì)算數(shù)據(jù)集的更新。

DiffResult:是DiffUtil的計(jì)算結(jié)果對(duì)象,通過(guò)DiffResult.dispatchUpdatesTo(RecyclerView.Adapter)來(lái)進(jìn)行更新。

代碼模式為

DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, newDatas), true);
mAdapter.setDatas(newDatas);
diffResult.dispatchUpdatesTo(mAdapter);

dispatchUpdatesTo()方法它會(huì)自動(dòng)計(jì)算新老數(shù)據(jù)集的差異,并根據(jù)差異情況,自動(dòng)調(diào)用以下四個(gè)方法

adapter.notifyItemRangeInserted(position, count);
adapter.notifyItemRangeRemoved(position, count);
adapter.notifyItemMoved(fromPosition, toPosition);
adapter.notifyItemRangeChanged(position, count, payload);
DiffUtil.Callback抽象類(lèi)
    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();

        /**
         * 比較兩個(gè)Item對(duì)象是否是同一個(gè)對(duì)象
         *
         * @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);

        /**
         * 比較兩個(gè)Item對(duì)象的內(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時(shí)調(diào)用,也就是說(shuō)兩個(gè)對(duì)象代表的數(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步驟

自定義類(lèi)繼承DiffUtil.Callback,通過(guò)重寫(xiě)特定方法給出數(shù)據(jù)比較邏輯。

調(diào)用DiffUtil.calculateDiff(DiffUtil.Callback callback,boolean detectMove)來(lái)計(jì)算更新,得到DiffResult對(duì)象。第二個(gè)參數(shù)可省,意為是否探測(cè)數(shù)據(jù)的移動(dòng),是否關(guān)閉需要根據(jù)數(shù)據(jù)集情況來(lái)權(quán)衡。當(dāng)數(shù)據(jù)集很大時(shí),此操作可能耗時(shí)較長(zhǎng),需要異步計(jì)算。

在UI線程中調(diào)用DiffResult.dispatchUpdatesTo(RecyclerView.Adapter),而后Adapter的onBindViewHolder(RecyclerView.ViewHolder holder, int position, Listpayloads)。注意這個(gè)方法比必須覆蓋的onBindViewHolder(RecyclerView.ViewHolder holder, int position)方法多一個(gè)參數(shù)payloads,而里面存儲(chǔ)了數(shù)據(jù)的更新。

示例 初始化RecyclerView

新建一個(gè)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后效果為:

實(shí)現(xiàn)DiffUtil.Callback

新建類(lèi)繼承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)用,用來(lái)判斷 兩個(gè)對(duì)象是否是相同的Item。
     * 例如,如果你的Item有唯一的id字段,這個(gè)方法就 判斷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)用,用來(lái)檢查 兩個(gè)item是否含有相同的數(shù)據(jù)
     * 這個(gè)方法僅僅在areItemsTheSame()返回true時(shí),才調(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,也就是說(shuō)兩個(gè)對(duì)象代表的數(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

下面通過(guò)兩種不同的改變RecyclerView條目來(lái)介紹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必須要實(shí)現(xiàn)public void onBindViewHolder(DiffItemHolder holder, int position, List payloads)方法。

  private void refresh() {
    mNewDatas.clear();
    mNewDatas.addAll(mDatas);
    //改變第三個(gè)位置的對(duì)象
    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效果為:

由圖可知,第四個(gè)位置的條目顯示變?yōu)閦hang。

結(jié)語(yǔ)

DiffUtil可用于高效進(jìn)行RecyclerView的數(shù)據(jù)更新,但DiffUtil本身的作用是計(jì)算數(shù)據(jù)集的最小更新。DiffUtil有強(qiáng)大的算法支撐,可以利用DiffUtil完成許多其他功能。

示例代碼

在RecyclerView目錄下

參考文章:

使用DiffUtil高效更新RecyclerView
詳解7.0帶來(lái)的新工具類(lèi):DiffUtil

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/65195.html

相關(guān)文章

  • Android AsyncListDiffer-RecyclerView最好的伙伴

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

    617035918 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<