1. 程式人生 > >Android瀑布流優化,解決Recyclerview展示大批量圖片時Item自動切換、閃爍、空白等問題...

Android瀑布流優化,解決Recyclerview展示大批量圖片時Item自動切換、閃爍、空白等問題...

code小生,一個專注於 Android 領域的技術分享平臺

作者:請抱抱陳先生
地址:https://www.jianshu.com/p/3a1c9699c190
宣告:本文是 請抱抱陳先生 原創,轉發等請聯絡原作者授權。

本文涉及的程式碼案例可以在下方的連結中找到:

https://github.com/CodeTillDoom/StaggeredRcl

問題分析

這段時間業務需求用到RecyclerView瀑布流載入並展示大批量圖片,但一開始單純使用RecyclerView直接載入圖片,使得顯示上出現了滑動到頂端時閃爍,Item自動切換位置(切換後資料與展示的畫面並不一致),頂端出現空白等等問題,體驗上十分差勁,於是開始了優化之旅。現在把優化過程和方法記錄下來,供有用者參考。

解決方案

① 在網上查閱資料時,有網友提供了一個解決方案

layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)

這種方法確實可以解決滑動到頂端時Item左右切換的問題,但遠遠不夠。載入瀑布流時仍然存在列的跳動、閃爍、頂端有空白等問題,需要進一步優化。

② 為什麼會出現這種列跳動、item閃爍、空白的問題呢?經過分析,應該是由於我們載入的圖片高度不確定(寬度確定因為可以根據螢幕寬度和每行Item數目進行等分),而當我們向RecyclerView下方滑動一段距離後,由於ViewHolder的回收機制,item的尺寸並不確定,滑回到上方時Item需要重新自行繪製,於是這個又導致重繪,所以會有閃爍、跳動、空白等問題。說到底,只要我們在重繪前確定了Item的尺寸,那麼就可以避免Item去重新計算自己的尺寸,就可以避免重繪導致的諸多問題。

這個時候有同學會說了,那我不讓RecyclerView回收不就完了,需要你搞這些七拐八彎的門道嗎?對於這些同學我只能說:OOM瞭解一下。

你腦子有問題.png

既然方案有了,接下來就是開幹。

我們從後臺請求到圖片後,先將其下載下來,再使用一個IntentService,根據Url獲取Bitmap(不要問我怎麼獲取Bitmap,Glide都不會用那你也不用看這篇文章了,也不要問我為什麼要用IntentService,後臺執行懂不懂,用完即棄懂不懂)。

首先成功從後臺拉取到圖片後,啟動IntentService,處理圖片.

ImageService.startService(MainActivity.this
, data, mSubtype);  

處理過程:使用IntentService根據url獲取Bitmap,在子執行緒中處理圖片,用完後Service自行結束,再使用EventBus通知主執行緒說:老哥,我處理完了,你可以展示了。

public class ImageService extends IntentService {  

public DataService() {  
   super("");  
}  

public static void startService(Context context, List datas, String subtype) {  

 Intent intent =new Intent(context, ImageService.class);  

 intent.putParcelableArrayListExtra("data", (ArrayList) datas);  

 intent.putExtra("subtype", subtype);  

 context.startService(intent);  
 }  

@Override
protected void onHandleIntent(Intent intent) {  
   if (intent == null) {  
       return;  
    }  
   List datas = intent.getParcelableArrayListExtra("data");  

   String subtype = intent.getStringExtra("subtype");  

   handleGirlItemData(datas, subtype);  

   }  

private void handleGirlItemData(List datas, String subtype) {  

       if (datas.size() == 0) {  

       EventBus.getDefault().post("finish");  

       return;  
     }  

       for (GirlItemData data : datas) {  

       Bitmap bitmap = ImageLoader.load(this, data.getUrl());  

       if (bitmap != null) {  

               data.setWidth(bitmap.getWidth());  

               data.setHeight(bitmap.getHeight());  

           }  

           data.setSubtype(subtype);  

       }  

       EventBus.getDefault().post(datas);  

   }  
}

處理完再在Adapter中載入:

public class GirlAdapter extends BaseQuickAdapter {  

public GirlAdapter(){  
   super(R.layout.item_girl_layout);  
}  

@Override  

protected void convert(BaseViewHolder helper, GirlItemData item) {  

       ScaleImageView imageView = helper.getView(R.id.girl_item_iv);  

       imageView.setInitSize(item.getWidth(), item.getHeight());  

       ImageLoader.load(BaseApplication.getContext(),  

               item.getUrl(), imageView);  

   }  

public void deleteItem(int position){  

       remove(position);  

       notifyDataSetChanged();  
   }  

}

這個時候我們可以發現:瀑布流確實也不閃爍了,也不突然切換列了,空白現象好像也消失了。

但是還是有不對的地方:瀑布流載入的速度慢了許多。。。這個問題可能比較嚴重了,使用者開啟5s還看到的是一片空白,於是回到桌面把我們app卸了。。。

為什麼會出現這個問題呢?因為在優化以前,我們從後臺得到Json檔案(包括圖片id,url,owner等),瀑布流二話不說就開始載入了,Glide再根據url去下載圖片,下載完一張就在瀑布流中展示出一張,下載之前展示的是佔位圖。

而優化之後呢?比如我們一次性拉取到10張照片的json資料,我們需要完整下載10張圖片,處理完長寬資訊,才能展示出來,這個時間就久了。

所以,這個時候只能給後臺同學提需求了:下放的Json資料需要包含圖片的長寬資訊,這樣我們就不用在客戶端處理了。

2831220228.png

所以,上方的程式碼,適用於後臺同學不給加需求的情況。

③ 最後,我們在測試中發現,在瀑布流中刪除某個Item之後,滑回到首頁仍然有小概率出現頂方存在空白的情況。對於這種問題,只需要給RecyclerView設定監聽,假如刪除過Item且滑回到首頁,就再重新整理一次Adapter。

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {  

@Override  
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  

       super.onScrollStateChanged(recyclerView, newState);  

       }  

@Override  
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {  

       super.onScrolled(recyclerView, dx, dy);  

       if (isItemDeleted){  

       StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) recyclerView.getLayoutManager();  

       int[] firstVisibleItem = null;  

        firstVisibleItem = layoutManager.findFirstVisibleItemPositions(firstVisibleItem);  

       if (firstVisibleItem != null && firstVisibleItem[0] == 0) {  

       if (mAdapter!=null) {  

       isItemDeleted =false;  

       mAdapter.notifyDataSetChanged();  
              }  

       }  
    }  
  }  
});  

基本上以上三個解決方案可以應對瀑布流中Item錯亂的大多數情況了。

優化後的瀑布流還是很穩定的,看小姐姐很得勁.

想看更多程式碼細節可以前往下方連結下載本文原始碼,如果對你有幫助,請支援下