ScrollView-Nested-Problems點(diǎn)擊打開鏈接
解決Android中出現(xiàn)ScrollView嵌套 ListView、RecyclerView、GridView 、WebView出現(xiàn)的高度問題。
開篇語:最近開始想寫一些技術(shù)總結(jié)了,一方面分享給其他同學(xué),另一方面也作為自己的技術(shù)積累。 今天我分享的是日常遇到的問題,ScrollView組件里面嵌套GridView、WebView、ListView等本身具有滑動(dòng)的組件時(shí),所引發(fā)的高度顯示不全的問題。
對(duì)于GridView、WebView、ListView這三類組件來說,大家都知道通常情況下這三類組件本身是具有滑動(dòng)特性的,其實(shí)際占用的高度也只是屏幕內(nèi)顯示的高度,當(dāng)嵌套在ScrollView組件里時(shí)就造成了沖突。 所以解決的思路可以從其onMeasure方法入手,onMeasure方法是重寫自定義View中用到的一個(gè)非常重要的方法,onMeasure方法的作用就是計(jì)算出自定義View的寬度和高度,這個(gè)計(jì)算的過程參照父布局給出的大小,以及自己特點(diǎn)算出結(jié)果。onMeasure方法是在父視圖計(jì)算子視圖大小時(shí)被調(diào)用的,其中的細(xì)節(jié)就不在此詳細(xì)講述。
@Override?
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {??
? int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);? ? ? ?super.onMeasure(widthMeasureSpec, mExpandSpec);
}
上面方法的2個(gè)參數(shù),來自于父視圖發(fā)過來給子視圖的限制條件,這涉及到一個(gè)知識(shí)點(diǎn)MeauseSpec這個(gè)類.
一.MeasureSpec的構(gòu)成
MeasureSpec代表一個(gè)32位的int值,前倆位代表SpecMode,后30位代表SpecSize.其中:SpecMode代表測(cè)量的模式,SpecSize值在某種測(cè)量模式下的規(guī)格大小。
共有三種測(cè)量模式:
1.EXACTLY: 父容器已經(jīng)檢測(cè)出子View所需要的精確大小,這個(gè)時(shí)候view的大小即為SpecSize的大小,他對(duì)應(yīng)于布局參數(shù)中的MATCH_PARENT,或者精確大小值;
2.AT_MOST: 父容器指定了一個(gè)大小,即SpecSize,子view的大小不能超過這個(gè)SpecSize的大??;
3.UNSPECIFIED: 父容器對(duì)子View的大小沒有任何要求,子View想多大都可以。
二.如何創(chuàng)建MeasureSpec MeasureSpec內(nèi)部提供了創(chuàng)建MeasureSpec的方法:
public static int makeMeasureSpec(int size, int mode) {
? ? ? ? if (sUseBrokenMakeMeasureSpec) {
? ? ? ? ? ? return size + mode;
? ? ? ? } else {
? ? ? ? ? ? return (size & ~MODE_MASK) | (mode & MODE_MASK);
? ? ? ? }
? ? }
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT; 二進(jìn)制 1100....00 32位
public static final int UNSPECIFIED = 0 << MODE_SHIFT; ? 二進(jìn)制 0000....00 32位
public static final int EXACTLY ? ? = 1 << MODE_SHIFT; ? 二進(jìn)制 0100....00 32位
public static final int AT_MOST = 2 << MODE_SHIFT; 二進(jìn)制 1000....00 32位
MeasureSpec代表一個(gè)32位的int值,前倆位代表SpecMode,后30位代表SpecSize.通過巧妙的位運(yùn)算,即可通過MeasureSpec來得到SpecSize,SpecMode.
public static int getMode(int measureSpec) {
? ? ? ? return (measureSpec & MODE_MASK);?
? ? ? } ? public static int getSize(int measureSpec) {
? ? ? ? return (measureSpec & ~MODE_MASK);
? ? }
對(duì)于RecyclerView來說,重寫onMeasure()方法就不管用了。
1.一種解決辦法是在RecyclerView的外部套上一層RelativeLayout
<RelativeLayout
? ? ? android:layout_width="match_parent"
? ? ? android:layout_height="wrap_content"
? ? ? android:descendantFocusability="blocksDescendants">
android:descendantFocusability屬性的值有三種:
beforeDescendants:viewgroup會(huì)優(yōu)先其子類控件而獲取到焦點(diǎn)
blocksDescendants:viewgroup會(huì)覆蓋子類控件而直接獲得焦點(diǎn)
afterDescendants:viewgroup只有當(dāng)其子類控件不需要獲取焦點(diǎn)時(shí)才獲取焦點(diǎn)
但是這個(gè)方案recyclerView時(shí)有卡頓的問題 原因還是滑動(dòng)沖突的問題,可以重寫LinearLayoutManager,設(shè)置讓其不可滑動(dòng),外部滑動(dòng)靠ScrollView,這樣就解決了滑動(dòng)時(shí)卡頓的問題
代碼如下: public class ScrollLinearLayoutManager extends LinearLayoutManager { private boolean isScrollEnabled = true;
? ? public ScrollLinearLayoutManager(Context context) {
? ? ? ? super(context);
? ? }
? ? public ScrollLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
? ? ? ? super(context, orientation, reverseLayout);
? ? }
? ? public ScrollLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
? ? ? ? super(context, attrs, defStyleAttr, defStyleRes);
? ? }
? ? public void setScrollEnabled(boolean flag) {
? ? ? ? this.isScrollEnabled = flag;
? ? }
? ? @Override
? ? public boolean canScrollVertically() {
? ? ? ? return isScrollEnabled && super.canScrollVertically();
? ? }
}
簡(jiǎn)單使用: ScrollLinearLayoutManager scrollLinearLayoutManager = new ScrollLinearLayoutManager(this);
scrollLinearLayoutManager.setScrollEnabled(false);
mRecyclerView.setLayoutManager(scrollLinearLayoutManager);
2.完美方案是這樣的:首先在xml布局中將你的ScrollView替換成android.support.v4.widget.NestedScrollView,并在java代碼中設(shè)置recyclerView.setNestedScrollingEnabled(false);