RecyclerView瀑布流優化方案探討


目錄介紹

  • 01.規則瀑布流實現
  • 02.不規則瀑布流實現
    • 2.1 實現方式
    • 2.2 遇到問題
  • 03.瀑布流上拉加載
  • 04.給瀑布流設置分割線
  • 05.自定義Manager崩潰
  • 06.如何避免刷新抖動
  • 07.為何有時出現跳動
  • 08.瀑布流圖片優化
  • 09.onBindViewHolder優化
  • 10.瀑布流item點擊事件優化
  • 11.Glide加載優化
  • 12.建議指定圖片的寬高

歡迎同行探討瀑布流極致優化方案

  • 如果同行看到這篇文章,有好的瀑布流優化方案,歡迎給出建議,或者給鏈接也可以。
  • 需求:
    • 瀑布流大概有10來中不同type的item視圖,然后視圖是根據動態設置寬高,服務器會返回比例
    • 瀑布流中item需要切割圓角
    • 目前使用glide加載圖片
  • 產品說讓參考抖音快手類的app,讓瀑布流滑動效果特別流暢……但目前遇到問題是滑動十幾頁沒什么問題,但是滑動三四十頁的時候會出現卡頓。歡迎同行給出建議!

好消息

  • 博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護並且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請注明出處,謝謝!
  • 鏈接地址:https://github.com/yangchong211/YCBlogs
  • 如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起於忽微,量變引起質變!

01.規則瀑布流實現

  • 最簡單規則瀑布流實現,下面這種是設置3列數據,然后組數據高度是相同的。是規則的瀑布流。

02.不規則瀑布流實現

  • 最簡單的不規則瀑布流實現,下面這種是設置2列數據,然后數據的高度都不同,圖片的高度隨機。
  • 這里是偽代碼。假設設置不同高度,代碼如下。簡單設置不同圖片高度不同,這個是在onBindViewHolder中操作。

2.2 遇到問題

  • 遇到問題
    • 1.RecyclerView 如何實現下拉加載更多;
    • 2.StaggeredGridLayoutManager 顯示加載更多的時候,加載更多作為最后一個item沒有單獨占滿屏幕寬度,只顯示為一個item的寬度;
    • 3.StaggeredGridLayoutManager 如何隨機設置item的高度;
    • 4.StaggeredGridLayoutManager 上拉加載數據刷新UI時,由於高度隨機,造成頁面item抖動問題;
    • 5.RecyclerView莫名的Inconsistency detected崩潰;
  • 卡頓和內存釋放
    • 瀑布流滑動幾頁感覺還行,但是一旦滑動了三四十頁,就感覺頁面有些卡頓了。

03.瀑布流上拉加載

  • 首先添加監聽方法,添加了這個方法就可以上拉加載更多數據呢。但是發現還有點問題,上拉加載更多的那個布局只是占1/spanCount列,這樣顯得特別難看。那么該如何處理呢,接着往下看……
    • 首先要能夠監聽recyclerView滑動事件;
    • 判斷recyclerView是否滑動到最后一個item;
    • recyclerView 加載更多RecyclerView.Adapter的設置處理:RecyclerView.Adapter的相關代碼,主要定義兩個ViewHolder類型,footType表示為底部的viewHolder,normalType表示為正常的item的viewHolder,根據position的不同來顯示不同的viewholder;
  • 因為是瀑布流,要設置footerView占據一行,這個需要這樣設置,代碼如下所示

04.給瀑布流設置分割線

  • 先來看看出現錯位,分割線出現問題的代碼。下面這種方式根據childCount來判斷奇數和偶數設置的不同間距。
    • 那么比如說當為奇數時,設置該item到左為20,到右為5;當為偶數時,該item到左為5,到右為20。
    • 如果奇數的item都在左邊,偶數的item都在右邊,那么間距就沒有問題。
    • 如果第一個item在左邊【高度很高】,第2個,第3個,第4個item都在右邊,第5個在左邊……那么思考一下,這個時候第3個item在右邊,那么就會造成間距不規則。
    • 顯然不能根據奇數或者偶數來設置item左右間距的大小的,會出現錯位。
  • 解決辦法,可以通過StaggeredGridLayoutManager.LayoutParams里的getSpanIndex()來判斷,這個方法不管你高度怎樣,他都是左右左右開始排列的。

05.自定義Manager崩潰

  • RecyclerView莫名的Inconsistency detected崩潰;
    • 出現這個異常原因:
      • 使用RecyclerView加官方下拉刷新的時候,如果綁定的List對象在更新數據之前進行了clear,而這時用戶緊接着迅速上滑RV,就會造成崩潰,而且異常不會報到你的代碼上,屬於RV內部錯誤。
    • 自定義一個CustomStaggeredGridLayoutManager 在onLayoutChildren對異常進行捕獲:
  • 關於StaggeredGridLayoutManager異常說明

06.如何避免刷新抖動

  • StaggeredGridLayoutManager 上拉加載數據刷新UI時,由於高度隨機,造成頁面item抖動問題; 這里由於直接調用 notifyDataSetChanged();那么是全局刷新,而刷新的時候item的高度重新隨機分配,導致數據刷新的時候會造成抖動。建議采用notifyItemRangeChanged進行局部刷新:

