成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Android中RecyclerView復(fù)用錯(cuò)亂的示例分析

這篇文章給大家分享的是有關(guān)Android中RecyclerView復(fù)用錯(cuò)亂的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來看看吧。

創(chuàng)新互聯(lián)專注于武進(jìn)企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城系統(tǒng)網(wǎng)站開發(fā)。武進(jìn)網(wǎng)站建設(shè)公司,為武進(jìn)等地區(qū)提供建站服務(wù)。全流程按需規(guī)劃網(wǎng)站,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)

復(fù)用錯(cuò)亂的解決辦法

本文的前半部分很簡單的,以為關(guān)于復(fù)用錯(cuò)亂,RecyclerView 已經(jīng)有他的前輩 ListView 替它踩了很多坑了。雖然他們的復(fù)用邏輯是有差異的,例如 ListView 只有兩層緩存,但是 RecyclerView 可以理解為有四層;ListView 緩存的單位是 view,而 RecyclerView 緩存的單位是 ViewHolder。但是不管他們復(fù)用邏輯的差異如何,終歸都是把那個(gè)緩存起來的 view 拿過來接著用,所以解決復(fù)用錯(cuò)亂的方法是一樣的。

RecyclerView 復(fù)用導(dǎo)致錯(cuò)亂的原因其實(shí)就是拿出來之前的 View 來添加到新 item 上,之前 View 的狀態(tài)一直保留著,所以也就錯(cuò)亂了。不過解決起來很簡單:

首先我們以 adapter 數(shù)據(jù)的來源分為兩大類:

1.當(dāng)數(shù)據(jù)來源是同步的

這種情況是最簡單的,你就保證當(dāng) onBindViewHolder 方法調(diào)用的時(shí)候,你的 itemview 中每個(gè) view 的狀態(tài)都有一個(gè)默認(rèn)值。這是什么意思呢?

if ("<unknown>".equals(artists)) {
      holder.cbMusicState.setChecked(true);
    } else {
      holder.cbMusicState.setChecked(false);
    }

假設(shè)我們的 holder 里面有個(gè) Checkbox 控件,當(dāng)歌手名為 unknown 時(shí),Checkbox 勾選。注意個(gè)時(shí)候你一定要加上這個(gè) else 條件,才能保證復(fù)用這個(gè) ViewHolder 的時(shí)候,Checkbox 的狀態(tài)不出錯(cuò)。任何控件都一樣,總結(jié)起來就是你要給每個(gè)控件的狀態(tài)賦一個(gè)新的值,替換掉之前的,這樣自然不會(huì)出現(xiàn)什么復(fù)用錯(cuò)亂的問題。

2.當(dāng)數(shù)據(jù)的來源是異步的

這種情況也很常見,我們舉個(gè)栗子,比如你的 ItemView 里面有個(gè) ImageView,每次 onBindViewHolder 的時(shí)候,你傳入一個(gè) url,等待服務(wù)器返回的結(jié)果,然后展示在 ImageView 上。這種情況會(huì)怎樣導(dǎo)致錯(cuò)亂呢?

是這樣的,假設(shè)我進(jìn)入了頁面,開始為第一個(gè) ImageView 請求圖片,但是此刻我下劃屏幕,劃到了第四個(gè) item,此時(shí)第一個(gè) item 已經(jīng)不可見了,第四個(gè) item 復(fù)用了第一個(gè) item 的 imageview,恰好此刻第一個(gè) imageview 的圖片結(jié)果返回了,就正好展示在了第四個(gè) itemview 上。 這樣就發(fā)生了圖片的錯(cuò)亂。

出現(xiàn)這個(gè)問題的原因就是這個(gè) ImageView 和請求的 url 沒一一綁定,所以按照這個(gè)思路來解決吧:

  holder.ivCameraImages.setBackground(R.drawable.place_holder);
  
  holder.ivCameraImages.setTag(imageURL);

    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if (msg.what == MSG_IMAGE) {
        Bitmap bm = (Bitmap) msg.obj;
        if (bm != null) {
          if (TextUtils.equals((String) imageView.getTag(), imageURL)) {
            imageView.setBackground(new BitmapDrawable(bm));
          }
        }
      }
    }

首先在沒加載圖片之前,給 ImageView 設(shè)置一個(gè)默認(rèn)圖片,然后通過 setTag 方法,將 ImageView 和 圖片的 url 一一對應(yīng)起來,設(shè)置的時(shí)候再判斷一下,這個(gè) imageview 的 tag 和當(dāng)時(shí)請求的 url,是不是一致的,如果是一致的,再保存。

以上就是復(fù)用錯(cuò)亂時(shí)兩種比較通用的解法,基本上可以覆蓋大部分情況。

一個(gè)奇怪的問題

這個(gè)問題的現(xiàn)象是這樣子的:

當(dāng) RecyclerView 的條目很少的時(shí)候,比如只有六個(gè),將 RecyclerView 從上滑動(dòng)到下,這個(gè)時(shí)候是正常的,onBindViewHolder 會(huì)調(diào)用,不過此時(shí)從底部上劃的時(shí)候,上方的 item 從不可見到可見的這個(gè)過程中,onBindViewHolder 并沒有調(diào)用,這個(gè)時(shí)候我也就沒辦法進(jìn)行一些刷新 item 的操作了。

這個(gè)問題的原因是 onBindViewHolder 方法不調(diào)用導(dǎo)致的,我在 StackOverflow 上搜索了很多答案,終于找到了一個(gè)可以解決我的問題的:

