zoukankan      html  css  js  c++  java
  • Android GUI之View事件处理(二)

      在上篇文章中,我们分析了View的事件处理过程,当然这里的View是指基本的View。当View接收到Touch事件时,首先会调用dispacheTouchEvent方法,在这个方法中会调用OnTouchListener和OnTouchEvent进行具体的事件处理,OnTouchListener优先于OnTouchEvent。如果在OnTouchListener的onTouch方法返回true,则后续的OnTouchEvent不再执行。而在OnTouchEvent根据Touch的动作进行具体的事件处理。这是我们在上篇文章中得到的结论,那么我们还需要考虑一个问题,我们的基本控件都是在布局文件中,而后显示在手机屏幕上,那么当我们触摸屏幕的时候,是如何将事件从Activity传递到布局(ViewGroup)再到View的呢?

      首先,我们先看一个简单的例子,我们先自定义一个View和ViewGroup分别去重新他们的dispatchTouchEvent和onTouchEvent方法,具体代码如下:

    public class MyButton extends Button {
    
        private static final String TAG="MyButton";
    
        public MyButton(Context context) {
            super(context);
        }
    
        public MyButton(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
    
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.i(TAG,"自定义按钮的dispatchTouchEvent执行了:ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.i(TAG,"自定义按钮的dispatchTouchEvent执行了:ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG,"自定义按钮的dispatchTouchEvent执行了:ACTION_UP");
                    break;
            }
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.i(TAG,"自定义按钮的OnTunchEvent执行了:ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.i(TAG,"自定义按钮的OnTunchEvent执行了:ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG,"自定义按钮的OnTunchEvent执行了:ACTION_UP");
                    break;
            }
            return super.onTouchEvent(event);
        }
    }
    public class MyGroupView extends LinearLayout {
    
        private static final String TAG="MyGroupView";
        public MyGroupView(Context context) {
            super(context);
        }
    
        public MyGroupView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            switch (ev.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.i(TAG,"自定义ViewGroup的dispatchTouchEvent执行了:ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.i(TAG,"自定义ViewGroup的dispatchTouchEvent执行了:ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG,"自定义ViewGroup的dispatchTouchEvent执行了:ACTION_UP");
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    Log.i(TAG,"自定义ViewGroup的onTouchEvent执行了:ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.i(TAG,"自定义ViewGroup的onTouchEvent执行了:ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.i(TAG,"自定义ViewGroup的onTouchEvent执行了:ACTION_UP");
                    break;
            }
            return super.onTouchEvent(event);
        }
    }

    在Activity中同样重写这两个方法,并为按钮绑定点击事件监听器具体如下:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i(TAG, "Activity的dispatchTouchEvent执行了:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG,"Activity的dispatchTouchEvent执行了:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(TAG,"Activity的dispatchTouchEvent执行了:ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i(TAG,"Activity的OnTunchEvent执行了:ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG,"Activity的OnTunchEvent执行了:ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i(TAG,"Activity的OnTunchEvent执行了:ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }

    运行程序,点击按钮,可以看到Log信息如下,从运行日志中,我们看以看出,事件的传递顺序依次为ActivityàViewGroupàView。

    在Activity,我们看到dispatchTouchEvent方法最终调用了super.dispatchTouchEvent方法,追踪代码,可以看到在Activity的源码中,其方法的具体实现为:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

      从源码中,可以看到,在Activity中是否执行onTouchEvent,完全取决于Window对象的superDispatchTounchEvent方法的返回值,之前的文章中提到过,Activity的Window实现类为PhoneWindow,找到此类的源码,查看此方法,内容如下:

        public boolean  superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event); 
    }

      很明显,调用了DecorView的superDispatchTouchEvent方法,在其具体实现中调用了父类也就是FrameLayout的dispatchTouchEvent方法,而FrameLayout并没有重写此方法,因此我们需要找到ViewGroup中的方法实现,此方法有点长,在这里就不全部贴出来,有兴趣的可以自行查看,在此方法中关键的地方调用了如下两个方法:

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits)
    public boolean onInterceptTouchEvent(MotionEvent ev)

      简单的讲上述两个方法的作用为dispatchTransfromedTouchEvent将事件传递给子View,而方法onInterceptTouchEvent则决定了是否将会拦截事件的分发。至此,View事件的传递过程我们有了基本的认识,这还不够,我们继续分析。

      之前有提过,一次完整事件是由Touch的几个动作(ACTION_DOWN、ACTION_MOVE、ACTION_UP)组成的,而且事件的开始总是由ACTION_DOWN开始,ACTION_UP结束。对上面的例子稍作修改,在MyViewGroup重写onInterceptTouchEvent方法。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.i(TAG,"自定义ViewGroup的onInterceptTouchEvent执行了");
        return super.onInterceptTouchEvent(ev);
    }

    运行后,输出日志如下,可以看到该方法返回false时,(ViewGroup中的默认返回值),并没有影响到事件的传递处理过程。

      再次修改该方法,将返回值,修改为true,日志输出结果如下,从结果看以看出事件并没有在传递给MyButton,而是被拦截了,被拦截后,执行了MyViewGroup的onTouchEvent方法。

    恢复onInterceptTouchEvent并修改dispatchTouchEvent,将其返回值改为true,运行日志结果如下,很明显事件被取消了,不再向下分发。

    恢复MyViewGroup的dispatchTouchEvent方法,修改MyButton,将onTouchEvent的返回值修改为true,运行日志结果为:

    将返回值修改为false,日志结果为:

    通过日志对比,我们可以发现当返回true时,包含MyButton的View的OnTouchEvent并没有执行,也就是意味该事件被MyButton处理了,即消费掉了。

    通过以上分析,我们可以得到如下结论:

    1、事件从Activity接收后,经ViewGroup最终传递到View中。该过程中涉及的方法有三个,具体如下:

    DispatchTouchEvent

    OnTouchEvent

    onInterceptTouchEvent

    其中onInterceptTouchEvent为ViewGroup中的方法。

    2、如果DispatchTouchEvent方法中返回值为true,在事件被取消,不再继续分发。

    3、如果View 的OnTouchEvent方法返回值为true,在意味着事件已被处理;如果返回值为false,则会依次向上调用父容器的onTouchEvent进行事件处理。

    3、如果onInterceptTouchEvent返回值为true,则事件被拦截,不再向下分发,同时调用自己的OnTouchEvent方法进行事件处理

      想要了解更多内容的小伙伴,可以点击查看源码,亲自运行测试、

    作者:杰瑞教育
    出处:http://www.cnblogs.com/jerehedu/ 
    版权声明:本文版权归烟台杰瑞教育科技有限公司和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    技术咨询:JRedu技术交流
     
  • 相关阅读:
    css报模块没找到 分析思路 从后往前找,先定位最后blue.less 解决:iview升级4.0 css没改导致编译不过去
    将config从内部移动到外部 3部曲
    iviewadmin url 加入 Router base #viewDesignAdmin
    phpStudy
    rimraf node_modules 突然不能用了 怀疑是yarn的问题,从环境变量将yarn删掉,能用了
    this.current = params.page || 1 (前提是params对象一定要存在)
    onOK Modal.warning iview 要写一个函数 套上,不然会得不到异步调用,直接弹出的时候就执行了
    ant-design-pro 如何打包成 本地html,双击即可查看
    iview mock main.js
    svelte & Polymer Project
  • 原文地址:https://www.cnblogs.com/jerehedu/p/4848345.html
Copyright © 2011-2022 走看看