解决 ScrollView 嵌套 ListView 时,高度不正常和滑动冲突的问题

来源:互联网 发布:诸暨淘宝拍卖资产 编辑:程序博客网 时间:2024/05/08 10:51

我们在用ScrollView嵌套ListView会两个问题,一个问题是ListView高度不正常,另外一个问题是ListView无法滑动。下面我们就来看看这两个问题怎么解决吧。


第一个问题

ListView只能显示一个Item高度的问题。因为ScrollView在测量ChildView的时候,强制把ChildView的MeasureSpec模式更改为MeasureSpec.UNSPECIFIED,而我们通过查看ListView的onMeasure方法可以发现

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

当heightMode为MeasureSpec.UNSPECIFIED时,ListView的高度会被设置为上下的padding加上一个childView的高度,所以就会出现只显示一个Item高度的问题。

那我们怎么解决呢?


第一种方法,我们可以重写ListView,在onMeasure的时候重新设置高度的MeasureSpec模式。

    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // MeasureSpec的前两位是模式,所以需要右移两位。        heightMeasureSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }
这样ListView就是完全展开的状态了。


第二种方法,我们可以在布局里面为ScollView加一个

android:fillViewport="true"
这样ScrollView就会被强制要求测量ChildView并设置模式MeasureSpec.EXACTLY,所以ListView会完全展开。


第二个问题

ScrollView嵌套ListView时,一般我们有两种需求

第一种是ListVIew完全伸展并跟随ScrollView一起滑动,那只要按照上面的解决了伸展的问题, 就实现这种效果了,因为ScrollView默认是拦截ListView的滑动事件的。


第二种是ScrollView不拦截滑动事件,当我们在ListView区域滑动时,由ListView处理滑动事件,只有在ListView已到达顶部还继续向上滑或者ListView已到达底部还继续向下滑时才重新拦截滑动事件。而当我们在非ListView区域滑动时,则直接由ScrollView处理滑动事件,那么我们看看怎么实现这种效果。


首先,我们先了解为什么ListView无法获取到滑动事件,我们先看一下ScrollView的onInterceptTouchEvent方法

 @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        final int action = ev.getAction();        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {            return true;        }省略       //........}
不重要的部分被我省略了,mIsBeingDragged是最小滑动像素,超过这个值才被认为是发生滑动了。那从上面的代码我们可以看出的,当发生滑动的第一时间,事件就被ScrollView拦截了,所以ListView不能滑动

但是我们注意到,在MotionEvent.ACTION_MOVE之前是会产生一个MotionEvent.ACTION_DOWN的,那么这个事件是没有被拦截的,并且在滑动未超过最小像素之前,
MotionEvent.ACTION_MOVE也是未被拦截的,那么其实我们是可以在ListView里面接收到MotionEvent.ACTION_DOWN和几个MotionEvent.ACTION_MOVE的。
既然如此,那我们在自定义ListView里面请求ScrollView不要拦截事件不就好了吗。


public class MyListview extends ListView{    private ScrollView mParent;    private float mDownY;    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);    }    public void setParent(ScrollView view){        mParent = view;    }    //重写该方法 在按下的时候让父容器不处理滑动事件    @Override    public boolean onTouchEvent(MotionEvent ev) {        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                setParentScrollAble(false);                mDownY = ev.getY();                break;            case MotionEvent.ACTION_MOVE:                //                if (isListViewReachTop() && ev.getY() - mDownY > 0) {                    setParentScrollAble(true);                } else if (isListViewReachBottom() && ev.getY() - mDownY < 0) {                    setParentScrollAble(true);                }                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                setParentScrollAble(true);                break;            default:                break;        }        return super.onTouchEvent(ev);    }    /**     * @param flag     */    private void setParentScrollAble(boolean flag) {        mParent.requestDisallowInterceptTouchEvent(!flag);    }    public boolean isListViewReachTop() {        boolean result=false;        if(getFirstVisiblePosition()==0){            View topChildView = getChildAt(0);            if (topChildView != null) {                result=topChildView.getTop()==0;            }        }        return result ;    }    public boolean isListViewReachBottom() {        boolean result=false;        if (getLastVisiblePosition() == (getCount() - 1)) {            View bottomChildView = getChildAt(getLastVisiblePosition() - getFirstVisiblePosition());            if (bottomChildView != null) {                result= (getHeight() >= bottomChildView.getBottom());            }        }        return  result;    }}
在上面的代码中,我们首先需要把ScrollView设置进来,然后在onTouchEvent中接收到MotionEvent.ACTION_DOWN事件时,请求ScrollView不要拦截滑动事件,这样ListView就可以处理滑动事件了,而在顶部仍然向上滑和在底部仍然向下滑 和 MotionEvent.ACTION_UP 和 MotionEvent_ACTION_CANCEL时,都直接重新把事件给回ScrollView。



                                             
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 牛仔裙太宽了怎么办 牛仔裙腰围小了怎么办 牛仔a字裙大了怎么办 牛仔a字裙易变形怎么办 红色皮衣染色了怎么办 羽绒服干了结块怎么办 羽绒服手洗后鸭毛堆在一起怎么办 白色羽绒服发霉了怎么办 早晨起床双脚冷怎么办 黑色棉衣粘毛怎么办 貂皮大衣旧了怎么办 pu皮床头掉皮怎么办 背心领子大怎么办鸡心 羊毛裙子缩水了怎么办 背带裙背带老掉怎么办 背带裙背带坏了怎么办 黑色衣服发红了怎么办 公司拖欠离职员工工资怎么办 windows系统坏了怎么办 背带裙肩带总掉怎么办 四个月宝宝干咳怎么办 4个多月宝宝干咳怎么办 小孩吃饭不嚼怎么办 四个月宝宝积食怎么办 新生儿睡枕头了怎么办 宝宝半夜练翻身怎么办 宝宝牙齿长歪怎么办 婴儿睡觉头歪怎么办 婴儿放在床就哭怎么办 宝宝米粉没吃完怎么办 宝宝吃米粉上火怎么办 幼儿发烧怎么办 两岁 七个月婴儿厌食怎么办 打卤时肉总是沾在一起怎么办 宝宝大便头干硬怎么办 婴儿辅食机的水怎么办 三岁宝宝牙痛怎么办 5岁宝宝牙痛怎么办 6岁儿童脑维缩怎么办 产后奶水多胀痛怎么办 新生儿心脏偏大怎么办