zoukankan      html  css  js  c++  java
  • Android:ViewGroup和View的Touch事件

    Android中ViewGroup和View中的Touch事件传递机制分析

    关键字:GroupView;View;Touch事件

    基础知识:

    onInterceptTouchEvent():在ViewGroup中定义(View中无该方法),用于拦截手势事件,触发的每个Touch事件都会先调用onInterceptTouchEvent()。

    onTouchEvent():分别View中(ViewGroup继承自View,自然包含该方法),用于处理传递到View中的Touch事件。

    View中的onTouchEvent()返回true时,表明:当次已经完全消费了该事件,不希望其他回调方法再次处理;反之则返回false。

    事件回调过程:当我们手指点击屏幕时候,先调用ACTION_DOWN事件,当onTouchEvent()里返回值是true的时候,onTouchEvent()会继续调用ACTION_UP事件,如果onTouchEvent()里返回值是false,那么onTouch只会调用ACTION_DOWN而不调用ACTION_UP。关于返回值的问题,基本规则很清楚,如果return true,那么表示该方法消费了此次事件,如果return false,那么表示该方法并未处理完全,该事件仍然需要以某种方式传递下去继续等待处理。

    Touch事件包含:ACTION_DOWN(getAction()=0),ACTION_UP(getAction()=1),ACTION_MOVE(getAction()=2),ACTION_CANCEL等事件。针对down事件的处理返回值直接影响到后续的move和up事件的接收和传递。

    Android SDK对onInterceptTouchEvent()的解释:

    由于onInterceptTouchEvent()的机制比较复杂,上面的说明写的也比较复杂,总结一下,基本的规则是:

    1. down事件首先会传递到onInterceptTouchEvent()方法;

    2. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return false,那么后续的move, up等事件将继续会先传递给该ViewGroup,之后才和down事件一样传递给最终的目标view的onTouchEvent()处理;

    3. 如果该ViewGroup的onInterceptTouchEvent()在接收到down事件处理完成之后return true,那么后续的move, up等事件将不再传递给onInterceptTouchEvent(),而是和down事件一样传递给该ViewGroup的onTouchEvent()处理,注意,目标view将接收不到任何事件;

    4. 如果最终需要处理事件的view的onTouchEvent()返回了false,那么该事件将被传递至其上一层次的view的onTouchEvent()处理;

    5. 如果最终需要处理事件的view 的onTouchEvent()返回了true,那么后续事件将可以继续传递给该view的onTouchEvent()处理。

    现通过如下的简单实例分析Android中的点击事件:

    其中,整个布局是一个自定义ViewGroup,下面的Button同样是自定义View。

    整体布局如下:

    <com.demo.view.DesignLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.demo.TouchEventDemo" >
        <com.demo.view.DesignView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello_world" />
    </com.demo.view.DesignLayout>

    自定义的ViewGroup为:

    package com.demo.view;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.widget.FrameLayout;
    import com.demo.LogUtil;
    public class DesignLayout extends FrameLayout {
        private static final String TAG = DesignLayout.class.getSimpleName();
        public DesignLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        public DesignLayout(Context context) {
            super(context);
        }
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // 默认返回false
            LogUtil.d(TAG,
                    "DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)="
                            + super.onInterceptTouchEvent(ev));
            return false;
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 默认返回false
            LogUtil.d(TAG, "DesignLayout:onTouchEvent::super.onTouchEvent(event)="
                    + super.onTouchEvent(event));
            return super.onTouchEvent(event);
        }
    }

    自定义的View:

    package com.demo.view;
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.widget.Button;
    import com.demo.LogUtil;
    public class DesignView extends Button {
        private static final String TAG = DesignView.class.getSimpleName();
        public DesignView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
        public DesignView(Context context) {
            super(context);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // 默认返回true
            LogUtil.d(TAG, "DesignView:onTouchEvent::super.onTouchEvent(event)="
                    + super.onTouchEvent(event));
            return false;
        }
    }

    主体的TouchEventActivity如下:

    package com.demo;
    import android.app.Activity;
    import android.os.Bundle;
    public class TouchEventDemo extends Activity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
        }
    }

    测试结果如下:

    Layout中的onInterceptTouchEvent()默认返回的是false,表示不拦截Touch事件,也就是说:Touch事件将传递到View控件中;onTouchEvent(),默认返回的是false;

    View中的onTouchEvent()默认返回的是true;

    实验一:Layout中onInterceptTouchEvent()默认返回的是false,View的onTouchEvent()返回true,执行以下逻辑:

    D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (13207): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (13207): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_UP
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
    D/Demo    (13207): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_UP

    Layout不拦截Touch事件,传递到子View中;子View消费Touch事件,不会去执行父ViewGroup的onTouchEvent(),本次Touch事件已消费并停止;Layout和子View会多次接收和消费其他Touch事件,比如ACTION_MOVE和ACTION_UP事件。

    实验二:让ViewGroup的onInterceptTouchEvent()返回默认的false且onTouchEvent()返回false,但是让View的onTouchEvent()返回false,执行如下逻辑:

    D/Demo    (13869): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (13869): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (13869): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
    D/Demo    (13869): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (13869): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (13869): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN

    Touch事件未被ViewGroup拦截,并传递到底层的View中;View的onTouchEvent()返回false时,表明不消费该Touch事件,会回传到其父ViewGroup的onTouchEvent()中。View里的onTouchEvent只消费了一次点击事件也就是ACTION_DOWN,还没有执行ACTION_UP,然后跑到ViewGroup里又去执行OnTouchEvent事件;此时ViewGroup中onTouchEvent()返回为false,也仅会消费一次ACTION_DOWN事件(或者说,本次Touch事件并未被消费,所以其他的ACTION_MOVE和ACTION_UP事件无法接收)。

    点击非Button区域时,也只会去执行ViewGroup的onTouchEvent(),并消费一次ACTION_DOWN。

    D/Demo    (14433): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (14433): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (14433): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14433): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN

    实验三:让ViewGroup的onInterceptTouchEvent()返回默认的false且onTouchEvent()返回true,但是让View的onTouchEvent()返回false,执行如下逻辑:

    点击Button区域,并滑动时:

    D/Demo    (14631): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (14631): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (14631): [DesignView] DesignView:onTouchEvent::super.onTouchEvent(event)=true
    D/Demo    (14631): [DesignView] DesignView:onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP

    Touch事件未被ViewGroup拦截,并传递到底层的View中;View的onTouchEvent()返回false时,表明不消费该Touch事件,会回传到其父ViewGroup的onTouchEvent()中。View里的onTouchEvent只接收一次点击事件也就是ACTION_DOWN(不再接收其他Touch事件),还没有执行ACTION_UP,然后跑到ViewGroup里又去执行OnTouchEvent事件;此时ViewGroup中onTouchEvent()返回为true,也就是消费了本次ACTION_DOWN事件(并表明身份:我能消费Touch事件,以后的事件都由我来消费吧!)。

    点击非Button区域,并滑动时:

    D/Demo    (14631): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (14631): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (14631): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (14631): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP

    每次点击、滑动并抬起时,其消费模式:一次ACTION_DOWN、多次ACTION_MOVE和一次ACTION_UP事件。

    实验四:将Layout中的onInterceptTouchEvent()返回值修改为true且onTouchEvent()返回默认值false。点击包含有View的Button区域时,执行下述逻辑:

    D/Demo    (10417): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (10417): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (10417): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false

    不会执行到View中的onTouchEvent(),表明Touch事件被ViewGroup拦截,会执行ViewGroup的onTouchEvent()(本次只会传递一次ACTION_DOWN事件,不会传递其他事件如:ACTION_MOVE和ACTION_UP)。点击非Button区域时,Logcat打印结果和上述相同。

    实验五:实验环境同实验二,不同之处在于ViewGroup的onTouchEvent()返回true,点击包含有View的Button区域时,执行以下逻辑:

    D/Demo    (15835): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (15835): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP

    不会执行到View中的onTouchEvent(),表明Touch事件被ViewGroup拦截,会执行ViewGroup的onTouchEvent();但不同之处在于:不但会接收到ACTION_DOWN事件,其他事件也会接收到如:ACTION_MOVE和ACTION_UP。

    点击非Button区域时,执行以下逻辑:

    D/Demo    (15835): [DesignLayout] DesignLayout:onInterceptTouchEvent::super.onInterceptTouchEvent(ev)=false
    D/Demo    (15835): [DesignLayout] onInterceptTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_DOWN
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_UP
    D/Demo    (15835): [DesignLayout] DesignLayout:onTouchEvent::super.onTouchEvent(event)=false
    D/Demo    (15835): [DesignLayout] onTouchEvent::MotionEvent.ACTION_MOVE
  • 相关阅读:
    iOS resign code with App Store profile and post to AppStore
    HTTPS科普扫盲帖 对称加密 非对称加密
    appid 评价
    使用Carthage安装第三方Swift库
    AngularJS:何时应该使用Directive、Controller、Service?
    xcode7 The operation couldn't be completed.
    cocoapods pod install 安装报错 is not used in any concrete target
    xcode7 NSAppTransportSecurity
    learning uboot how to set ddr parameter in qca4531 cpu
    learning uboot enable protect console
  • 原文地址:https://www.cnblogs.com/CVstyle/p/6388060.html
Copyright © 2011-2022 走看看