您好,登錄后才能下訂單哦!
這篇文章主要講解了“如何使用RecyclerView實現瀑布流高度自適應”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何使用RecyclerView實現瀑布流高度自適應”吧!
背景:使用時在RecyclerView外嵌套了自定義的ScrollView,需要讓RecyclerView高度自適應,由于是瀑布流格式網上找了好多方法都無法實現或是動態計算的高度不準確。估計大家都知道recyclerview 內容的高度不是 recyclerview 控制的而是由LayoutManager 來設置的。下面我來說下我的解決方案吧:
布局中的使用
<android.support.v7.widget.RecyclerView android:id="@+id/rcv_indexfragment_article_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/dimen12" android:padding="@dimen/dimen4" android:background="@color/bg_white"/>
個人認為最簡單有效的方法,解決問題最終選用的方法。
官網博客翻譯資料:
RecyclerView 控件提供了靈活一種創建列表和網格的基本方案,而且還支持動畫。這個版本為 LayoutManager API帶來了一個非常激動人心的新特性:自動測量!讓RecyclerView可以根據其內容的大小調整自己。這意味著以前那些無解的場景,比如對RecyclerView的一個dimension 使用WRAP_CONTENT成為了可能。你會發現所有的內置LayoutManager現在都支持自動測量。
因為這個變化的原因,你得仔細檢查 item view的 layout parameters 了:以前會被自動忽略的 layout parameters(比如在滾動方向上的MATCH_PARENT ),現在會被完全考慮進去。如果你有一個自定義的LayoutManager,且沒有繼承自內置的LayoutManager,那么這就是一個可選的API - 你需要調用setAutoMeasureEnabled(true) 并且根據這個方法的Javadoc中的描述做一些微小的改變。
注意,雖然RecyclerView可以讓自己的子View動畫,但是它不能動畫自己的邊界改變。如果你想要讓RecyclerView的邊界變化也呈現動畫,你可以使用 Transition API。
配置的版本:
compile 'com.android.support:recyclerview-v7:23.2.1'
想要內容隨高度變化需設置:(注意:此方式是Android Support Library 23.2中引入的,如果配置之前的版本會報錯哦~)
layoutManager.setAutoMeasureEnabled(true);
Activity中的使用:
recyclerview= ViewFindUtils.find(view, R.id.recyclerview); StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL); layoutManager.setAutoMeasureEnabled(true); recyclerview.setLayoutManager(layoutManager); recyclerview.setHasFixedSize(true); recyclerview.setNestedScrollingEnabled(false); SpacesItemDecoration decoration = new SpacesItemDecoration(6); recyclerview.addItemDecoration(decoration);
自定義CustomStaggeredGridLayoutManager類繼承自StaggeredGridLayoutManager。(注:此方法用到我的項目中計算的高度不準確,列表后面總有一段空白,所以我放棄了~不過你們可以試試,可能是我布局嵌套的問題)
配置的版本(建議使用最新的版本):
compile 'com.android.support:recyclerview-v7:23.1.1'
Activity中的使用:
recyclerview= ViewFindUtils.find(view, R.id.recyclerview); CustomStaggeredGridLayoutManager layoutManager = new CustomStaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL); recyclerview.setLayoutManager(layoutManager); recyclerview.setHasFixedSize(true); recyclerview.setNestedScrollingEnabled(false); SpacesItemDecoration decoration = new SpacesItemDecoration(6); recyclerview.addItemDecoration(decoration);
SpacesItemDecoration.class
public class SpacesItemDecoration extends RecyclerView.ItemDecoration { private int space; public SpacesItemDecoration(int space) { this.space = space; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.top = space; outRect.bottom = space; outRect.left = space; outRect.right = space; } }
CustomStaggeredGridLayoutManager.class
package com.sunny.demo.widget; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import android.view.View; import android.view.ViewGroup; import com.parkmecn.ehc.utils.LogUtil; /** * 解決ScrollView嵌套RecyclerView時RecyclerView需要高度自適應的問題 */ public class CustomStaggeredGridLayoutManager extends StaggeredGridLayoutManager { public CustomStaggeredGridLayoutManager(int spanCount, int orientation) { super(spanCount, orientation); } // 尺寸的數組,[0]是寬,[1]是高 private int[] measuredDimension = new int[2]; // 用來比較同行/列那個item罪寬/高 private int[] dimension; @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { // 寬的mode+size final int widthMode = View.MeasureSpec.getMode(widthSpec); final int widthSize = View.MeasureSpec.getSize(widthSpec); // 高的mode + size final int heightMode = View.MeasureSpec.getMode(heightSpec); final int heightSize = View.MeasureSpec.getSize(heightSpec); // 自身寬高的初始值 int width = 0; int height = 0; // item的數目 int count = getItemCount(); // item的列數 int span = getSpanCount(); // 根據行數或列數來創建數組 dimension = new int[span]; for (int i = 0; i < count; i++) { measureScrapChild(recycler, i, View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), View .MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED), measuredDimension); // 如果是豎直的列表,計算item的高,否則計算寬度 // LogUtil.d("LISTENER", "position " + i + " height = " + measuredDimension[1]); if (getOrientation() == VERTICAL) { dimension[findMinIndex(dimension)] += measuredDimension[1]; } else { dimension[findMinIndex(dimension)] += measuredDimension[0]; } } if (getOrientation() == VERTICAL) { height = findMax(dimension); } else { width = findMax(dimension); } switch (widthMode) { // 當控件寬是match_parent時,寬度就是父控件的寬度 case View.MeasureSpec.EXACTLY: width = widthSize; break; case View.MeasureSpec.AT_MOST: break; case View.MeasureSpec.UNSPECIFIED: break; } switch (heightMode) { // 當控件高是match_parent時,高度就是父控件的高度 case View.MeasureSpec.EXACTLY: height = heightSize; break; case View.MeasureSpec.AT_MOST: break; case View.MeasureSpec.UNSPECIFIED: break; } // 設置測量尺寸 setMeasuredDimension(width, height); LogUtil.e("setMeasuredDimension(width, height)--->height==" + height); } private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec, int heightSpec, int[] measuredDimension) { // 挨個遍歷所有item if (position < getItemCount()) { try { View view = recycler.getViewForPosition(position);//fix 動態添加時報IndexOutOfBoundsException if (view != null) { RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) view.getLayoutParams(); int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, getPaddingLeft() + getPaddingRight (), lp.width); LogUtil.e(position + "--->heightSpec=" + heightSpec + ";getPaddingTop()=" + getPaddingTop() + ";" + "getPaddingBottom()" + getPaddingBottom() + ";lp.height=" + lp.height); int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, getPaddingTop() + getPaddingBottom(), lp.height); LogUtil.e(position + "--->viewchildHeightSpec=" + childHeightSpec); // 子view進行測量,然后可以通過getMeasuredWidth()獲得測量的寬,高類似 view.measure(childWidthSpec, childHeightSpec); // 將item的寬高放入數組中 measuredDimension[0] = view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; //FIXME 此處計算的高度總比實際高度要高一些,導致最后RecycerView的高度計算不對最后留有一段空白,暫時沒有找到問題所在,待大神的解決啊~ measuredDimension[1] = view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; LogUtil.e(position + "--->view.getMeasuredHeight()=" + view.getMeasuredHeight() + ";lp" + ".topMargin=" + lp.topMargin + ";lp.bottomMargin=" + lp.bottomMargin); recycler.recycleView(view); } } catch (Exception e) { e.printStackTrace(); } } } private int findMax(int[] array) { int max = array[0]; for (int value : array) { if (value > max) { max = value; } } return max; } /** * 得到最數組中最小元素的下標 * * @param array * @return */ private int findMinIndex(int[] array) { int index = 0; int min = array[0]; for (int i = 0; i < array.length; i++) { if (array[i] < min) { min = array[i]; index = i; } } return index; } }
感謝各位的閱讀,以上就是“如何使用RecyclerView實現瀑布流高度自適應”的內容了,經過本文的學習后,相信大家對如何使用RecyclerView實現瀑布流高度自適應這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。