RecyclerView 源碼分析(五) - Adapter的源碼分析

  熟悉RecyclerView的同窗應該都知道,Adapter做爲RecyclerView四大組成部分(AdapterLayoutManagerItemAnimatorItemDecoration)之一,其重要性天然是不言而喻。今天,咱們來分析一下Adapter的源碼。我打算將Adapter的源碼分析分爲兩個部分,一是,從普通的角度上來看Adapter,從源碼的角度上來分析咱們平常使用的一些操做;二是,分析DiffUtil,可能會涉及到Adapter的部分源碼。因此Adapter源碼分析分爲兩篇,本文是第一篇。數組

1. 概述

  在分析Adapter源碼以前,咱們先來回顧一下,咱們常用的幾個方法。緩存

方法名 做用
onCreateViewHolder 建立一個ViewHolder對象,主要做用是將數據保存在ViewHolder,以供後面bind操做使用
onBindViewHolder 數據綁定方法
getItemCount 當前Adapter擁有數據的數量,該方法必須被重寫,不然RecyclerView展現不了任何數據
getItemViewType 該方法帶一個Position,主要是返回當前位置的ViewType。這個方法一般用於一個RecyclerView須要加載不一樣的佈局。
getItemId 該方法表示的意思是返回當前位置Item的id,此方法只在setHasStableIds設置爲true纔會生效
setHasStableIds 設置當前RecyclerViewItemView是否擁有固定id,跟getItemId方法一塊兒使用。若是設置爲true,會提升RecyclerView的緩存效率。

  上表中所列的方法應該就是咱們使用Adapter常用的方法,接下來,我將正式分析Adapter的相關代碼。我打算從以下角度來分析:bash

  1. 從新從RecyclerView緩存角度來分析onCreateViewHolderonBindViewHolder
  2. onBindViewHolder的一個重載方法--主要是用於局部刷新。
  3. 結合Adapter,分析ViewHolder的position。

1. onCreateViewHolder和onBindViewHolder

  onCreateViewHolder方法和onBindViewHolder方法算是咱們使用次數最多的方法,不少自定義Adapter的框架也都是從這兩個方法入手的。咱們來看看這兩個方法到底有什麼做用。app

(1).onCreateViewHolder

  首先,咱們來看一下onCreateViewHolder方法,從它的調用時機入手。框架

  在本文以前,我分析過RecyclerView的緩存機制,當時我將RecyclerView的緩存分爲4級緩存,其中分別是:less

  1. 一級緩存:scrap數組
  2. 二級緩存:CachedView
  3. 三級緩存:ViewCacheExtension
  4. 四級緩存:RecyclerViewPool

  LayoutManager會獲取ViewHolder時,若是4級緩存都沒有命中,就會調用AdapteronCreateViewHolder方法來建立一個新的ViewHolder。咱們來看看相關的代碼:ide

