日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

RecyclerView局部刷新机制——payload

發(fā)布時(shí)間:2024/4/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 RecyclerView局部刷新机制——payload 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

這篇文章其實(shí)之前就完成了,一直遺忘在角落里了,今天無意翻之前的筆記發(fā)現(xiàn)的,大部分內(nèi)容應(yīng)該還是有效的。

之前在使用RecyclerView的遇到過一個(gè)問題,使用notifyItemChanged刷新數(shù)據(jù)的時(shí)候會(huì)出現(xiàn)重影或者閃爍的現(xiàn)象。

這個(gè)問題很容易出現(xiàn),當(dāng)我們的列表中有進(jìn)度顯示(比如下載),這時(shí)候需要不停的更新進(jìn)度,就需要使用notifyItemChanged

使用notifyItemChanged可以只刷新那一個(gè)item,這樣就避免了像ListView那樣全部刷新

但是如果使用notifyItemChanged(position),在滑動(dòng)的時(shí)候刷新就會(huì)出現(xiàn)重影或者閃爍的問題。

解決這個(gè)問題很簡(jiǎn)單,將notifyItemChanged(position)替換為notifyItemChanged(position,0)即可。

測(cè)試問題確實(shí)解決了,但是為啥?這個(gè)參數(shù)有啥用?

源碼分析

我們從源碼入手來看看

public final void notifyItemChanged(int position, @Nullable Object payload) {this.mObservable.notifyItemRangeChanged(position, 1, payload); }

可以看到payload是一個(gè)object,并非int。它調(diào)用了mObservable的notifyItemRangeChanged

public void notifyItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {for(int i = this.mObservers.size() - 1; i >= 0; --i) {((RecyclerView.AdapterDataObserver)this.mObservers.get(i)).onItemRangeChanged(positionStart, itemCount, payload);}}

調(diào)用了AdapterDataObserver的onItemRangeChanged,這是一個(gè)接口,它的實(shí)現(xiàn)是RecyclerViewDataObserver,實(shí)現(xiàn)的函數(shù)

public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {RecyclerView.this.assertNotInLayoutOrScroll((String)null);if(RecyclerView.this.mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {this.triggerUpdateProcessor();} }

又調(diào)用了mAdapterHelper的onItemRangeChanged

boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {if(itemCount < 1) {return false;} else {this.mPendingUpdates.add(this.obtainUpdateOp(4, positionStart, itemCount, payload));this.mExistingUpdateTypes |= 4;return this.mPendingUpdates.size() == 1;} }

調(diào)用了obtainUpdateOp函數(shù)

public AdapterHelper.UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount, Object payload) {AdapterHelper.UpdateOp op = (AdapterHelper.UpdateOp)this.mUpdateOpPool.acquire();if(op == null) {op = new AdapterHelper.UpdateOp(cmd, positionStart, itemCount, payload);} else {op.cmd = cmd;op.positionStart = positionStart;op.itemCount = itemCount;op.payload = payload;}return op; }

可以看到作為參數(shù)賦給一個(gè)UpdateOp對(duì)象,那么哪里使用了這個(gè)對(duì)象的payload?

在AdapterHelper中查找發(fā)現(xiàn)有幾處這樣的代碼

this.mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);

這個(gè)callback也是一個(gè)接口,在RecyclerView中可以找到它的實(shí)現(xiàn),其中對(duì)應(yīng)的函數(shù):

public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {RecyclerView.this.viewRangeUpdate(positionStart, itemCount, payload);RecyclerView.this.mItemsChanged = true; }

調(diào)用了viewRangeUpdate函數(shù)

void viewRangeUpdate(int positionStart, int itemCount, Object payload) {int childCount = this.mChildHelper.getUnfilteredChildCount();int positionEnd = positionStart + itemCount;for(int i = 0; i < childCount; ++i) {View child = this.mChildHelper.getUnfilteredChildAt(i);RecyclerView.ViewHolder holder = getChildViewHolderInt(child);if(holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart && holder.mPosition < positionEnd) {holder.addFlags(2);holder.addChangePayload(payload);((RecyclerView.LayoutParams)child.getLayoutParams()).mInsetsDirty = true;}}this.mRecycler.viewRangeUpdate(positionStart, itemCount); }

可以看到調(diào)用了holder的addChangePayload

void addChangePayload(Object payload) {if(payload == null) {this.addFlags(1024);} else if((this.mFlags & 1024) == 0) {this.createPayloadsIfNeeded();this.mPayloads.add(payload);}}private void createPayloadsIfNeeded() {if(this.mPayloads == null) {this.mPayloads = new ArrayList();this.mUnmodifiedPayloads = Collections.unmodifiableList(this.mPayloads);}}List<Object> getUnmodifiedPayloads() {return (this.mFlags & 1024) == 0?(this.mPayloads != null && this.mPayloads.size() != 0?this.mUnmodifiedPayloads:FULLUPDATE_PAYLOADS):FULLUPDATE_PAYLOADS; }

