最近有一個奇葩的需要,具體場景是移動端要實現根據服務端配置的數據動態展示不同的佈局,也就是說客戶端界面中的佈局不能寫死,而是要動態添加展示,相信大家要去實現也不復雜,然而奇葩之處是其中能夠動態展示的佈局要包含瀑布流效果的佈局,那麼對於一個界面來說我們是不確定需要加載哪些佈局的,也不知道有多少數據需要展示,那麼我們肯定會使用scrollview作爲根佈局,對於瀑布流效果使用RecyclerView的StaggeredGridLayoutManager佈局管理器就可以很容易實現。
那麼你會發現這樣的話問題就來了,scrollview中使用RecyclerView必然會出現類似於scrollview中使用listview等一樣的各種問題,假如你使用如下代碼實現瀑布流效果,你會發現根本沒有效果,什麼都不會顯示。
View recyclerViewLayout =LayoutInflater.from(context).inflate(R.layout.layout_recycleview, null);
RecyclerView recyclerView =(RecyclerView) recyclerViewLayout.findViewById(R.id.layout_recycle_view);
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
那怎麼辦呢?不要慌。。。咱們只需要重寫StaggeredGridLayoutManager,然後重寫它的onMeasure方法重新測量子View即可,完整的代碼在文章結尾GitHub中可自行下載,這裏貼出核心代碼如下:
@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
......
int[] maxHeight = new int[spanCount];//存儲每一列中各View的height總和
for (int i = 0; i < adapterItemCount; i++) {//循環計算每一列View的總高度
int position = i % spanCount;//spanCount 爲列數
if (i < spanCount) {//第一排的View將height存到各自的maxHeight 中
maxHeight[position] += childColumnDimensions[i];//childColumnDimensions存儲了所有View的height
} else if (position < spanCount) {//第二排開始需要將接下來的View排到高度最小的一列
int mixHeight = maxHeight[0];
int mixPosition = 0;
for (int j = 0; j < spanCount; j++) {
if (mixHeight > maxHeight[j]) {
mixHeight = maxHeight[j];
mixPosition = j;
}
}
maxHeight[mixPosition] += childColumnDimensions[i];
}
}
for (int i = 0; i < spanCount; i++) {//將所得的maxHeight的數據進行排序(降序)
for (int j = 0; j < spanCount - i - 1; j++) {
if (maxHeight[j] < maxHeight[j + 1]) {
int temp = maxHeight[j];
maxHeight[j] = maxHeight[j + 1];
maxHeight[j + 1] = temp;
}
}
}
height = maxHeight[0];//獲取到最後排列好後最大的高度作爲RecyclerView最終的高度
......
}
說明:其中註釋寫的也比較詳細可參考,需要說明的一點時,RecyclerView瀑布流中子View的排列順序是先按順序排好第一排,從第二排起接下來的View先排到第一排中高度最小的View底部,然後依次這麼排列下去,如果有一個子View的高度很高,那麼它底部不會有View排列,直到其他位置的子View的總高度比它高時纔會有子View排在它底部,這個規則在我們計算RecyclerView的子View的總高度時要特別注意,否則就會出現最終RecyclerView子View都正常顯示了,但是底部會出現一塊空白區域的問題。
自定義好了之後使用的話很簡單,直接將StaggeredGridLayoutManager替換爲FullyStaggeredGridLayoutManager就可以了,其他的不需要變。
View recyclerViewLayout =LayoutInflater.from(context).inflate(R.layout.layout_recycleview, null);
RecyclerView recyclerView =(RecyclerView) recyclerViewLayout.findViewById(R.id.layout_recycle_view);
recyclerView.setLayoutManager(new FullyStaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
對於scrollview中嵌套RecyclerView實現如listview效果的我們也可以自定義LinearLayoutManager就能解決,RecyclerView的優勢就是特別靈活,一些炫酷的效果我們也可以直接繼承RecyclerView.LayoutManager來實現,具體的可以根據自己的需要實現。下面是完整代碼的傳送門。