让listview在scrollview中自由滑动

来源:互联网 发布:淘宝浏览app赚佣金 编辑:程序博客网 时间:2024/05/21 10:38

总有人我listview嵌套scrollview怎么弄。一问就是半天,太耗时,所以写个博客也算是自己总结一下。

目标

  • scrollview嵌套listview,可以自由的定义listview的大小,而不是展示全部listview。

  • 让listview在scrollview中自由滑动。

  • 当listview滑动到顶端或底端的时候,让scrollview开始滑动

直接上图看效果好了:
这里写图片描述

代码也很简单 直接上代码

<?xml version="1.0" encoding="utf-8"?><ScrollView xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:background="@color/colorPrimary"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="wang.com.scrollholdlistview.MainActivity">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="vertical">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <wang.com.scrollholdlistview.MyListView            android:id="@+id/lv"            android:layout_width="match_parent"            android:layout_height="400dp"            android:background="@color/colorAccent"></wang.com.scrollholdlistview.MyListView>        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="@dimen/activity_vertical_margin"            android:text="Hello World!"            android:textSize="30sp" />        <wang.com.scrollholdlistview.MyListView            android:id="@+id/lv1"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:background="@color/colorAccent">        </wang.com.scrollholdlistview.MyListView>    </LinearLayout></ScrollView>
package wang.com.scrollholdlistview;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.ListView;/** * 创建日期: 16/4/3 下午9:55 * 作者:wanghao * 描述: */public class MyListView extends ListView {    private static final String TAG = "MyListView";    public MyListView(Context context) {        this(context, null);    }    public MyListView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int defaultsize=measureHight(Integer.MAX_VALUE >> 2, heightMeasureSpec);        int expandSpec = MeasureSpec.makeMeasureSpec(defaultsize, MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }    private int measureHight(int size, int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        if (specMode == MeasureSpec.EXACTLY) {            Log.i(TAG, "exactly" );            result = specSize;        } else {            result = size;//最小值是200px ,自己设定            if (specMode == MeasureSpec.AT_MOST) {                result = Math.min(result, specSize);            }            Log.i(TAG, "specMode:"+specMode+"--result:"+result );        }        return result;    }    /**     * 改listview滑到底端了     *     * @return     */    public boolean isBottom() {        int firstVisibleItem = getFirstVisiblePosition();//屏幕上显示的第一条是list中的第几条        int childcount = getChildCount();//屏幕上显示多少条item        int totalItemCount = getCount();//一共有多少条        if ((firstVisibleItem + childcount) >=totalItemCount) {            return true;        }        return false;    }    /**     * 改listview在顶端     *     * @return     */    public boolean isTop() {        int firstVisibleItem = getFirstVisiblePosition();        if (firstVisibleItem ==0) {            return true;        }        return false;    }    float down = 0;    float y;    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                down = event.getRawY();                getParent().requestDisallowInterceptTouchEvent(true);                break;            case MotionEvent.ACTION_MOVE:                y = event.getRawY();                if (isTop()) {                    if (y - down > 1) {//                        到顶端,向下滑动 把事件教给父类                        getParent().requestDisallowInterceptTouchEvent(false);                    } else {                        //                        到顶端,向上滑动 把事件拦截 由自己处理                        getParent().requestDisallowInterceptTouchEvent(true);                    }                }                if (isBottom()) {                    if (y - down > 1) {//                        到底端,向下滑动 把事件拦截 由自己处理                        getParent().requestDisallowInterceptTouchEvent(true);                    } else {//                        到底端,向上滑动 把事件教给父类                        getParent().requestDisallowInterceptTouchEvent(false);                    }                }                break;            default:                break;        }        return super.dispatchTouchEvent(event);    }}

上面代码直接用就可以啦。
那么下面说下好多人遇到过的疑惑

  • 为什么这么写就能避免嵌套问题呢
    下面代码是网上流传最多的
@Overridepublic void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int expandSpec = MeasureSpec.makeMeasureSpec(            Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);    super.onMeasure(widthMeasureSpec, expandSpec);}

这样写呢有个弊端,就是 在scrollview里面展示了 listview的全部内容。有100条就显示了100条。
Integer.MAX_VALUE >> 2这个呢就是一个值。让你的listview最大值是多大。你可以写成65535,或者其他数值。为什么用 MeasureSpec.AT_MOST呢?
看下super里面

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // Sets up mListPadding        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightSize = MeasureSpec.getSize(heightMeasureSpec);       ...        if (heightMode == MeasureSpec.AT_MOST) {            // TODO: after first layout we should maybe start at the first visible position, not 0            heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);        }        setMeasuredDimension(widthSize, heightSize);        mWidthMeasureSpec = widthMeasureSpec;    }

再看下measureHeightOfChildren()的内容

final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,            int maxHeight, int disallowPartialChildPosition) {        final ListAdapter adapter = mAdapter;        if (adapter == null) {            return mListPadding.top + mListPadding.bottom;        }        // Include the padding of the list        int returnedHeight = mListPadding.top + mListPadding.bottom;        final int dividerHeight = ((mDividerHeight > 0) && mDivider != null) ? mDividerHeight : 0;        // The previous height value that was less than maxHeight and contained        // no partial children        int prevHeightWithoutPartialChild = 0;        int i;        View child;        // mItemCount - 1 since endPosition parameter is inclusive        endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;//获取有多少个item        final AbsListView.RecycleBin recycleBin = mRecycler;        final boolean recyle = recycleOnMeasure();        final boolean[] isScrap = mIsScrap;//遍历所有item并且 获取到 所有item的整体高度        for (i = startPosition; i <= endPosition; ++i) {            child = obtainView(i, isScrap);            measureScrapChild(child, i, widthMeasureSpec, maxHeight);            if (i > 0) {                // Count the divider for all but one child                returnedHeight += dividerHeight;            }            // Recycle the view before we possibly return from the method            if (recyle && recycleBin.shouldRecycleViewType(                    ((LayoutParams) child.getLayoutParams()).viewType)) {                recycleBin.addScrapView(child, -1);            }            returnedHeight += child.getMeasuredHeight();//如果item的高度大于咱们赋值的高度也就是Integer.MAX_VALUE >> 2             if (returnedHeight >= maxHeight) {                // We went over, figure out which height to return.  If returnedHeight > maxHeight,                // then the i'th position did not fit completely.                return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)                            && (i > disallowPartialChildPosition) // We've past the min pos                            && (prevHeightWithoutPartialChild > 0) // We have a prev height                            && (returnedHeight != maxHeight) // i'th child did not fit completely                        ? prevHeightWithoutPartialChild 如果是大于就用自己item整体的高度作为listview的高度                        : maxHeight; 如果是等于那么久用 我们赋值的高度            }//如果item的高度 不大于等于 默认值 那么久显示他自己的高度了             if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {                prevHeightWithoutPartialChild = returnedHeight;            }        }        // At this point, we went through the range of children, and they each        // completely fit, so return the returnedHeight        return returnedHeight;    }

所以我们赋值的Integer.MAX_VALUE >> 2只是一个参考值,你想写多少就是多少。
但是那样写有个弊端就是在xml里面写的高度 就没用了。所以我们就这么写了

  @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int defaultsize=measureHight(Integer.MAX_VALUE >> 2, heightMeasureSpec);        int expandSpec = MeasureSpec.makeMeasureSpec(defaultsize, MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }    private int measureHight(int size, int measureSpec) {        int result = 0;        int specMode = MeasureSpec.getMode(measureSpec);        int specSize = MeasureSpec.getSize(measureSpec);        if (specMode == MeasureSpec.EXACTLY) {            Log.i(TAG, "exactly" );            result = specSize;        } else {            result = size;//最小值是200px ,自己设定            if (specMode == MeasureSpec.AT_MOST) {                result = Math.min(result, specSize);            }            Log.i(TAG, "specMode:"+specMode+"--result:"+result );        }        return result;    }

这样的话就是如果 xml里面是wrap_content的话 就显示所有item的高度。如果是设置了 高度值 比如是400dp。那么listview显示的高度就是400dp。
为什么那么些 请戳这尼自定义view,viewgroup的onMeasure 方法

那么问题又来了。
如果显示400dp的高度。listview就没法全部显示了。
所以就不能让scrollview拦截事件,要让该事件传递到listview,并且处理。 一行代码就搞定

  getParent().requestDisallowInterceptTouchEvent(true);  true:就是拦截此事件  false:不拦截此事件,让父类处理

剩下的就只是当listview滑动到顶端和底端 listview就拦截该事件了。代码里已经体现的很简单了。
尊重原创:http://blog.csdn.net/wanghao200906/article/details/51084975
代码在这里

最近太忙了。发个福利安慰一下自己
这里写图片描述

2 0