使用RecyclerView实现瀑布流高度自适应

 更新时间:2021年09月15日 16:32:34   作者:SmilingGirl_  
这篇文章主要为大家详细介绍了使用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;
    }


}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Android全面屏适配与判断超详细讲解

    Android全面屏适配与判断超详细讲解

    这篇文章主要介绍了Android 全面屏适配及判断是否为全面屏,全面屏手势和虚拟导航栏的判断,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-01-01
  • Android实现价格走势自定义曲线图

    Android实现价格走势自定义曲线图

    本篇文章主要介绍了Android实现价格走势曲线图,非常具有实用价值,需要的朋友可以参考下
    2017-04-04
  • Android开发中的数据库事务用法分析

    Android开发中的数据库事务用法分析

    这篇文章主要介绍了Android开发中的数据库事务用法,分析了Android数据库事务的功能、使用方法与相关注意事项,需要的朋友可以参考下
    2016-06-06
  • Input系统分发策略及其应用示例详解

    Input系统分发策略及其应用示例详解

    这篇文章主要为大家介绍了Input系统分发策略及其应用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Android原生集成RN最新版教程

    Android原生集成RN最新版教程

    这篇文章主要为大家介绍了Android原生集成RN最新版教程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Android 系统相机拍照后相片无法在相册中显示解决办法

    Android 系统相机拍照后相片无法在相册中显示解决办法

    这篇文章主要介绍了Android 系统相机拍照后相片无法在相册中显示解决办法的相关资料,需要的朋友可以参考下
    2016-12-12
  • Glide实现加载图片显示进度条效果

    Glide实现加载图片显示进度条效果

    Glide作为安卓开发常用的图片加载库,有许多实用而且强大的功能,那么,下面这篇文章主要给大家介绍了利用Glide实现加载图片显示进度条效果的相关资料,文中给出了详细的示例代码供大家参考学习,需要的朋友们下来一起看看吧。
    2017-05-05
  • Android RecyclerView多类型布局卡片解决方案

    Android RecyclerView多类型布局卡片解决方案

    这篇文章主要介绍了Android RecyclerView多类型布局卡片解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • Android 控制wifi 相关操作实例

    Android 控制wifi 相关操作实例

    本篇文章主要介绍了Android 控制wifi 的开发实例,并附有实例源码等相关资料,需要的朋友可以参考下
    2016-07-07
  • Android getSystemService用法实例总结

    Android getSystemService用法实例总结

    这篇文章主要介绍了Android getSystemService用法,结合实例形式总结分析了getSystemService获取系统Service的相关使用方法与注意事项,需要的朋友可以参考下
    2016-01-01

最新评论