android开发:探索学习Android Touch事件分发传递机制(一)

想要把View的滑动事件冲突搞懂得先弄懂android的事件传递机制

滑动冲突主要有俩种方式:

1.外部滑动方向与内部方向不一致:像viewPager和listView,但是viewPager已经把这种冲突解决了。

2.外部方向与内部方向一致:像外部ScrollView嵌套一个listView。

今天要讲的例子就是ScrollView嵌套listView怎么解决滑动冲突:

如下图ScrollView直接嵌套listView滑动的效果:

很明显listView已经滑动不了,而我们希望的效果是:先滑动listView,listView滑动到底部后再滑动ScrollView

Android 两个AAR冲突了 android事件冲突解决_控件

我们采用内部拦截法来解决:

1.自定义ScrollView,重写onInterceptTouchEvent(),除了点击事件外其他都拦截

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("父布局","onInterceptTouchEvent");
        Boolean b;
        if (ev.getAction()==MotionEvent.ACTION_DOWN)
        {
            b=super.onInterceptTouchEvent(ev);
        } else {
            b=true;
        }
        return b;
    }

2.自定义listView,重写onTouchEvent()方法

@Override
    public boolean onTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e("子view点击", "onTouchEvent");
                Log.e("子view点击", "" + this.getCount());
                startX = (int) ev.getX();
                startY = (int) ev.getY();
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e("子view移动", "" + getHeight());
                Log.e("子view移动", "" + this.getLastVisiblePosition());
                int up = (int) (ev.getY() - startY);
                Log.e("子view移动", "" + up);
                View view = (View) getChildAt(getChildCount() - 1);
                //获取textView的高度,getBottom()是获取textView最初位置自身底部到父控件顶部的距离
                int childViewHight = view.getBottom();
                //getMeasuredHeight获取listview的高度,getScrollY获取listView上边界距离它内容的上边界距离
                childViewHight -= (getMeasuredHeight() + getScrollY());
                if (childViewHight == 0) {
                    Log.e("底部", "" + this.getLastVisiblePosition());
                    getParent().requestDisallowInterceptTouchEvent(false);
                    break;
                }
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
        }
        return super.onTouchEvent(ev);
    }

至于怎么判断listView什么时候是滑动到底部我们来了解一下:

Android 两个AAR冲突了 android事件冲突解决_点击事件_02

上图可以很清楚的看到listView的状态:

getBottom():它的定义是获取view的初始位置底部到父控件顶部的距离。

getScrollY():它的定义是获取view的上边界到view的内容上边界距离,如果view是listView那它的内容就是textView.

 

View view = (View) getChildAt(getChildCount() - 1);
    //获取textView的高度,getBottom()是获取textView最初位置自身底部到父控件顶部的距离
    int childViewHight = view.getBottom();
    //getMeasuredHeight获取listview的高度,getScrollY获取listView上边界距离它内容的上边界距离
    childViewHight -= (getMeasuredHeight() + getScrollY());

           

最后理一下程序流程:

我们的父控件onInterceptTouchEvent()方法除了点击事件不拦截之外其他事件都进行拦截,所以我在屏幕上点击listView的时候,点击事件都能传递到listView,而我在listView 的onTouchEvent()方法中判断只要是点击事件就去去调用  getParent().requestDisallowInterceptTouchEvent(true);(这句话意思是告诉父控件:后面的事件不要拦截,事件交由我来消费,因此listView可以滑动),当listView获取了消费权后就可以自由滑动了,然后我在case MotionEvent.ACTION_MOVE:中去判断,只要listView滑动到底部后,就调用   getParent().requestDisallowInterceptTouchEvent(false);(通知让父控件进行拦截),这样子父控件就会拦截事件,因此ScrollView开始滚动。最后来看一下运行效果:

Android 两个AAR冲突了 android事件冲突解决_嵌套_03

此外还有一种外部拦截法,在父控件拦截,满足条件则把事件下发: