安卓必须知道的android进阶教程程序员

Android开发艺术探索-第三章-View的事件体系

2016-01-18  本文已影响1039人  KuTear

layout: post
date: 2016-01-08
title: Android开发艺术探索-第三章-View的事件体系
categories: blog
tags: [Activity,Android,View,MotionEvent,TouchSlop]
category: Android
description:


本文首发于个人博客KuTear,转载引用请注明原出处.谢谢!
另外,更多文章分享请查看博客KuTear

3.1 View的基础知识

3.2 View的滑动

3.3 View的事件分发机制

  1. 事件分发过程的三个重要方法

    • dispatchTouchEvent

      函数原型

         public boolean dispatchTouchEvent(MotionEvent ev)
      

      主要的功能是负责事件的分发.
      返回值:
      true: 表示向下分发中断
      false: 表示继续向下分发

    • onInterceptTouchEvent

      函数原型

         public boolean onInterceptTouchEvent(MotionEvent event)
      

      主要功能是负责事件的拦截
      返回值:
      true:拦截,事件交由自己(View/ViewGroup)的onTouchEvent(...)处理
      false:不拦截,事件继续向下分发.

    • onTouchEvent

      函数原型

         public boolean onTouchEvent(MotionEvent event)
      

      主要功能是处理触摸事件
      返回值:
      true:表示消费了这个事件.
      false:表示没有消费该事件,返回到上级处理.如果一直得不到处理,最终反馈到Activity的onTouchEvent(...)

  2. 函数之间的逻辑关系

    • 以上三个函数的伪代码

      类似于递归调用的方式

        public boolean dispatchTouchEvent(MotionEvent ev) {
            boolean consume = false;
            if (onInterceptTouchEvent(ev)) {
                consume = onTouchEvent(ev);
            } else {
                consume = child.dispatchTouchEvent(ev);
            }
            return consume;
        }
      
    • 函数与监听接口

      在通常情况下,我们为Button等组件设置了onClickListener接口,有时也会设置onTouchListener接口,但在什么时候接口中的方法才会执行呢?如果设置了onTouchListener接口监听,会对View(ViewGroup)的onTouchEvent有一定的影响.如果设置了onTouchListener,她的onTouch的返回值会影响view中onTouchEvent的调用与否,onTouch返回值的含义与onTouchEvent一样,表示是否消费了该事件.onTouch会先于onTouchEvent执行.伪代码为

         //true表示消费掉
         if(!listener.onTouch(ev)){
             onTouchEvent(ev);
         }
      

      对于onClickListener接口,他内部方法onCLick的调用是在onTouchEvent中(根据上面就知道如果在onTouchListener的onTouch中返回true,onclick就不会再执行了),其内部部分代码如下.

         //View#onTouchEvent(...)
         if (mPerformClick == null) {
            mPerformClick = new PerformClick();
         }
         if (!post(mPerformClick)) {
            performClick();
         }
         
         //点击事件的处理者 
         private final class PerformClick implements Runnable {
            @Override
            public void run() {
                performClick();
            }
        }
        
        //点击调用onClick函数
        public boolean performClick() {
            //ListenerInfo封装了各种监听
            final ListenerInfo li = mListenerInfo;
            if (...) {
                //调用部分
                li.mOnClickListener.onClick(this);
                result = true;
            }
            ...
            return result;
        }
      

      根据上面的描述,知道调用顺序为onTouchListener#onTouch,返回值决定是否继续执行view的onTouchEvent,最后在onTouchEvent中执行onClickListener的onClick方法.

  3. 分发过程

    • Activity分发

      触摸事件最先到达Activity,所以首先会在Activity中分发

             //Activity#dispatchTouchEvent()
             public boolean dispatchTouchEvent(MotionEvent ev) {
                 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                     onUserInteraction();
                 }
                 //分发到Window.
                 if (getWindow().superDispatchTouchEvent(ev)) {
                     //true表示不再向下分发
                     return true;
                 }
                 return onTouchEvent(ev);
             }
      

      在getWindow()中返回mWindow,最终在函数attach(...)中发现

             mWindow = new PhoneWindow(this);
      

      PhoneWindow不在SDK中,在在线源码(Android源码)网站上可以找到相关的代码

             public boolean superDispatchTouchEvent(MotionEvent event ) {
                 //DecorView extends FrameLayout 
                 //       DecorView#superDispatchTouchEvent(ev)
                 //       public boolean superDispatchTouchEvent(MotionEvent event) {
                 //               //来到了ViewGroup
                 //               return super.dispatchTouchEvent(event);
                 //       }
                 return mDecorView.superDispatchTouchEvent(event);
             }
      

      由此就把事件分发到了ViewGroup,接下来就是在VieGroup中分发.

同样是dispatchTouchEvent(...)的部分代码

            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                //这里说明没有子View处理该事件,只得有View的dispatchTouchEvent(...)来处理.
                //关于该函数的部分源码在后面介绍.
                handled = dispatchTransformedTouchEvent(ev, canceled, null/*child*/,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
             ...   
            }
    
 函数addTouchTarget(...)的具体实现.
            
            private TouchTarget addTouchTarget(View child, int pointerIdBits) {
                TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
                target.next = mFirstTouchTarget;
                mFirstTouchTarget = target;
                return target;
            }
            
  函数dispatchTransformedTouchEvent(...)的部分实现.
  
        ....
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        ....
        return handled.

3.4 View的滑动冲突

参考

  1. Art of Android Development Reading Notes 3

  2. 用户手势检测-GestureDetector使用详解

  3. ViewDragHelper详解

  4. Android触摸屏事件派发机制详解与源码分析一(View篇)

上一篇下一篇

猜你喜欢

热点阅读