ScrollView巢狀ListView或GridView等,使得其高度自適應解決方案
阿新 • • 發佈:2019-01-23
這類的文章有很多,寫此文的目的是為了備忘吧。ScrollView裡面巢狀ListView或GridView等,兩個View都有滾動的效果,在巢狀使用時起了衝突,一般不建議兩者套用。解決的方案有很多但是最優的解決方案如下:
在Android中,一個控制元件所佔的模式和大小是通過一個整數int來表示的,這裡很多同學就疑惑了,一個int值是怎麼來表示模式的大小的,這裡來看一張圖片:
原來,Android裡面把int的最高2兩位來表示模式,最低30位來表示大小。
測量View大小使用的是onMeasure函式,我們可以從onMeasure的兩個引數中取出寬高的相關資料:
從上面可以看出 onMeasure 函式中有 widthMeasureSpec 和 heightMeasureSpec 這兩個 int 型別的引數, 毫無疑問他們是和寬高相關的, 但它們其實不是寬和高, 而是由寬、高和各自方向上對應的測量模式來合成的一個值:
測量模式一共有三種, 被定義在 Android 中的 View 類的一個內部類View.MeasureSpec中: 3種模式
1):UNSPECIFIED模式,官方意思是:父佈局沒有給子佈局強加任何約束,子佈局想要多大就要多大,說白了就是不確定大小
2)EXACTLY模式,官方意思是:父佈局給子佈局限定了準確的大小,子佈局的大小就是精確的,父親給多大就是多大
3)AT_MOST模式,官方意思是:父佈局給定了一個最大的值,子佈局的大小不能超過這個值,當然可以比這個值小
private
static final
int MODE_SHIFT = 30;public
static final
int UNSPECIFIED = 0 << MODE_SHIFT;public
static final
int EXACTLY = 1 << MODE_SHIFT;public
static final
int AT_MOST = 2 << MODE_SHIFT;
package com.base.frame.view; import android.content.Context; import android.util.AttributeSet; import android.widget.ListView; public class MyListView extends ListView { public MyListView(Context context) { super(context); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); } public MyListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } /** * 重寫該方法,達到使ListView適應ScrollView的效果(因為listview的高度是不確定的,所以每次要重新測量) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
原理解析:
瞭解自定義view 的肯定知道 onMeasure()是什麼意思,makeMeasureSpec()方法中 Integer.MAX_VALUE >> 2在Android中,一個控制元件所佔的模式和大小是通過一個整數int來表示的,這裡很多同學就疑惑了,一個int值是怎麼來表示模式的大小的,這裡來看一張圖片:
原來,Android裡面把int的最高2兩位來表示模式,最低30位來表示大小。
測量View大小使用的是onMeasure函式,我們可以從onMeasure的兩個引數中取出寬高的相關資料:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthsize = MeasureSpec.getSize(widthMeasureSpec); //取出寬度的確切數值 int widthmode = MeasureSpec.getMode(widthMeasureSpec); //取出寬度的測量模式 int heightsize = MeasureSpec.getSize(heightMeasureSpec); //取出高度的確切數值 int heightmode = MeasureSpec.getMode(heightMeasureSpec); //取出高度的測量模式 }
從上面可以看出 onMeasure 函式中有 widthMeasureSpec 和 heightMeasureSpec 這兩個 int 型別的引數, 毫無疑問他們是和寬高相關的, 但它們其實不是寬和高, 而是由寬、高和各自方向上對應的測量模式來合成的一個值:
測量模式一共有三種, 被定義在 Android 中的 View 類的一個內部類View.MeasureSpec中: 3種模式
1):UNSPECIFIED模式,官方意思是:父佈局沒有給子佈局強加任何約束,子佈局想要多大就要多大,說白了就是不確定大小
2)EXACTLY模式,官方意思是:父佈局給子佈局限定了準確的大小,子佈局的大小就是精確的,父親給多大就是多大
3)AT_MOST模式,官方意思是:父佈局給定了一個最大的值,子佈局的大小不能超過這個值,當然可以比這個值小
不確定模式是0左移30位,也就是int型別的最高兩位是00
精確模式是1左移30位,也就是int型別的最高兩位是01
最大模式是是2左移30位,也就是int型別的最高兩位是10
所以呼叫了makeMeasureSpec方法,這個方法是用來生成一個帶有模式和大小資訊的int值的,第一個引數Integer.MAX_VALUE >> 2,這個引數是傳的一個大小值,為什麼是這個值呢,我們現在已經知道了,我們要生成的控制元件,它的大小最大值是int的最低30位的最大值,我們先取Integer.MAX_VALUE來獲取int值的最大值,然後左移2位就得到這個臨界值最大值了
當然,我們在手機上的控制元件的大小不可能那麼大,極限值就那麼大,實際肯定比那個小,所以這個模式就得選擇MeasureSpec.AT_MOST了,最後將生成的這個大小傳遞給父控制元件就可以了,super.onMeasure(widthMeasureSpec, expandSpec),這個函式只改變的是控制元件的高度,寬度沒有改變,實際開發當中不管listview有多少條資料,都能一次性展現出來。最後選用第三種方式完美實現。