自定义可设置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);
}
}
来看一下效果:
可以看到,通过代码重新设置了RececlerView的最大高度为300dp与400dp两个高度,列表下方的按钮是跟随着列表的,当列表达到最大高度时,按钮就不再下移了。
最后,上源码:
demo源码:https://github.com/MingHuang1024/MaxHeightRecyclerView
由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享、探讨!
博客:http://blog.csdn.net/MingHuang2017
GitHub:https://github.com/MingHuang1024
Email: [email protected]
微信:724360018