自定義可設置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