recyclerview-not-recycling-views-if-the-view-count-is-small

(中文資料壓根就沒有,所以掌握英文搜索是多么的重要)

你可以調(diào)用

recyclerView.setItemViewCacheSize(int);

這個(gè) api,去調(diào)整 RecyclerView 的復(fù)用邏輯和方式來解決 onBindViewHolder 沒有調(diào)用的這個(gè)問題。

但是原理是怎樣的呢?作為一名好奇心頗重的程序員,一步步 debug RecyclerView 的源代碼,發(fā)現(xiàn)了導(dǎo)致這個(gè)問題的原因,一起來看看吧。

在上一篇文章中,我們分析了 RecyclerView 的源碼,其中復(fù)用邏輯的模塊,有一個(gè)非常重要的核心方法 tryBindViewHolderByDeadline,這個(gè)方法目的就是在 RecyclerView 的層層緩存結(jié)構(gòu)中,取出 ViewHolder。

這里就不再次研究它了,想了解的去看之前的文章,我來描述一下對于這個(gè)場景,簡化之后的邏輯:

當(dāng) RecyclerView 從底部向上滑動(dòng)的時(shí)候,會(huì)先后從 mCachedViews 和 mRecyclerPool 中尋找緩存的 ViewHolder。

mCachedViews 和 mRecyclerPool 之間又有什么關(guān)系呢?

public void setViewCacheSize(int viewCount) {
      mRequestedCacheMax = viewCount;
      updateViewCacheSize();
    }

    void updateViewCacheSize() {
      int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
      mViewCacheMax = mRequestedCacheMax + extraCache;

      // first, try the views that can be recycled
      for (int i = mCachedViews.size() - 1;
          i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
        recycleCachedViewAt(i);
      }
    }

當(dāng)調(diào)用 setViewCacheSize 這個(gè)方法時(shí),相當(dāng)于是給 mViewCacheMax 這個(gè)變量賦值了, for 循環(huán)調(diào)用 recycleCachedViewAt 的作用是將 mCachedViews 中緩存的 ViewHolder 放進(jìn) RecyclerPool 中??梢钥吹?for 循環(huán)的周期是從 mCachedViews 的最后一個(gè)對象直到 mCachedViews.size == mViewCacheMax 這個(gè)值時(shí)。

也就是可以這么理解, setViewCacheSize 這個(gè)方法其實(shí)就是為 mCachedViews 集合設(shè)置所能持有 ViewHolder 的最大數(shù)量。

當(dāng)  setViewCacheSize(0)時(shí),RecyclerView 想去復(fù)用 ViewHolder 時(shí),只能去 RecyclerPool 中去取了,這里就有問題來了,從 RecyclerPool 中取和從 mCachedViews 中取 ViewHolder 中又有什么區(qū)別呢?

if (holder == null) { // fallback to pool
          if (DEBUG) {
            Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
                + position + ") fetching from shared pool");
          }
          holder = getRecycledViewPool().getRecycledView(type);
          if (holder != null) {
            holder.resetInternal();
            if (FORCE_INVALIDATE_DISPLAY_LIST) {
              invalidateDisplayListInt(holder);
            }
          }
        }

當(dāng)從 RecyclerPool 取出 ViewHolder 時(shí),調(diào)用了 resetInternal 這個(gè)函數(shù)的作用是清空一些記錄的參數(shù),包括之前記錄 ViewHolder 狀態(tài)的 mFlags。

 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);
        }
        final int offsetPosition = mAdapterHelper.findPositionOffset(position);
        bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
      }

代碼再往下走的時(shí)候,剛剛清空的 flag 參數(shù)這個(gè)時(shí)候就用到了,holder.isBound() 返回 flase,進(jìn)入 if 判斷,調(diào)用 tryBindViewHolderByDeadline 進(jìn)而調(diào)用了 onBindViewHolder。

到這里這個(gè)邏輯就描述清楚了,所以設(shè)置 setViewCacheSize 來調(diào)整 mCachedViews 保存 ViewHolder 的大小,就能解決問題。

當(dāng)然有些特殊的情況,某些位置就不能調(diào)用 onBindViewHolder,沒關(guān)系,可以監(jiān)聽 RecyclerView 的滑動(dòng),當(dāng)滑動(dòng)停止的時(shí)候,再調(diào)用 notify 刷新下列表也是可以的。

感謝各位的閱讀!關(guān)于“Android中RecyclerView復(fù)用錯(cuò)亂的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

分享題目:Android中RecyclerView復(fù)用錯(cuò)亂的示例分析
本文路徑:http://jinyejixie.com/article16/ggedgg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、云服務(wù)器、標(biāo)簽優(yōu)化企業(yè)建站、網(wǎng)站收錄軟件開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司
旬邑县| 明溪县| 延安市| 佳木斯市| 韩城市| 韩城市| 广水市| 綦江县| 汉源县| 诸城市| 阿合奇县| 阿鲁科尔沁旗| 长丰县| 杂多县| 鹤岗市| 民乐县| 通榆县| 兴安县| 米泉市| 湖南省| 印江| 新邵县| 聂荣县| 原平市| 甘孜县| 博野县| 化德县| 延吉市| 临邑县| 攀枝花市| 旅游| 阳原县| 留坝县| 靖州| 宝兴县| 庄浪县| 罗江县| 平乐县| 博爱县| 岳西县| 哈密市|