這里有兩個(gè)list,mPayloads和mUnmodifiedPayloads,在getUnmodifiedPayloads中可以看到當(dāng)mPayloads不為空才會(huì)返回mUnmodifiedPayloads,否則返回FULLUPDATE_PAYLOADS,即Collections.EMPTY_LIST。

在RecyclerView中搜索getUnmodifiedPayloads函數(shù),發(fā)現(xiàn)其中一處應(yīng)該跟我們的問題有關(guān)

boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {return this.mItemAnimator == null || this.mItemAnimator.canReuseUpdatedViewHolder(viewHolder, viewHolder.getUnmodifiedPayloads()); }

payloads應(yīng)該對(duì)這個(gè)函數(shù)的返回值有影響,繼續(xù)看mItemAnimator的對(duì)應(yīng)函數(shù)。

這個(gè)mItemAnimator也是一個(gè)接口,實(shí)現(xiàn)類是DefaultItemAnimator,它的對(duì)應(yīng)函數(shù)

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

可以看到如果payloads不為空,即最開始的payload不為null(因?yàn)槭莖bject,所以0還是其它都無所謂,只要不為空就行),canReuseUpdatedViewHolder則為true。

那么canReuseUpdatedViewHolder影響什么,同樣在RecyclerView中搜索發(fā)現(xiàn)

void scrapView(View view) {RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(view);if(!holder.hasAnyOfTheFlags(12) && holder.isUpdated() && !RecyclerView.this.canReuseUpdatedViewHolder(holder)) {if(this.mChangedScrap == null) {this.mChangedScrap = new ArrayList();}holder.setScrapContainer(this, true);this.mChangedScrap.add(holder);} else {if(holder.isInvalid() && !holder.isRemoved() && !RecyclerView.this.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." + RecyclerView.this.exceptionLabel());}holder.setScrapContainer(this, false);this.mAttachedScrap.add(holder);}}

可以看到如果有payload,holder會(huì)被放入mAttachedScrap,否則放入mChangedScrap。

mAttachedScrap和mChangedScrap

這兩個(gè)就涉及到RecyclerView的緩存機(jī)制了,整個(gè)緩存機(jī)制包含多個(gè)集合,這兩個(gè)集合就是其中的重要部分,這個(gè)機(jī)制就不在這篇文章里細(xì)說了。

先看看它們倆個(gè)有什么用

mChangedScrap與RecyclerView分離的ViewHolder列表
mAttachedScrap未與RecyclerView分離的ViewHolder列表

簡(jiǎn)單來說當(dāng)holder有了變化就會(huì)放入mChangedScrap,這樣刷新的時(shí)候會(huì)移除重新bind一下;

而holder沒有改變則放入mAttachedScrap,這樣刷新的時(shí)候就不需要重新bind,直接更新數(shù)據(jù)即可。

所以正是因?yàn)闆]有payload需要重新bind,所以會(huì)出現(xiàn)閃爍。而在滑動(dòng)中不僅位置一直變,因?yàn)檫M(jìn)度也在變,所以不停的進(jìn)行移除bind,就會(huì)導(dǎo)致重影的現(xiàn)象。

而使用了payload后,不會(huì)移除重新bind,只更新進(jìn)度條自己,就不會(huì)閃爍或重影了。

payload的大用處

最后再補(bǔ)充一個(gè)重要的部分!

payload的應(yīng)用不僅僅是這么簡(jiǎn)單,在研究的過程中我還發(fā)現(xiàn)了另外一個(gè)函數(shù)

public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) {this.onBindViewHolder(holder, position); }

很熟悉吧,這是RecyclerView的Adapter中的一個(gè)函數(shù),我們一般使用

public abstract void onBindViewHolder(@NonNull VH var1, int var2);

因?yàn)樯厦婺莻€(gè)重載的函數(shù)不是abstract的,所以我們不容易注意到。那么這個(gè)函數(shù)有什么用?

可以看到默認(rèn)處理就是調(diào)用了下面的函數(shù),沒什么特殊,但是我們可以重寫它。

比如說我們刷新的時(shí)候,只想改變一個(gè)TextView的文案

如果是之前的處理,會(huì)重新執(zhí)行一遍onBindViewHolder(@NonNull VH var1, int var2),這樣不僅那個(gè)TextView,其它組件也會(huì)更新一遍數(shù)據(jù),雖然數(shù)據(jù)沒變,尤其有圖片的時(shí)候需要重新load一次。

但是重寫onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads)我們就可以只為TextView重新設(shè)置文案即可,如下:

@Override public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {if(payloads.isEmpty()){onBindViewHolder(holder, position);}else{holder.tv.setText("change text");} }

而且通過對(duì)payload設(shè)置不同的值,我們可以通過判斷payload分別處理不同的刷新,比如:

總結(jié)

總結(jié)

以上是生活随笔為你收集整理的RecyclerView局部刷新机制——payload的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。