if (holder == null) {
                    long start = getNanoTime();
                    if (deadlineNs != FOREVER_NS
                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
                        // abort - we have a deadline we can't meet return null; } holder = mAdapter.createViewHolder(RecyclerView.this, type); if (ALLOW_THREAD_GAP_WORK) { // only bother finding nested RV if prefetching RecyclerView innerView = findNestedRecyclerView(holder.itemView); if (innerView != null) { holder.mNestedRecyclerView = new WeakReference<>(innerView); } } long end = getNanoTime(); mRecyclerPool.factorInCreateTime(type, end - start); if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder"); } } 複製代碼

  上面的代碼是RecyclerViewtryGetViewHolderForPositionByDeadline方法代碼片斷。以前,咱們在分析緩存機制時,就已經仔細分析這個方法,這裏我就再也不贅述,有興趣的同窗能夠我以前的文章:RecyclerView 源碼分析(三) - RecyclerView的緩存機制源碼分析

  咱們回到上面的代碼片斷中來,從上面的代碼上,咱們看到這裏是調用的是AdaptercreateViewHolder方法來建立ViewHolder。咱們來看看AdaptercreateViewHolder方法:佈局

public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
           try {
                TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
                final VH holder = onCreateViewHolder(parent, viewType);
                if (holder.itemView.getParent() != null) {
                    throw new IllegalStateException("ViewHolder views must not be attached when"
                            + " created. Ensure that you are not passing 'true' to the attachToRoot"
                            + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
                }
                holder.mItemViewType = viewType;
                return holder;
            } finally {
                TraceCompat.endSection();
            }
        }
複製代碼

  其實createViewHolder方法裏面也沒有作什麼的操做,差很少就是調用onCreateViewHolder方法。簡而言之,onCreateViewHolder有點帶兜底的韻味,緩存都沒有命中,只能乖乖的建立ViewHolderpost

  咱們來看看第二方法,也就是onBindViewHolder方法。

(2). onBindViewHolder

  咱們都知道,onBindViewHolder方法的做用是進行數據綁定,因此執行這個方法的條件相對於onCreateViewHolder有點苛刻。爲何呢?咱們這麼想一下吧,假設咱們change了其中一個ItemView的數據,而後經過notifyItemChanged來通知數據源已經改變。在這種狀況下,正常來講,都是隻刷新對應位置的ItemView就好了,不必刷新其餘數據沒有改變的ItemView(這裏的刷新就是指執行onBindViewHolder方法)。如今,咱們來看看對應的執行代碼:

boolean bound = false;
            if (mState.isPreLayout() && holder.isBound()) {
                // do not update unless we absolutely have to.
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                if (DEBUG && holder.isRemoved()) {
                    throw new IllegalStateException("Removed holder should be bound and it should"
                            + " come here only in pre-layout. Holder: " + holder
                            + exceptionLabel());
                }
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
            }
複製代碼

  從上面的代碼,咱們能夠看出來,最後調用了tryBindViewHolderByDeadline方法。而調用tryBindViewHolderByDeadline方法條件比較苛刻,不過無論怎麼苛刻,只要記住一點,若是對應位置的數據被更新了,該位置會執行一次onBindViewHolder方法。咱們繼續看一下tryBindViewHolderByDeadline方法的代碼:

private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
                int position, long deadlineNs) {
            // ······
            mAdapter.bindViewHolder(holder, offsetPosition);
            // ······
        }
複製代碼

  執行過程跟onCreateViewHolder方法差很少,都是在依靠Adapter內部一個對應的final方法來回調。這樣所作的好處,能夠在onBindViewHolder方法執行先後作一些其餘的操做,好比初始化操做和清理操做,這種模式有點相似於Java中靜態代理模式中的繼承代理。而後,咱們來看看AdapterbindViewHolder方法:

public final void bindViewHolder(@NonNull VH holder, int position) {
            holder.mPosition = position;
            if (hasStableIds()) {
                holder.mItemId = getItemId(position);
            }
            holder.setFlags(ViewHolder.FLAG_BOUND,
                    ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
                            | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
            TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
            onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
            holder.clearPayload();
            final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
            if (layoutParams instanceof RecyclerView.LayoutParams) {
                ((LayoutParams) layoutParams).mInsetsDirty = true;
            }
            TraceCompat.endSection();
        }
複製代碼

  從這裏,咱們能夠簡單發現,在執行onBindViewHolder方法先後,各自作了一些不一樣的操做。好比,在執行onBindViewHolder方法以前,更新了ViewHoldermPosition屬性和給ViewHolder設置了一些flag;在執行onBindViewHolder方法以後,清理了ViewHolderpayload,而且仍是給ItemViewLayoutParamsmInsetsDirty屬性設置爲true。

  這裏額外的提出兩個點:

  1. payload主要是用於局部刷新的,待會我會詳細解釋怎麼進行局部刷新。
  2. 關於LayoutParamsmInsetsDirty屬性,這個屬性尤其重要的,主要用於ItemView的繪製,後續我在分析ItemDecoration時會詳細的解釋這個屬性。

3. 局部刷新的基本使用和實現原理

(1). 局部刷新的基本使用

  在分析局部刷新以前,咱們先來討論一下怎麼進行佈局刷新,也就是說怎麼經過RecyclerView實現ItemView的局部刷新。

  假設下面的一個Demo:

  點擊一下下面灰色的Buttonposition爲0的ItemView會改變顯示的文字。若是咱們不作局部刷新,出現什麼問題呢?咱們先來試試:

mDataList.get(0).setString("change data");
        mAdapter.notifyItemChanged(0);
複製代碼

  正常的實現應該就是上面的代碼,很是的簡單,也是咱們常常書寫的代碼。這樣書寫有什麼問題嗎?有很大的問題!就是整個ItemView會閃爍一下,效果以下:

  網上給了一堆的緣由分析,我我的以爲,緣由很是的簡單,就是第一個ItemView執行的change動畫。因此介於這兩個緣由,咱們能夠找到兩種解決方案:

  1. 設置RecyclerView的change動畫時間爲0,也就是調用ItemAnimatorsetChangeDuration方法。
  2. 直接將RecyclerViewItemAnimator設置爲null。

  對於第二種方案,我不置能否。這樣來想,咱們直接將動畫設置爲null,那麼RecyclerView就沒有任何動畫,是否是感受有點得不償失?

  第一種方案比起第二種方案稍微要好一些,咱們將change動畫時間設置爲0,隻影響了change動畫(至關於取消了change動畫),不會影響其餘其餘操做的動畫。不過,仍是感受美中不足,至關於後面全部的change操做都沒有了動畫,若是我想有些change操做有動畫呢?

  此時就須要局部刷新出手了。咱們先來看看怎麼實現局部刷新:

  首先,調用帶兩個參數的notifyItemChanged方法,以下:

mAdapter.notifyItemChanged(0, "");
複製代碼

  第二參數是一個payload,Object類型,因此咱們能夠傳遞任意對象,這裏就傳遞一個空字符串。

  而後咱們得重寫AdapteronBindViewHolder方法(這裏重寫的是帶三個參數的onBindViewHolder方法,帶兩個參數的onBindViewHolder該怎麼寫就怎麼寫)。

@Override
    public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position, @NonNull List<Object> payloads) {
        if (payloads.contains("")) {
            holder.mTextView.setText(mDataList.get(position).getString());
        } else {
            super.onBindViewHolder(holder, position, payloads);
        }
    }
複製代碼

  這裏咱們判斷了一下payloads裏面是否含有以前咱們傳遞的空字符串,若是含有的話,直接更新顯示文字便可,若是不含有則走默認邏輯。如今來咱們看看效果:

  是否是感受perfect?

(2). 局部刷新的實現原理

  局部刷新的使用是很是的簡單的,就是重寫了Adapter帶三個參數的onBindViewHolder方法,而後調用的也是帶兩個參數的notifyItemChanged方法。

  可是,咱們不由好奇,爲何這樣作ItemView就不會閃爍呢?我在這裏就能夠告訴你們答案,是由於沒有執行change動畫。爲了保持求知若飢,虛心若愚的良好傳統,你們確定會進一步的問,爲何在這種狀況下不會執行動畫呢?其實爲了回答這個問題,我早就已經爲你們打好鋪墊,在理解局部刷新的原理以前,你們最好已經理解了RecyclerView的動畫機制,有興趣的同窗能夠看看我以前的文章:RecyclerView 源碼分析(四) - RecyclerView的動畫機制

  前面頻繁的源碼追蹤咱們這裏就不進行了,能夠參考個人文章:RecyclerView 源碼分析(四) - RecyclerView的動畫機制,這裏直接從根源入手。咱們都知道,當咱們調用AdapternotifyItemChanged方法,會執行到AdapterHelper$CallbackmarkViewHoldersUpdated方法。

  而咱們這裏不看markViewHoldersUpdated方法,而是看哪裏調用了這個方法。

  根據咱們辛苦的追蹤代碼,咱們發現主要是有兩個地方在調用markViewHoldersUpdated方法:1. postponeAndUpdateViewHolders方法;2. consumeUpdatesInOnePass方法。

  這其中,consumeUpdatesInOnePass方法是咱們的老朋友,該方法主要是在dispatchLayoutStep2方法,其做用也是不言而喻,主要是給消費以前添加的operation。而postponeAndUpdateViewHolders方法咱們就感受很是的陌生,這個方法是在哪裏被調用呢?

  根據咱們的追蹤,發現它的調用源頭是AdapterHelperpreProcess方法。而preProcess方法又是在哪裏被調用的呢?是在processAdapterUpdatesAndSetAnimationFlags方法:

private void processAdapterUpdatesAndSetAnimationFlags() {
        // ······
        if (predictiveItemAnimationsEnabled()) {
            mAdapterHelper.preProcess();
        } else {
            mAdapterHelper.consumeUpdatesInOnePass();
        }
        // ······
    }
複製代碼

  而processAdapterUpdatesAndSetAnimationFlags方法只在dispatchLayoutStep1方法調用(這裏不考慮非自動測量的狀況)。這裏,咱們就完全明瞭。dispatchLayoutStep1方法階段被預佈局階段,也就是說,change操做在預佈局階段就已經回調markViewHoldersUpdated方法。

  而markViewHoldersUpdated方法的做用是啥呢?其實在RecyclerView 源碼分析(四) - RecyclerView的動畫機制,我就已經解釋過了,主要做用有兩個:

  1. 給每一個ViewHolder打了對應的flag
  2. 更新每一個ViewHolder的position。

  關於這兩個做用的分析,flag咱們能夠直接跳過,position在後面我會詳細的分析。

  從而,咱們知道,在預佈局階段,每一個ViewHolder的position和flag就已經肯定了,這個有什麼做用呢?還記得咱們以前分析RecyclerView的動畫機制說過,在預佈局階段若是條件容許的話,會進行一次佈局,也就是會調用LayoutManageronLayouyChildren方法。

  而onLayouyChildren方法會作啥呢?我主要介紹兩點(這裏以LinearLayoutManager爲例):

1.調用LayoutManagerdetachAndScrapAttachedViews方法,回收全部的ViewHolder,將他們放入四級緩存中。 2. 調用fill方法進行佈局。在fill方法調用流程會調用RecyclerViewtryGetViewHolderForPositionByDeadline方法從緩存中獲取ViewHolder

  這裏咱們先來看回收部分,咱們知道detachAndScrapAttachedViews方法最終會調用到RecyclerscrapView方法裏面去。咱們來看看scrapView方法(請你們睜大眼睛,這是尋找答案的第一條線索):

void scrapView(View view) {
            final ViewHolder holder = getChildViewHolderInt(view);
            if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
                    || !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
                if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
                    throw new IllegalArgumentException("Called scrap view with an invalid view."
                            + " Invalid views cannot be reused from scrap, they should rebound from"
                            + " recycler pool." + exceptionLabel());
                }
                holder.setScrapContainer(this, false);
                mAttachedScrap.add(holder);
            } else {
                if (mChangedScrap == null) {
                    mChangedScrap = new ArrayList<ViewHolder>();
                }
                holder.setScrapContainer(this, true);
                mChangedScrap.add(holder);
            }
        }
