自定义可设置MaxHeight的RecyclerView

时间:2024-04-08 14:31:04

自定义可设置MaxHeight的RecyclerView

引言

在实际工作中,可能会遇到这样一种需求,在一个列表下面紧跟一个按钮,就像垂直的LinearLayout中,先是一个RecyclerView然后跟着一个button,但是这样布局会有一个问题,当RecyclerView的内容超过一屏时,这个button就被移到屏幕外了,看不见了。如果用相对布局呢,把button定在底部,这也显然不是那么完美,因为当recyclerView的内容不足一屏时,button还是在屏幕底部,不会紧跟在列表下面,显得有点突兀。此时我们很自然会想到有没有类似maxHeight这样的属性,给RecyclerView设置一个最大高度,这样就能达到跟随在RecyclerView底部的效果了。不过很遗憾,sdk并没有提供这样一个属性,如果要达到这个效果就只能自定义RecyclerView了。下面来说说具体做法:

一、用代码设置RecyclerView的最大高度

首先新建一个Class,继承RecyclerView。

/**
 * 可以设置最大高度的recyclerView,在布局里使用 maxHeight属性指定最大高度
 *
 * @author Huangming  2019/4/8
 */
public class MaxHeightRecyclerView extends RecyclerView {
    private int mMaxHeight;

    /**
     * 设置最大高度
     *
     * @param maxHeight 最大高度 px
     */
    public void setMaxHeight(int maxHeight) {
        this.mMaxHeight = maxHeight;
        // 重绘 RecyclerView
    	requestLayout();
    }

    public MaxHeightRecyclerView(Context context) {
        super(context);
    }

    public MaxHeightRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MaxHeightRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if (mMaxHeight > 0) {
            heightSpec = View.MeasureSpec.makeMeasureSpec(mMaxHeight, View.MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthSpec, heightSpec);
    }

}

这里,除了构造方法,还提供了一个setMaxHeight的方法,并重写了onMeasure(int widthSpec, int heightSpec)方法,最重要的就是重写这个onMeasure方法,不过却非常简单,只有一行关键代码:

heightSpec = View.MeasureSpec.makeMeasureSpec(mMaxHeight, View.MeasureSpec.AT_MOST);

这行代码是什么意思呢?从方法名上可以知道这是生成测量规则,就是在绘制view之前要把这个view的高宽先测量好,而测量的规则就是在这里指定的,此处我们重新指定了测量高度的规则。再来看下这个方法具体怎么用,这个方法带有两个参数:int size, int mode,先看看官方文档:Creates a measure specification based on the supplied size and mode.(译:根据提供的大小和模式创建度量规范。)size就是尺寸了,mode有三种:

View.MeasureSpec.AT_MOST 任意尺寸,但最大不超过size指定的尺寸

View.MeasureSpec.EXACTLY 固定为size指定的尺寸

View.MeasureSpec.UNSPECIFIED 无限制,可以是任意尺寸

所以上面这行代码的意思就是:这个RecyclerView的高度可以是任意的,但最大不超过maxHight px,与我们的实现目标是一致的,使用的时候直接调一下 setMaxHeight()方法就可以了。

二、通过在layout中设置maxHeight属性值来指定RecyclerView的最大高度

用代码设置最大高度是一种方法,但有时候我们也希望在layout里面直接设置最大高度,这就需要来自定义一个属性了。

2.1 定义maxHeight属性

在res-values文件夹下新建一个attrs.xml文件,如果已存在则不必新建。打开attrs.xml文件,声明一个属性,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MaxHeightRecyclerView">
        <attr name="maxHeight" format="dimension"></attr>
    </declare-styleable>
</resources>

其中:declare-styleable name="MaxHeightRecyclerView" 中的name可以任意取,attr name="maxHeight" 中的name可以任意取。

2.2 在layout中使用maxHeight属性

首先在layout文件中声明一个命名空间:

xmlns:app="http://schemas.android.com/apk/res-auto"

然后在引用自定义RecyclerView标签中指定maxHeight的属性值,如下:

<com.example.maxheightrecyclerview.MaxHeightRecyclerView
    android:id="@+id/maxRecyclerView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:maxHeight="300dp"> //指定maxHeight属性值,注意命名空间要跟上面声明的一致,属性名也要跟 attr name="" 所定义的一致

</com.example.maxheightrecyclerview.MaxHeightRecyclerView>

最后在自定义view中读取maxHeight值,

private void initialize(Context context, AttributeSet attrs) {
    TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightRecyclerView);// 注意这里的 R.styleable.MaxHeightRecyclerView 要与attrs.xml中的<declare-styleable name="MaxHeightRecyclerView">所声明的name一致
    mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightRecyclerView_maxHeight, mMaxHeight);// 注意第一个参数的名字要写正确,是styleable的名字+‘_’+attr的名字,第二个参数是默认值
    arr.recycle();
}

当然,每个带attrs参数的构造方法都要调一下initialize()方法,完整代码如下:

/**
 * 可以设置最大高度的recyclerView,在布局里使用 maxHeight属性指定最大高度
 *
 * @author Huangming  2019/4/8
 */
public class MaxHeightRecyclerView extends RecyclerView {
    private int mMaxHeight;

    /**
     * 设置最大高度
     *
     * @param maxHeight 最大高度 px
     */
    public void setMaxHeight(int maxHeight) {
        this.mMaxHeight = maxHeight;
        // 重绘 RecyclerView
    	requestLayout();
    }

    public MaxHeightRecyclerView(Context context) {
        super(context);
    }


    public MaxHeightRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initialize(context, attrs);
    }

    public MaxHeightRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initialize(context, attrs);
    }

    private void initialize(Context context, AttributeSet attrs) {
        TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.MaxHeightRecyclerView);
        mMaxHeight = arr.getLayoutDimension(R.styleable.MaxHeightRecyclerView_maxHeight, mMaxHeight);
        arr.recycle();
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        if (mMaxHeight > 0) {
            heightSpec = View.MeasureSpec.makeMeasureSpec(mMaxHeight, View.MeasureSpec.AT_MOST);
        }
        super.onMeasure(widthSpec, heightSpec);
    }

}

来看一下效果:
自定义可设置MaxHeight的RecyclerView
可以看到,通过代码重新设置了RececlerView的最大高度为300dp与400dp两个高度,列表下方的按钮是跟随着列表的,当列表达到最大高度时,按钮就不再下移了。

最后,上源码:

demo源码:https://github.com/MingHuang1024/MaxHeightRecyclerView




由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享、探讨!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email: [email protected]

微信:724360018