关于view事件体系的几个问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38703938/article/details/83748777

view和viewGroup事件区别

这里主要说下在事件分发上的区别,绘制上面也是有区别的,我们都看过相关源码。

这里总结一下:

①事件分发

事件分发机制主要有三个方法:dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()

1.ViewGroup包含这三个方法,而View则只包含dispatchTouchEvent()、onTouchEvent()两个方法,不包含onInterceptTouchEvent()。

2.触摸事件由Action_Down、Action_Move、Action_Up组成,一次完整的触摸事件,包含一个Down和Up,以及若干个Move(可以为0);

3.在Action_Down的情况下,事件会先传递到最顶层的ViewGroup,调用ViewGroup的dispatchTouchEvent(),①如果ViewGroup的onInterceptTouchEvent()返回false不拦截该事件,则会分发给子View,调用子View的dispatchTouchEvent(),如果子View的dispatchTouchEvent()返回true,则调用View的onTouchEvent()消费事件。②如果ViewGroup的onInterceptTouchEvent()返回true拦截该事件,则调用ViewGroup的onTouchEvent()消费事件,接下来的Move和Up事件将由该ViewGroup直接进行处理。

4.当某个子View的dispatchTouchEvent()返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下来的Move和Up事件将由该子View直接进行处理。

5.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch();触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。

6..由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上层ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象。如ViewGroup0——ViewGroup1——TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,将被保存在ViewGroup0中;当Move和Up事件来时,会先从ViewGroup0传递到ViewGroup1,再由ViewGroup1传递到TextView,最后事件由TextView消费掉。

7.子View可以调getParent().requestDisallowInterceptTouchEvent(),请求父ViewGroup不拦截事件

②绘制原理

UI绘制主要有五个方法:onDraw(),onLayout(),onMeasure(),dispatchDraw(),drawChild()

1.ViewGroup包含这五个方法,而View只包含onDraw(),onLayout(),onMeasure()三个方法,不包含dispatchDraw(),drawChild()。

2.绘制流程:onMeasure(测量)——>onLayout(布局)——>onDraw(绘制)。

3.onLayout():对于View来说,onLayout()只是一个空实现;而对于ViewGroup来说,onLayout()使用了关键字abstract的修饰,要求其子类必须重载该方法,目的就是安排其children在父视图的具体位置。

4.draw过程:drawBackground()绘制背景——>onDraw()对View的内容进行绘制——>dispatchDraw()对当前View的所有子View进行绘制——>onDrawScrollBars()对View的滚动条进行绘制。

viewpager和listview的事件冲突

滑动冲突的解决方案:

1.外部拦截法

具体做法就是当你不想把事件传递给子控件的时候在onInterceptTouchEvent方法中返回true即可拦截事件,这时候子控件将不会再接收到这一次的touch事件流(所谓touch事件流是以ACTION_DOWN开始,中间包含若干个ACTION_MOVE,以ACTION_UP结束的一连串事件)。伪代码如下:

public boolean onInterceptTouchEvent(MotionEvent ev) {
    if ( condition ) {
        return true;
    }
    return false;
}

condition就得根据具体情形来设定。

2.内部拦截法

首先,我们让父控件拦截除了ACTION_DOWN以外的所有事件,如果连ACTION_DOWN都拦截那么子控件将无法收到任何touch事件:

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    int action = event.getAction();
    if (action == MotionEvent.ACTION_DOWN) {
        return false;
    } else {
        return true;
    }
}

然后,在控件的内部分发事件的时候请求需要的事件(实际上就是禁止父控件拦截事件):

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            //通知父容器不要拦截事件
            parent.requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:

            if ( <condition> ){
                //通知父容器拦截此事件
                parent.requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
            parent.requestDisallowInterceptTouchEvent(false);
            break;
        default:
            break;
    }

    return super.dispatchTouchEvent(ev);
}

这样,就可以解决touch事件的冲突问题,从控件本身解决。内部拦截法使用起来稍显复杂,需要修改两个控件,一般情况下我们都通过外部拦截法解决滑动冲突,如果有特殊情况需要使用内部拦截法才会使用内部拦截法。

写在最后

关于这两种解决方案的实例以后会补上,再具体说明。

猜你喜欢

转载自blog.csdn.net/weixin_38703938/article/details/83748777
今日推荐