在RecyclerView上使用StaggeredGridLayoutManager佈局管理器很容易實現瀑布流佈局。瀑布流佈局比線性佈局和網格佈局美觀,手機屏幕空間利用率高,可是實現方式也比它們複雜,並且常常會有一些莫名奇怪的bug會在瀑布流佈局上出現,線性和網格則不會。java
原本將重點介紹瀑布流兩個臭名昭著的bug。經過深刻探索瀑布流佈局的實現原理,分析它們的造成緣由,並給出優雅解決它們的解決方案。git
bug復現操做:github
bug描述數組
當滑動到頂部時,頂部出現空白,鬆手時瀑布流發生重排序,而且觸發了動畫。緩存
在實際的項目中,咱們不太但願用戶看到這種明顯的動畫,甚至不想用戶看到瀑布流頂部出現空白。markdown
bug復現操做同bug1框架
bug描述oop
「1漢皇重色...」和「5雲鬢花顏...」左側沒有對齊。整個瀑布流佈局有不少這樣的間距錯亂問題。佈局
網上的解決方案以下: 性能
該方法可以解決問題,可是比較耗性能。由於,它無論會不會出現空白狀況,都會清空mLazySpanLookup,而且從新佈局。
個人解決方案是,經過反射StaggeredGridLayoutManager的checkForGaps()。在滑動時,僅在有須要的狀況下才會從新佈局。
效果以下:一樣的操做,當滑到頂部時,空白問題解決了。可是bug2的問題仍然存在
本案例中,瀑布流每一個Item之間的間隔是10dp。屏幕兩側與Item之間的間隔也是10dp。我是經過ItemDecoration來實現的。
解決方案以下:
完美解決間距錯亂問題。
在本案例中,瀑布流共四列。屏幕被分割成四個Span。每一個Span對應的下標分別是0、一、二、3。而左右間距正是根據下標變化的。若是下標不正確,那麼ItemView的間距也會計算錯誤。好比說「1漢皇重色...」的span原本應該是0,若是變成了1,那麼它的左邊距和右邊距就是5dp。而正確的應該是左間距10dp,右間距5dp。
我分別在getItemOffsets()和animateMove()方法中打印spanIndex
zijiexiaozhan getItemOffsets 1漢皇重色思傾國,御宇多年求不得 index 1
zijiexiaozhan animate 1漢皇重色思傾國,御宇多年求不得 index 0
咱們知道「1漢皇重色思傾國」的正確的spanIndex是0。而在getItemOffsets中倒是1,在作動畫的時候又變成了正確的0。緣由可能有兩種
由圖咱們能夠看到設置spanIndex會在getItemOffsets方法前面調用,因此排除第一種可能性
在getItemOffsets中打斷點,得到調用棧以下
//RecyclerView.java 緣由是lp.mInsetsDirty在dispatchLayoutStep1階段被設置成true了。致使在dispatchLayoutStep2真正佈局階段,不會調用到getItemOffsets方法。從而致使缺失了一次更正Decoration繪製的機會。關於RecyclerView動畫原理,請查閱RecyclerView佈局和動畫原理一文。
解決方法就顯而易見了,在checkForGaps返回true後把lp.mInsetsDirty設成false。反射調用RecyclerView的markItemDecorInsetsDirty()
RecyclerView是Android UI框架中一個很是重要的組件。它使用簡單,上手快。可是它的高級使用,一旦遇到問題,那就會變成一件很是棘手的事情。由於我專門寫了一系列關於RecyclerView高級進階的文章。包含了佈局原理、動畫原理、滑動原理、緩存實現機制、實戰踩坑填坑等方面。相信你學習完必定會有收貨。
本文案例已上傳到github。關注我回復「瀑布流」,獲取完整教程。