複製代碼

  從這裏咱們知道,scrapView方法的做用就是ViewHolder分別放到mAttachedScrapmChanedScrap數組。這裏咱們重點關注canReuseUpdatedViewHolder(holder)這個判斷條件,咱們來追蹤這個方案的代碼,最終咱們找到了DefaultItemAnimatorcanReuseUpdatedViewHolder方法:

@Override
    public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
            @NonNull List<Object> payloads) {
        return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
    }
複製代碼

  看到沒?這裏判斷了一下payloads是否爲空。這個有什麼做用呢?咱們回到scrapView方法來,若是payloads不爲空的話,當前的ViewHolder會被回收到mAttachedScrap。這裏,咱們必定要記得,當ViewHolderpayloads不爲空,那麼在回收時,ViewHolder會被回收到mAttachedScrap。這個有什麼做用呢?這就須要咱們去尋找第二條線索。

  第二條線索就藏在tryGetViewHolderForPositionByDeadline方法裏面。咱們來瞅瞅:

ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
            final int scrapCount = mAttachedScrap.size();

            // Try first for an exact, non-invalid match from scrap.
            for (int i = 0; i < scrapCount; i++) {
                final ViewHolder holder = mAttachedScrap.get(i);
                if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
                        && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
                    holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
                    return holder;
                }
            }
            // ······
        }
複製代碼

  結合上面的分析,當在預佈局階段,也就是dispatchLayoutStep1階段進行佈局,經過帶兩個參數的notifyItemChanged方法進行通知,確定會在上面的代碼返回一個ViewHolder。也就是說,在這種狀況下,變化先後該ItemViewViewHolder確定是同一個ViewHolder

  如上就是第二條線索,那第二條線索有什麼做用呢?就得看第三條線索了。那第三條線索在哪裏呢?就在dispatchLayoutStep3方法裏面。

  咱們都知道,dispatchLayoutStep3階段被稱爲後佈局,主要進行動畫的執行,咱們來看看咱們的change操做會執行哪些代碼:

private void dispatchLayoutStep3() {
        // ······
        if (mState.mRunSimpleAnimations) {
            for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
                           // ······
                            animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
                                    oldDisappearing, newDisappearing);
                           // ······
        }
        // ······
    }
複製代碼

  change操做確定會執行到如上的代碼,咱們在分析動畫機制時就已經分析過了。咱們來看看animateChange方法:

private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
            @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
            boolean oldHolderDisappearing, boolean newHolderDisappearing) {
        // ······
        if (oldHolder != newHolder) {
             // ······
            addAnimatingView(oldHolder);
             // ······
        }
         // ······
    }
複製代碼

  看到沒,這就是最終的答案,只有兩個ViewHolder不是同一個對象纔會添加一個AnimatingView

  因爲局部刷新的先後,ItemView的是同一個ViewHolder對象,纔會致使局部刷新不會執行change動畫,纔會解決ItemView的閃爍。

  有可能有人又有疑問了,爲何會全局刷新不是同一個ViewHolder呢?咱們經過scrapView方法能夠知道,若是全局刷新,那麼change的ViewHolder會被回收到mChangedScrap數組裏面去,而在tryGetViewHolderForPositionByDeadline方法裏面,咱們能夠知道,只有預佈局階段纔會從mChangedScrap數組裏面獲取ViewHolder對象:

if (mState.isPreLayout()) {
                holder = getChangedScrapViewForPosition(position);
                fromScrapOrHiddenOrCache = holder != null;
            }
複製代碼

  因此預佈局階段和正式佈局階段同一個ItemView確定是不一樣的ViewHolder,從而會執行change動畫。

  因爲這個問題的答案尋找起來比較麻煩,這裏我就針對這個問題作一個簡單的總結:

佈局刷新之因此能解決ItemView的閃爍問題,是由於在局部刷新的狀況下,不會執行change動畫。而不執行的chang動畫的緣由是由於在刷新先後都是同一個ViewHolder,而且都是從mAttachedScrap數組裏面得到,因此在動畫執行階段,不會執行局部刷新致使的change動畫,進而解決閃爍問題;而全局刷新因爲刷新先後不是同一個ViewHolder,因此會執行change動畫。

4. ViewHolder的position

  在ViewHolder的內部有幾個讓人難以理解的問題,一個是flag,衆多的flag讓人很是的懵逼,這個我在緩存機制那一篇文章,我已經作了詳細的總結,有興趣的同窗能夠看看個人文章:RecyclerView 源碼分析(三) - RecyclerView的緩存機制;另外一個是position,本文來重點分析一下。

  這裏主要分析兩個方法,分別是getAdapterPositiongetLayoutPosition,對應着ViewHolder內部兩個成員變量mPositionmPreLayoutPosition兩個屬性。

  你們在使用這兩個方法時,應該都對這兩個方法有必定的疑問,這裏我簡單的解釋一下這兩個方法的區別(其實咱們從這兩個方法的註釋就能看出區別)。

  咱們先來看看這兩個方法的代碼,首先來看一下getAdapterPosition方法:

