自定義可設置MaxHeight的RecyclerView

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章