1. 程式人生 > >Android RecyclerView局部刷新那個坑

Android RecyclerView局部刷新那個坑

原因 ide color () lda 也會 容易 參考 blog

關鍵:public final void notifyItemChanged(int position, Object payload)

RecyclerView局部刷新大家都遇到過,有時候還說會遇見圖片閃爍的問題。

優化之前的效果:

技術分享圖片

優化之後的效果:

技術分享圖片

如果想單獨更新一個item,我們通常會這樣做,代碼如下:

mLRecyclerViewAdapter.notifyItemChanged(position);

這裏的position就是那個列表項的索引,調用這個方法可以更新一個Item的UI(當然,你要是直接調用notifyDataSetChanged()方法也可以,但這樣會造成其他不需要更新的item也會刷新)。

即便如此,圖片閃爍還是出現了,什麽原因引起來的呢,這裏猜測可以有如下幾個原因:

  1. 流傳甚為廣泛的一種說法,imageView的寬高不固定導致的(wrap_content)?

  2. 是RecyclerView自帶的更新動畫效果導致的?

  3. 是因為圖片加載框架(glide 的 animte)的動畫效果導致的?

  4. getView中(RecyclerView中是onBindViewHolder)加載圖片的時候,設置一個tag,當發現這個imageView的tag和之前的tag一致時就不加載。

這裏我們不再對上面的原因進行具體的分析,針對上面可能引起閃爍的原因進行一一驗證後的結果是令人感到失望的:都不是引起圖片閃爍的根本原因。

那麽怎麽解決這個圖片閃爍的問題呢?通過查看api,我們發現了另一個方法:

技術分享圖片

重點看payload參數介紹:

payload Optional parameter, use null to identify a "full" update

翻譯過來就是如果payload參數是null,那麽就會來一個“完整的”更新,也就是說會全部更新。

我們再看一下mLRecyclerViewAdapter.notifyItemChanged(position)的源碼:

技術分享圖片

從源碼中看到,notifyItemChanged(position)調用了 notifyItemRangeChanged(int positionStart, int itemCount)

方法,源碼如下:

技術分享圖片

notifyItemRangeChanged(int positionStart, int itemCount)方法最終還是調用了notifyItemRangeChanged(int positionStart, int itemCount, Object payload)方法,只是payload參數是null。

那麽如果payload傳一個不為null的參數,就可以實現對列表項中的具體控件更新了嗎?我們通過代碼驗證下。

模擬更新一條數據:

技術分享圖片

這裏,我們將payload參數賦值為”jdsjlzx”,當然你也可以賦值為其他值,只要不空就行。

重寫adapter中的onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads)方法:

技術分享圖片

如果payloads列表不是空的,如上圖所示,你就可以在else代碼塊裏面刷新你想更新的控件了(記得不需要更新的控件就不要寫在這裏了)。

註意:

以上代碼都是結合LRecyclerView框架來測試的,想要體驗完整demo,請參考:https://github.com/jdsjlzx/LRecyclerView

總結

由此看來,RecyclerView做局部刷新還是非常容易的,其實就是使用好帶payload參數的這個notifyItemChanged方法,以及重寫帶payload的這個onBindViewHolder方法,在onBindViewHolder中去刷新你想更新的控件即可。

PS:

拿朋友圈來說,我發一張照片,這就是一個item,但這個item裏還要加上贊和評論。
當我有評論和贊要刷新時,我需要判斷當前要改動的item是否是屏幕中的可見位置。如果是,通過調用帶payload參數的這個notifyItemChanged方法更新item,就能達到只刷贊或者只刷評論,而不用重新加載照片(也就是圖片閃爍的原因)的效果。

怎麽判斷當前position是位於屏幕中呢?下面給出參考代碼:

private void doAnim(int position) {
        int firstItemPosition = layoutManager.findFirstVisibleItemPosition();
        if (position - firstItemPosition >= 0) {
            //得到要更新的item的view
            View view = mRecyclerView.getChildAt(position - firstItemPosition + 1);
            if (null != mRecyclerView.getChildViewHolder(view)) {
                ProductsViewHolder viewHolder = (ProductsViewHolder) mRecyclerView.getChildViewHolder(view);

                //do something

            }

        }

    }

上面代碼同時也獲取到了ViewHolder視圖,有了ViewHolder,你還可以做其他操作哦(比如item動畫效果)。

Android RecyclerView局部刷新那個坑