public final int getAdapterPosition() {
            if (mOwnerRecyclerView == null) {
                return NO_POSITION;
            }
            return mOwnerRecyclerView.getAdapterPositionFor(this);
        }
複製代碼

  別看getAdapterPosition方法比較麻煩,還調用了RecyclerViewgetAdapterPositionFor方法進行位置的計算。可是它表達的意思是很是簡單的,就是獲取當前ViewHolder所綁定ItemView的真實位置。這裏的真實位置說的比較籠統,這樣來解釋吧,當咱們remove掉爲position爲0的item,正常來講,後面ViewHolder的position應該都減1。可是RecyclerView處理Adapter的更新採用的延遲處理策略,因此在正式處理以前獲取ViewHolder的位置可能會出現偏差,介於這個緣由,getAdapterPosition方法就出現了。

  getAdapterPosition方法是怎樣保證每次計算都是正確的呢?包括在正式處理以前呢?咱們知道,在RecyclerView中,延遲處理的實現是在notify階段往一個叫mPendingUpdates數組裏面添加Operation,分別在dispatchLayoutStep1階段或者dispatchLayoutStep2階段進行處理。經過追蹤getAdapterPositionFor方法,咱們知道getAdapterPosition方法在計算位置時,考慮到mPendingUpdates數組的存在,因此在notify階段和dispatchLayoutStep1階段之間(這裏假設dispatchLayoutStep1就會處理),getAdapterPosition方法返回正確的位置。

  而getLayoutPosition方法呢?getLayoutPosition方法就不能保證在notify階段和dispatchLayoutStep1階段之間獲取的位置是正確的。爲何這麼說呢?咱們來看看getLayoutPosition方法的代碼:

public final int getLayoutPosition() {
            return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
        }
複製代碼

  getLayoutPosition方法返回的是mPosition或者mPreLayoutPosition,可是在dispatchLayoutStep1階段以前,還未更新每一個ViewHolder的position,因此得到不必定的是正確(只有在處理mPendingUpdates的操做時,position纔會被更新,對應着的代碼就是執行AdapterHelper$Callback接口的方法)。

  可是getLayoutPosition方法爲何還有存在的必要呢?咱們發現getLayoutPosition方法不會每次都計算,也就是說,getLayoutPosition方法的效率比getAdapterPosition方法高。當咱們在Adapter這種調用方法來獲取ViewHolder的位置時,能夠優先考慮getLayoutPosition方法,由於Adapter的方法回調階段不在mPendingUpdates處理以前,因此此時getLayoutPosition方法跟getAdapterPosition方法沒有任何區別了。

  可是須要注意,若是咱們在其餘地方獲取ViewHolderposition,要特別注意這種狀況,由於其餘地方不能保證與RecyclerView狀態同步,這種狀況爲了保證結果的正確性,咱們應該優先考慮getAdapterPosition方法。

5. 總結

  本文到這裏差很少就結束了,在這裏咱們作一個簡單的總結

  1. 之因此局部刷新能解決ItemView閃爍的問題,是由於局部刷新進行change操做時沒有執行change動畫。而沒有執行change動畫的緣由是由於在預佈局階段和後佈局階段,ItemViewViewHolder是同一個對象。
  2. getAdapterPosition方法在任什麼時候候獲取的都是ViewHolder真實的位置,而getLayoutPosition方法只在mPendingUpdates數組處理以後才能獲取真實的位置。這是兩個方法區別。

  下一篇文章是分析DiffUtil的實現,來看看DiffUtil來怎麼實現差量計算的。

相關文章
相關標籤/搜索