07.為何有時出現跳動

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

08.瀑布流圖片優化

  • 具體優化方案
    • 第一步:減少布局嵌套,並且在拿到服務器的尺寸后,在onBindViewHolder中給圖片控件設置寬高時,避免創建大量臨時的LayoutParams對象
    • 第二步:使用glide加載,綁定activity或者fragment的生命周期,盡量不用用全局上下文或者靜態上下文。注意with()方法中傳入的實例會決定Glide加載圖片的生命周期,如果傳入的是Activity或者Fragment的實例,那么當這個Activity或Fragment被銷毀的時候,圖片加載也會停止。如果傳入的是ApplicationContext,那么只有當應用程序被殺掉的時候,圖片加載才會停止。
    • 第三步:對於list條目,尤其是瀑布流,不建議使用TransitionOptions來加載設置的動畫,尤其是不要使用自己自定義的動畫
  • 避免使用圓角的ImageView
    • 在實際項目內,經常會用到一些帶圓角的圖片,或者直接就是圓形的圖片。圓形的圖片,多數用於一些用戶的頭像之類的顯示效果。
    • 而在 Android 下,也有大量的類似 XxxImageView 的開源控件,用於操作 Bitmap 以達到一個圓角圖片的效果,例如 Github 上比較火的 RoundedImageView。
    • 它們大部分的原理,是接收到你傳遞的 Bitmap ,然后再輸出一個與原來 Bitmap 等大的新 Bitmap ,在此基礎之上,進行圓角的一些處理,這就導致了,實際上會在內存中,多持有一個 Bitmap ,一下一張圖片占用的內存就被加倍了。
    • 所以既然已經選擇使用Glide,推薦使用glide-transformations這個開源庫配合使用,glide-transformations 利用 Glide 的 bitmapTransfrom() 接口,實現對加載的 Bitmap 的進行一些變換操作。glide-transformations提供一系類對加載的圖片的變換操作,從形狀變換到色彩變換,全部支持,基本上滿足大部分開發需要,並且它會復用 Glide 的 BitmapPool ,來達到節約內存的目的。

09.onBindViewHolder優化

  • 在這個方法中,主要是做數據綁定到View視圖。由於瀑布流會有多種不同type類型的視圖,有些需要設置寬高,有的則要從服務器拿到寬高數據然后動態修改視圖屬性。因此關於寬高的計算其實還可以做一些優化。
  • 先來看一下最初的代碼,這里只展示動態設置寬高的代碼。如下所示
    • 看了下面代碼,會發現這些問題。第一,頻繁計算瀑布流中不同類型的寬度,該寬度是屏幕寬度減去間距,然后是除以2,瀑布流為兩行。第二,頻繁創建LayoutParams對象,然后設置寬高的屬性。
  • 然后看一下優化后的代碼。
    • 那么下面這種代碼,就可以極大減少頻繁的動態計算寬高等屬性。通過imageView34.getLayoutParams()方式獲取layoutParams,就可以避免上面那種通過new創建大量的對象。要知道,平時像上面代碼那么用也沒問題,但是在recyclerView瀑布流中,也可以細微優化一下。

10.瀑布流item點擊事件優化

  • 關於rv設置item條目點擊事件有兩種方式:1.在onCreateViewHolder中寫;2.在onBindViewHolder中寫;3.在ViewHolder中寫。那么究竟是哪一種好呢?
    • 1.在onCreateViewHolder中寫
    • 2.在onBindViewHolder中寫
    • 3.在ViewHolder中寫
  • onBindViewHolder() 中頻繁創建新的 onClickListener 實例沒有必要,建議實際開發中應該在 onCreateViewHolder() 中每次為新建的 View 設置一次就行。

11.Glide加載優化

  • glide優化方案代碼,滑動時候禁止加載圖片,停止滑動后開始恢復加載圖片。
  • 綁定控件的生命周期
    • 當一個界面離開之后,我們更希望當前的圖片取消加載,那么 Glide 是怎么做到的呢?當在recyclerView列表中,item從可見滑動到不可見的時候,如何控制圖片請求的生命周期,那么可以與控件生命周期相綁定。
  • 低內存的情況下主動清除緩存,看最新版本glide,其實源碼中以及處理了下面相關的邏輯。

12.建議指定圖片的寬高

  • Glide設置圖片控件wrap_content不建議支持的問題
    • 官方說,不支持並且不建議imageview設置wrap_content。因為這樣glide不知道要加載多大的圖片給我們才好,在他的接口(Sizes and dimensions)中也有體現。普通的imageview其實也還好,如果放在列表(RecyclerView)中, 由於我們並不知道目標圖片大小是多大的,所以我們選擇了wrap_content,那么在上下來回滾動過程中,就會導致圖片一會大一會小的bug.
  • 官方 issue 作者回答如下:
    • 所以,如果可以,那么還是指定圖片控件的寬高吧!

01.關於博客匯總鏈接

02.關於我的博客

業余demo鏈接:https://github.com/yangchong211/YCRefreshView


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM