ScrollView嵌套ListView時(shí)可能產(chǎn)生的問題解決

一.ListView的高度設(shè)置為wrap_content

當(dāng)ScrollView嵌套ListView時(shí),ListView的高度設(shè)置為wrap_content時(shí)會(huì)產(chǎn)生,一般情況下ListView只顯示的第一個(gè)Item。
代碼如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#aaa">

        <ListView
            android:id="@+id/lv_test1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#ccc" />
    </ScrollView>

</LinearLayout>

正常情況下,高度設(shè)置為“wrap_content”的ListView在測(cè)量自己的高度會(huì)使用MeasureSpec.AT_MOST這個(gè)模式高度來返回可包含住其內(nèi)容的高度。

而實(shí)際上當(dāng)ListView被ScrollView嵌套時(shí),ListView使用的測(cè)量模式是ScrollView傳入的MeasureSpec.UNSPECIFIED。

// ScrollView的measureChildWithMargins()代碼:
@Override
protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    ViewGroup.LayoutParams lp = child.getLayoutParams();

    int childWidthMeasureSpec;
    int childHeightMeasureSpec;

    childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft
            + mPaddingRight, lp.width);

    childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
            MeasureSpec.getSize(parentHeightMeasureSpec), MeasureSpec.UNSPECIFIED);

    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

而在ListView的onMeasure方法中:使用MeasureSpec.UNSPECIFIED模式測(cè)量時(shí)只能加載一部分高度。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Sets up mListPadding
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

     ...
     ...

    if (heightMode == MeasureSpec.UNSPECIFIED) {
        heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                getVerticalFadingEdgeLength() * 2;
    }


    setMeasuredDimension(widthSize, heightSize);

    mWidthMeasureSpec = widthMeasureSpec;
}

直接運(yùn)行結(jié)果如下


20180711_111957.gif

解決方法:覆寫ListView的onMeasure方法:

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.ListView;

/**
* Created by cxm on 2016/9/15.
*/
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 defStyleAttr) {
       super(context, attrs, defStyleAttr);
   }

   @TargetApi(Build.VERSION_CODES.LOLLIPOP)
   public MyListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
       super(context, attrs, defStyleAttr, defStyleRes);
   }

   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2, // 設(shè)計(jì)一個(gè)較大的值和AT_MOST模式
               MeasureSpec.AT_MOST);
       super.onMeasure(widthMeasureSpec, newHeightMeasureSpec);//再調(diào)用原方法測(cè)量
   }
}

上述解決方法有一個(gè)缺陷就是,當(dāng)?shù)谝淮芜M(jìn)入界面動(dòng)態(tài)加載listview的i數(shù)據(jù)后,ScrollView會(huì)跳滑到listview的第一個(gè)子項(xiàng)。
解決方法:先設(shè)置ListView失去焦點(diǎn),這并不影響item的點(diǎn)擊事件發(fā)生
結(jié)果如圖


20180711_111602.gif

二.ListView高度固定

當(dāng)對(duì)listview的高度設(shè)置為固定值(例200dp)時(shí),listview的高度是可以直接顯示出來的。但嵌套在一起后ScrollView中的ListView就沒法上下滑動(dòng)了,事件先被ScrollView響應(yīng)了。
布局代碼如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <TextView
                android:text="Test2"
                android:layout_width="match_parent"
                android:layout_height="300px" />
            <ListView
                android:id="@+id/lv_test2"
                android:layout_width="match_parent"
                android:layout_height="500dp"
                android:background="#ccc" />
            <TextView
                android:text="Test2"
                android:layout_width="match_parent"
                android:layout_height="300px" />
            <TextView
                android:text="Test2"
                android:layout_width="match_parent"
                android:layout_height="300px" />
            <TextView
                android:text="Test2"
                android:layout_width="match_parent"
                android:layout_height="300px" />
        </LinearLayout>

    </ScrollView>

</LinearLayout>
20180711_112616.gif

解決方法:當(dāng)ListView自身接收到的滑動(dòng)事件時(shí),使ScrollView取消攔截。ListView區(qū)域內(nèi)的滑動(dòng)事件由自己處理,ListView區(qū)域外滑動(dòng)事件由外層ScrollView處理??梢韵到y(tǒng)自帶的API來實(shí)現(xiàn):requestDisallowInterceptTouchEvent這一方法。
自定義ListView來重寫ListView的dispatchTouchEvent函數(shù):

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ListView;

/**
 * Created by cxm on 2016/9/15.
 */
public class ScollListView extends ListView {
    public ScollListView(Context context) {
        super(context);
    }

    public ScollListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ScollListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ScollListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                getParent().requestDisallowInterceptTouchEvent(false);
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
}

如圖


20180711_112219(1).gif

demo地址:https://github.com/ShenChaohui/ScrollViewAndListView.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容