溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何使用RecyclerView實現(xiàn)瀑布流高度自適應(yīng)

發(fā)布時間:2021-09-15 20:03:14 來源:億速云 閱讀:174 作者:chen 欄目:開發(fā)技術(shù)

這篇文章主要講解了“如何使用RecyclerView實現(xiàn)瀑布流高度自適應(yīng)”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何使用RecyclerView實現(xiàn)瀑布流高度自適應(yīng)”吧!

背景:使用時在RecyclerView外嵌套了自定義的ScrollView,需要讓RecyclerView高度自適應(yīng),由于是瀑布流格式網(wǎng)上找了好多方法都無法實現(xiàn)或是動態(tài)計算的高度不準確。估計大家都知道recyclerview 內(nèi)容的高度不是 recyclerview 控制的而是由LayoutManager 來設(shè)置的。下面我來說下我的解決方案吧:

布局中的使用

<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"/>

方法一

個人認為最簡單有效的方法,解決問題最終選用的方法。

官網(wǎng)博客翻譯資料:

RecyclerView 控件提供了靈活一種創(chuàng)建列表和網(wǎng)格的基本方案,而且還支持動畫。這個版本為 LayoutManager API帶來了一個非常激動人心的新特性:自動測量!讓RecyclerView可以根據(jù)其內(nèi)容的大小調(diào)整自己。這意味著以前那些無解的場景,比如對RecyclerView的一個dimension 使用WRAP_CONTENT成為了可能。你會發(fā)現(xiàn)所有的內(nèi)置LayoutManager現(xiàn)在都支持自動測量。
因為這個變化的原因,你得仔細檢查 item view的 layout parameters 了:以前會被自動忽略的 layout parameters(比如在滾動方向上的MATCH_PARENT ),現(xiàn)在會被完全考慮進去。如果你有一個自定義的LayoutManager,且沒有繼承自內(nèi)置的LayoutManager,那么這就是一個可選的API - 你需要調(diào)用setAutoMeasureEnabled(true) 并且根據(jù)這個方法的Javadoc中的描述做一些微小的改變。
注意,雖然RecyclerView可以讓自己的子View動畫,但是它不能動畫自己的邊界改變。如果你想要讓RecyclerView的邊界變化也呈現(xiàn)動畫,你可以使用 Transition API。

配置的版本:

compile 'com.android.support:recyclerview-v7:23.2.1'

想要內(nèi)容隨高度變化需設(shè)置:(注意:此方式是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需要高度自適應(yīng)的問題
 */
public class CustomStaggeredGridLayoutManager extends StaggeredGridLayoutManager {
    public CustomStaggeredGridLayoutManager(int spanCount, int orientation) {
        super(spanCount, orientation);
    }

    // 尺寸的數(shù)組,[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的數(shù)目
        int count = getItemCount();
        // item的列數(shù)
        int span = getSpanCount();
        // 根據(jù)行數(shù)或列數(shù)來創(chuàng)建數(shù)組
        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;
        }
        // 設(shè)置測量尺寸
        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 動態(tài)添加時報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的寬高放入數(shù)組中
                    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;
    }

    /**
     * 得到最數(shù)組中最小元素的下標
     *
     * @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實現(xiàn)瀑布流高度自適應(yīng)”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對如何使用RecyclerView實現(xiàn)瀑布流高度自適應(yīng)這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI