view事件解析(上)

在写博客之前先感谢郭霖大神,之前拜读了好多次郭大神关于事件的文章,Android事件分发机制完全解析,带你从源码的角度彻底理解(上)Android事件分发机制完全解析,带你从源码的角度彻底理解(下),感谢郭大神。
郭老大已经对事件分析的很透彻了,小弟写这篇文章仅仅是加了一点自己的思考。
说到事件得先从view事件出发。首先自定义了一个Btn2继承Button,然后分别在onTouchEvent(),dispatchTouchEvent(),setOnTouchListener(),setOnClickListener方法中加了log,来分析其对应的流程。

public class Btn2 extends Button{

    public Btn2(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        System.out.println("Btn2---OnTouchListener  onTouch---DOWN");
                        //return true;
                       break;
                    case MotionEvent.ACTION_MOVE:
                        System.out.println("Btn2---OnTouchListener  onTouch---MOVE");
                        break;
                    case MotionEvent.ACTION_UP:
                        System.out.println("Btn2---OnTouchListener  onTouch---UP");
                        break;
                    default:
                        break;
                }
                return false;
            }
        });

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                 Log.e("Btn2","---OnClickListener onClick---go on");
            }
        });
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                System.out.println("Btn2---onTouchEvent---DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                System.out.println("Btn2---onTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
                System.out.println("Btn2---onTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //getParent().requestDisallowInterceptTouchEvent(true);
                System.out.println("Btn2---dispatchTouchEvent---DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                System.out.println("Btn2---dispatchTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
                System.out.println("Btn2---dispatchTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(event);
    }
}

这里写图片描述

然后点击btn2,下面是打印的log:
这里写图片描述
可以看到事件view的流程依次是dispatchTouchEvent-down -> OnTouchListener onTouch DOWN -> onTouchEvent DOWN ->dispatchTouchEvent UP -> OnTouchListener onTouch UP ->onTouchEvent UP -> OnClickListener onClick go on。可以看到dispatchTouchEvent分发,onTouch和onTouchEvent都走了2次,走完后最后走了一次OnClickListener。为什么会出现这样,下面我们来看看源码。
btn2继承Button,在Button中我们没有找到dispatchTouchEvent,然后继续像上找,找到view,看到dispatchTouchEvent方法中有这么一句话:

 if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

我们看到这句li.mOnTouchListener.onTouch(this, event),这里调用了onTouch方法,如果这里返回true(同时前面的2个参数都为true)时,result为true,然后下面的if取反,不在去调用onTouchEvent方法。ok,下面我们改一下btn2的代码,将onTouch方法的返回值直接改为true,看看打印的log情况。
这里写图片描述
看到结果是onTouchEvent和onClick方法都没有执行,很好奇为什么onClick也没有执行,后来发现在onTouchEvent的源码中调用了performClick方法,在这个方法里面执行了onclick,正好解答了刚才的疑问。

public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

也就是我们常说的如果返回true,那么该事件就认为被消化掉,不再向下传递。

接着我们再看看ViewGroup的事件到底是怎么进行的。我在Btn2外面定义了一个MyFrameLayout继承FrameLayout,然后也对其几个关键的放大加了log,这里要注意在在ViewGroup中多了一个事件拦截的方法onInterceptTouchEvent。

  @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
               System.out.println("MyFrameLayout---dispatchTouchEvent---DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
             System.out.println("MyFrameLayout---dispatchTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
              System.out.println("MyFrameLayout---dispatchTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
               System.out.println("MyFrameLayout---onTouchEvent---DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
              System.out.println("MyFrameLayout---onTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
             System.out.println("MyFrameLayout---onTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
              Log.e("MyFrameLayout", "---onInterceptTouchEvent---DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
            Log.e("MyFrameLayout","---onInterceptTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
             Log.e("MyFrameLayout","---onInterceptTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(event);
    }
    <com.example.ontouchtext.MyFrameLayout
        android:id="@+id/framelayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <com.example.ontouchtext.Btn2
            android:id="@+id/btn2"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="#aaaaaa"
            android:text="btn2" />
    </com.example.ontouchtext.MyFrameLayout>

接着我们再次点击一下log,注意一下我这里所有的onTouch默认返回都是false。
这里写图片描述
可以看到先走了btn2父类MyFrameLayout的分发dispatchTouchEvent的方法,接着走了拦截方法onInterceptTouchEvent,紧接着走的是btn2自己的分发dispatchTouchEvent方法,然后走了自己的onTouch,这里返回的是false,所有走了onTouchEvent,最后走了onClick。也就是说事件会先走父类,然后分发给自己,自己在进行一次分发。
说到这里基本上就把整个分发说的差不多了,下面来说一下两个有点小意思的东西。
1.如果在父类的onInterceptTouchEvent中的MotionEvent.ACTION_DOWN返回true,同时onTouchEvent的MotionEvent.ACTION_DOWN也返回true,说明父类从down就开始消化掉这个事件。我们来看看log,有啥有意思的地方:

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
               System.out.println("MyFrameLayout---onTouchEvent---DOWN");
                return true;
                //break;
            case MotionEvent.ACTION_MOVE:
              System.out.println("MyFrameLayout---onTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
             System.out.println("MyFrameLayout---onTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
              Log.e("MyFrameLayout", "---onInterceptTouchEvent---DOWN");
                return true;
              //  break;
            case MotionEvent.ACTION_MOVE:
            Log.e("MyFrameLayout","---onInterceptTouchEvent---MOVE");
              //  return true;
               break;
            case MotionEvent.ACTION_UP:
             Log.e("MyFrameLayout","---onInterceptTouchEvent---UP");
                break;
            default:
                break;
        }
        return true;
    }

这里写图片描述
有发现了没,发现只要父类决定拦截,而且消化掉这个事件,那么以后的move,up将不再走父类的拦截,也不会再进行分发。
2.在子类(Btn2)中的onTouchEvent返回true,在父类MyFrameLayout中onInterceptTouchEvent的MotionEvent.ACTION_MOVE返回true,onTouchEvent中的MotionEvent.ACTION_MOVE也返回true。我们再来看看log。


Btn2:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                System.out.println("Btn2---onTouchEvent---DOWN");
                return true;
              //break;
            case MotionEvent.ACTION_MOVE:
                System.out.println("Btn2---onTouchEvent---MOVE");
                break;
            case MotionEvent.ACTION_UP:
                System.out.println("Btn2---onTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }
MyFrameLayout:

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
               System.out.println("MyFrameLayout---onTouchEvent---DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
              System.out.println("MyFrameLayout---onTouchEvent---MOVE");
                return true;
                //break;
            case MotionEvent.ACTION_UP:
             System.out.println("MyFrameLayout---onTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
        public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.e("MyFrameLayout", "---onInterceptTouchEvent---DOWN");
               break;
            case MotionEvent.ACTION_MOVE:
            Log.e("MyFrameLayout","---onInterceptTouchEvent---MOVE");
              return true;
              // break;
            case MotionEvent.ACTION_UP:
             Log.e("MyFrameLayout","---onInterceptTouchEvent---UP");
                break;
            default:
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

这里写图片描述
哈哈,发现down父类没有拦截,然后就交给子类的onTouchEvent了,然而这里我子类的onTouchEvent中的down即使返回了true,到了move仍然走了父类的拦截方法,这时候我在父类中onInterceptTouchEvent得ACTION_MOVE返回了true,同时在父类中onTouchEvent的ACTION_MOVE返回了true,然后下面的move和up都被父类消化了,这也验证了上面第一点。

欢迎拍砖。 View事件解析(下)这里写链接内容

源码:http://download.csdn.net/detail/linhui9010/9332825

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值