本文涉及的代碼案例能夠在下方的連接中找到,若是對你有幫助,請給個Star(#^.^#)git
https://github.com/CodeTillDoom/StaggeredRclgithub
問題分析json
這段時間業務需求用到RecyclerView瀑布流加載並展現大批量圖片,但一開始單純使用RecyclerView直接加載圖片,使得顯示上出現了滑動到頂端時閃爍,Item自動切換位置(切換後數據與展現的畫面並不一致),頂端出現空白等等問題,體驗上十分差勁,因而開始了優化之旅。如今把優化過程和方法記錄下來,供有用者參考。app
這是優化以前的展現畫面,能夠看到存在諸多問題。ide
解決方案
① 在網上查閱資料時,有網友提供了一個解決方案post
layoutManager.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE)
這種方法確實能夠解決滑動到頂端時Item左右切換的問題,但遠遠不夠。加載瀑布流時仍然存在列的跳動、閃爍、頂端有空白等問題,須要進一步優化。測試
② 爲何會出現這種列跳動、item閃爍、空白的問題呢?通過分析,應該是因爲咱們加載的圖片高度不肯定(寬度肯定由於能夠根據屏幕寬度和每行Item數目進行等分),而當咱們向RecyclerView下方滑動一段距離後,因爲ViewHolder的回收機制,item的尺寸並不肯定,滑回到上方時Item須要從新自行繪製,因而這個又致使重繪,因此會有閃爍、跳動、空白等問題。說到底,只要咱們在重繪前肯定了Item的尺寸,那麼就能夠避免Item去從新計算本身的尺寸,就能夠避免重繪致使的諸多問題。優化
這個時候有同窗會說了,那我不讓RecyclerView回收不就完了,須要你搞這些七拐八彎的門道嗎?對於這些同窗我只能說:OOM瞭解一下。ui
既然方案有了,接下來就是開幹。this
咱們從後臺請求到圖片後,先將其下載下來,再使用一個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<GirlItemData> datas, String subtype) { Intent intent = new Intent(context, ImageService.class); intent.putParcelableArrayListExtra("data", (ArrayList<? extends Parcelable>) datas); intent.putExtra("subtype", subtype); context.startService(intent); } @Override protected void onHandleIntent(Intent intent) { if (intent == null) { return; } List<GirlItemData> datas = intent.getParcelableArrayListExtra("data"); String subtype = intent.getStringExtra("subtype"); handleGirlItemData(datas, subtype); } private void handleGirlItemData(List<GirlItemData> 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<GirlItemData, BaseViewHolder> { 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數據須要包含圖片的長寬信息,這樣咱們就不用在客戶端處理了。
因此,上方的代碼,適用於後臺同窗不給加需求的狀況
③ 最後,咱們在測試中發現,在瀑布流中刪除某個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錯亂的大多數狀況了。
優化後的瀑布流仍是很穩定的,看小姐姐很得勁:
想看更多好看的小姐姐能夠前往下方連接下載本文源碼,有幫助請給個Star(#^.^#)