Android View的事件分发机制深入分析讲解

目录
  • 1.分发对象-MotionEvent
  • 2.如何传递事件
    • 1.传递流程
    • 2.事件分发的源码解析
      • 1.Activity对点击事件的分发过程
      • 2.顶级View对点击事件的分发过程
  • 3.主要方法
  • 4.事件传递中listener
  • 5.滑动冲突如何用事件分发处理

1.分发对象-MotionEvent

事件类型有:

1.ACTION_DOWN-----手指刚接触屏幕

2.ACTION_MOVE------手指在屏幕上移动

3.ACTION_UP------手指从屏幕上松开的一瞬间

4.ACTION_CANCEL-----事件被上层拦截时触发

MotionEvent主要的方法:

getX() 得到事件发生的x轴坐标(相对于当前视图)
getY() 得到事件发生的y轴坐标(相对于当前视图)
getRawX() 得到事件发生的x轴坐标(相对于屏幕左顶点)
getRawY() 得到事件发生的y轴坐标(相对于屏幕左顶点)

2.如何传递事件

1.传递流程

底层IMS->ViewRootImpl->activity->viewgroup->view

2.事件分发的源码解析

1.Activity对点击事件的分发过程

Activity#dispatchTouchEvent()

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
//事件交给Activity所附属的Window进行分发,如果返回true,循环结束,返回false,没人处理
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
//所有View的onTouchEvent都返回false,那么Activity的onTouchEvent就会被调用
        return onTouchEvent(ev);
}

Window#superDispatchTouchEvent

public abstract boolean superDispatchTouchEvent(MotionEvent event);

PhoneWindow#superDispatchTouchEvent

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

DecorView#superDispatchTouchEvent()

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

ViewGroup#dispatchTouchEvent()

public boolean dispatchTouchEvent(MotionEvent ev) {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->

2.顶级View对点击事件的分发过程

把ViewGroup的dispatchTouchEvent()方法中的代码进行分段说明

第一段:

描述的是View是否拦截点击事件这个逻辑

 // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {//事件类型为down或者mFirstTouchTarget有值
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);//询问是否拦截,方法返回true就拦截
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;//直接拦截了
            }

当事件类型为down或者mFirstTouchTarget有值时,就不拦截当前事件,否则直接拦截了这个事件。那么mFirstTouchTarget什么时候有值?当ViewGroup不拦截事件并且把事件交给子元素处理时,mFirstTouchTarget就有值并且指向子元素。所以当事件类型为down并且拦截事件,那么mFirstTouchTarget为空,这会让后面的事件move和up无法满足mFirstTouchTarget有值的条件,直接无法调用onInterceptTouchEvent方法。

特殊情况:通过requestDisallowInterceptTouchEvent方法来设置标记位FLAG_DISALLOW_INTERCEPT,ViewGroup就无法拦截除了ACTION_DOWN以外的点击事件,这个标记位无法影响ACTION_DOWN事件,因为当事件为ACTION_DOWN时,就会重置这个标记位,将导致子View设置的这个标记位无效。

总结:

1.当ViewGroup决定拦截事件后,那么后续的点击事件将会默认交给它处理并且不再调用它的onInterceptTouchEvent方法。

2.当ViewGroup不拦截ACTION_DOWN事件,那么标记位FLAG_DISALLOW_INTERCEPT让ViewGroup不再拦截事件。

第二段:

当ViewGroup不拦截事件时,分发事件给子View,看哪个子View处理事件

                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x =
                                isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
                        final float y =
                                isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                       //对子元素进行排序
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);
                            // If there is a view that has accessibility focus we want it
                            // to get the event first and if not handled we will perform a
                            // normal dispatch. We may do a double iteration but this is
                            // safer given the timeframe.
                            if (childWithAccessibilityFocus != null) {
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }
                            if (!child.canReceivePointerEvents()
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                            resetCancelNextUpFlag(child);
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
//
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

遍历ViewGroup的所有的子元素,判断子元素是否在播动画和点击事件的坐标是否落在子元素的区域内,如果是,就能接收到点击事件,并且事件会传递给它来处理。

我们来看一下dispatchTransformedTouchEvent方法

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
            ...
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            ...
}

dispatchTransformedTouchEvent实际上调用的是子元素的dispatchTouchEvent方法。

如果子元素的dispatchTouchEvent方法返回true,那么mFirstTouchTarget就会被赋值同时跳出for循环

newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;

这几行代码完成了mFirstTouchTarget的赋值并终止对子元素的遍历,如果子元素的dispatchTouchEvent方法返回false,那么ViewGroup就会把事件分发给下一个子元素。

其实mFirstTouchTarget真正的赋值是在addTouchTarget方法里面,mFirstTouchTarget是一种单链表结构。

    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }

第三段:

执行事件

//当前View的事件处理代码
if (mFirstTouchTarget == null) {
          // No touch targets so treat this as an ordinary view.
           handled = dispatchTransformedTouchEvent(ev, canceled, null,
            TouchTarget.ALL_POINTER_IDS);
} else {
//子View的事件处理代码
...

dispatchTransformedTouchEvent方法的第三个参数为null,则会调用super.dispatchTouchEvent方法,也就是View的dispatchTouchEvent方法,所以点击事件给View处理。

View对点击事件的处理过程

View(不包含ViewGroup)是一个单独的元素,没有子元素,只能自己处理事件。

public boolean dispatchTouchEvent(MotionEvent event) {
   ...
   boolean result = false;
   ...
   if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //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;
            }
     }
    ...
    return result;
}

首先判断是否设置了OnTouchListener,如果OnTouchListener的onTouch方法返回true,就不会调用onTouchEvent方法,否则就会调用onTouchEvent方法。

public boolean onTouchEvent(MotionEvent event) {
           ...
           if ((viewFlags & ENABLED_MASK) == DISABLED
                && (mPrivateFlags4 & PFLAG4_ALLOW_CLICK_WHEN_DISABLED) == 0) {
                if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                     setPressed(false);
                }
                mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                // A disabled view that is clickable still consumes the touch
                // events, it just doesn't respond to them.
                 return clickable;
          }
               ...
}

不可用状态下的View照样会消耗点击事件

switch (action) {
    case MotionEvent.ACTION_UP:
        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
         if ((viewFlags & TOOLTIP) == TOOLTIP) {
                  handleTooltipUp();
         }
         ...
         if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
         ...
              if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                           removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                                if (!post(mPerformClick)) {
                                    performClickInternal();
                                }
                            }
                }
         }
         ...
         mIgnoreNextUpEvent = false;
         break;

当ACTION_UP事件发生时,会触发performClick方法,如果View设置了OnClickListener,那么performClick方法内部会调用它的onClick方法。

 private boolean performClickInternal() {
        // Must notify autofill manager before performing the click actions to avoid scenarios where
        // the app has a click listener that changes the state of views the autofill service might
        // be interested on.
        notifyAutofillManagerOnClick();
        return performClick();
    }
public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        //关键代码,判断是否设置了onClickListener
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }
        ...
        return result;//最终返回执行结果
}

点击事件的分发机制的源码实现已经分析完了。

3.主要方法

1.dispatchTouchEvent:用来进行事件的分发,如果事件可以传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。

2.onInterceptTouchEvent:用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。

3.onTouchEvent:用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。

4.requestDisallowInterceptTouchEvent:一般用于子View中,要求父View不拦截事件。

5.dispatchTransformedTouchEvent:如果child不为空,就发到child的dispatchTouchEvent中,否则发给自己。

4.事件传递中listener

onTouch,performClick和onClick调用的顺序以及onTouch返回值的影响?

当一个View需要处理事件时,View的dispatchTouchEvent方法中,如果设置了OnTouchListener,那么OnTouchListener的onTouch方法会被调用,当onTouch方法返回true时,onTouchEvent就不会被调用,当onTouch方法返回false时,onTouchEvent方法就被调用,在onTouchEvent方法里面进入performClick方法,在performClick方法里面判断是否设置onClickListener,并且如果设置了onClickListener,那么onClick方法就被调用,performClick方法就返回true,如果没有设置了onClickListener,performClick方法就返回false。

总的来说方法调用的顺序为

5.滑动冲突如何用事件分发处理

滑动冲突定义:当有内外两层View都可以响应事件时,事件由谁来决定。

滑动冲突类型:1.当内外两层View滑动方向不一致

2.当内外两层滑动方向一致的时候

3.两种情况叠加

解决思路:

内部拦截:dispatchTouchEvent+dispatchTransformedTouchEvent

重写子元素的dispatchTouchEvent方法

down事件分发给子元素,move事件是看条件的,如果不满足条件,就把事件交给子元素处理,如果满足条件,就会取消子元素的处理事件,然后把事件交给父元

public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                //down事件,父容器不要拦截我
                parent.requestDisallowInterceptTouchEvent(true);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                if (父容器需要此类点击事件) {
                   //父容器拦截我
                    parent.requestDisallowInterceptTouchEvent(false);
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                break;
            }
            default:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }

(当move事件时,进入第一块代码,调用intercepted = onInterceptTouchEvent(ev),我们在onInterceptTouchEvent方法中设置不是down事件就返回true,所以intercepted为true,然后第二块代码不会执行,进入第三块代码,因为intercepted为true,所以cancelChild就为true,取消子元素事件执行,调用dispatchTransformedTouchEvent方法,cancel为true->

event.setAction(MotionEvent.ACTION_CANCEL)->handled = child.dispatchTouchEvent(event)

把mFirstTouchTarget设置为空,所以到下一个move事件来的时候,mFirstTouchTarget是为空的,在第一段代码中intercepted为true,第二段代码不执行,第三块代码走dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS),即由当前View的事件处理代码(父元素))

重写父元素的onInterceptTouchEvent方法

当为down事件时,要return false,因为在ViewGroup的dispatchTouchEvent方法中,当为down事件时,会调用resetTouchState()方法,在resetTouchState()方法里面会重置状态,把mGroupFlags也重置,这样会导致在前面的parent.requestDisallowInterceptTouchEvent(true)没有用,所以我们在onInterceptTouchEvent方法里面要设置为down事件时返回false,因为在down事件时onInterceptTouchEvent一定会执行。

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

外部拦截:onInterceptTouchEvent

点击事件先经过父容器的拦截处理,如果父容器需要这件事就拦截,不需要就不拦截。

public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                intercepted = false;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (满足父容器的拦截要求) {
                    intercepted = true;
                } else {
                    intercepted = false;
                }
                break;
            }
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }
        mLastXIntercept = x;
        mLastYIntercept = y;
        return intercepted;
    }

到此这篇关于Android View的事件分发机制深入分析讲解的文章就介绍到这了,更多相关Android View事件分发机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android事件分发之View事件处理关键及示例分析

    目录 目的 View处理事件的关键 View事件处理分析 View.onTouchEvent()分析 处理长按事件 处理点击事件 处理tap事件 总结 目的 网上已经有很多关于事件分发的优秀文章,为何我还要自己写?因为别人总结的毕竟都是别人的,自己亲自阅读源码不仅会让自己更懂得原理,也会让自己记得更清楚,而且也会发现另一番天地. View处理事件的关键 由于所以的控件都直接或者间接继承自View,因此View的事件分发机制就是最基础的一环,需要首先掌握其原理. 那么View的事件从哪里来的呢?当

  • Android事件分发机制 ViewGroup分析

    目录 整体流程 源码分析 前言: 事件分发从手指触摸屏幕开始,即产生了触摸信息,被底层系统捕获后会传递给Android的输入系统服务IMS,通过Binder把消息发送到activity,activity会通过phoneWindow.DecorView最终发送给ViewGroup.这里就直接分析ViewGroup的事件分发 整体流程 配合图在看一段伪代码: public boolean dispatchTouchEvent(MotionEvent ev) :Boolean{ val result

  • Android事件分发的事件由来原理分析

    目录 Andriod事件分发的事件从何而来 调用WMS中的成员mInputManager 调用的mNative的方法 看看InputManager怎么初始化 createInputChannel干了3件事 首先看下openInputChannelPair 回到createInputChannel中 Andriod事件分发的事件从何而来 上一篇最后留下了一个疑问,WMS的事件是哪里来的? 注册事件回调是通过mWindowSession.addToDisplayAsUser来实现的,这是一个Bind

  • Android进阶事件分发机制解决事件冲突

    目录 引言 1 Android 事件分发机制 1.1 事件分发流程 1.2 View的事件消费 1.3 ViewGroup的事件分发 -- ACTION_DOWN 1.1.1 万事皆始于ACTION_DOWN 1.1.2 ViewGroup拦截事件 1.1.3 ViewGroup不拦截事件 1.4 ViewGroup的事件分发 -- ACTION_MOVE 2 Android事件冲突处理 2.1 内部拦截法 2.2 外部拦截法 引言 相信伙伴们在日常的开发工作中,一定会遇到事件冲突的问题,e.g

  • Android自定义View事件分发流程详解

    目录 正文 事件分发流程 总结 正文 事件传递和事件分发其实就是一个东西,叫法不一致罢了,你不用被名称所迷惑.有的人管这个叫事件传递机制,有的人则叫它事件分发机制.为了避免混淆,我这里统一称为事件分发.事件分发在自定义View开发中属于重点也是难点,多少人遇到瓶颈倒在这里了,所以完全有必要拿出来讲解一下. 事件分发流程 首先一个事件先从Activity的dispatchTouchEvent()方法开始. public boolean dispatchTouchEvent(MotionEvent

  • Android开发Input系统触摸事件分发

    目录 引言 1. InputDispatcher 收到触摸事件 1.1 截断策略查询 2. InputDispatcher 分发触摸事件 2.1 寻找触摸的窗口 2.1.1 根据坐标找到触摸窗口 2.1.2 保存窗口 结束 引言 Input系统: InputReader 处理触摸事件 分析了 InputReader 对触摸事件的处理流程,最终的结果是把触摸事件包装成 NotifyMotionArgs,然后分发给下一环.根据 Input系统: InputManagerService的创建与启动 可

  • Android事件分发机制示例分析

    Android事件类型 public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: break; case MotionEvent.ACTION_UP: break; case MotionEvent.ACTION_CANCEL: break; } return tru

  • Android View的事件分发机制

    一.Android View框架提供了3个对事件的主要操作概念. 1.事件的分发机制,dispatchTouchEvent.主要是parent根据触摸事件的产生位置,以及child是否愿意负责处理该系列事件等状态,向其child分发事件的机制. 2.事件的拦截机制,onInterceptTouchEvent.主要是parent根据它内部的状态.或者child的状态,来把事件拦截下来,阻止其进一步传递到child的机制. 3.事件的处理机制,onTouchEvent.主要是事件序列的接受者(可以是

  • 解析Android点击事件分发机制

    开头说说初衷 网上关于点击事件分发的文章一搜一大堆,标题一看,不是"30分钟让你弄明白XXX"就是"这是讲解XXX最好的文章",满怀憧憬与信心,忍不住兴奋的点进去一看,发现不是代码就全是图,我基本上看完了所有相关的文章,结果硬是看了三个小时也没搞懂.所以最后还是决定自己去试一试,看一看点击事件分发到底是怎么个流程,我写的肯定不会比其他文章好多少,但是呢,带着一个初学者的心,去分析这个东西,自己能弄明白的同时,也让想学习这个的人看了之后有些许收获,那就足够了. 运行的

  • Android View的事件分发详解

    1.前言 近两天学习了一下view的事件分发,把自己的理解总结了一遍,只表达了自己认为需要明白的地方,毕竟是菜鸟一枚,不对的地方还请大神们多指教! 2.三个方法 public boolean dispatchTouchEvent(MotionEvent ev) 用于事件的分发,返回结果受以下两个方法的影响,表示是否消耗了事件. public boolean onInterceptTouchEvent(MotionEvent ev) 事件是否被拦截,返回true表示拦截,false表示不拦截 pu

  • 如何自己实现Android View Touch事件分发流程

    Android Touch事件分发是Android UI中的重要内容,Touch事件从驱动层向上,经过InputManagerService,WindowManagerService,ViewRootImpl,Window,到达DecorView,经View树分发,最终被消费. 本文尝试通过对其中View部分的事件分发,也是与日常开发联系最紧密的部分,进行重写.说是重写,其实是对Android该部分源码进行大幅精简而不失要点,且能够独立运行,以一窥其全貌,而不陷入到源码繁杂的细节中. 以下类均为

  • 聊聊Android中的事件分发机制

    View事件分发机制的本质就是就是MotionEvent事件的分发过程,即MotionEvent产生后是怎样在View之间传递及处理的. 首先介绍一下什么是MotionEvent.所谓MotionEvent,即用户手指触碰手机屏幕时产生的一系列触摸事件.典型的触摸事件有: ACTION_DOWN:手指刚接触屏幕的一瞬间. ACTION_MOVE:手指在屏幕上滑动. ACTION_UP:手指离开屏幕的一瞬间. ACTION_CANCLE:当前事件序列终止. 一个事件序列一般都是以DOWN事件开始,

  • Android从源码的角度彻底理解事件分发机制的解析(上)

    其实我一直准备写一篇关于Android事件分发机制的文章,从我的第一篇博客开始,就零零散散在好多地方使用到了Android事件分发的知识.也有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图片使用Button而不用ImageView?等等--对于这些问题,我并没有给出非常详细的回答,因为我知道如果想要彻底搞明白这些问题,掌握Android事件分发机

  • Android View事件分发机制详解

    准备了一阵子,一直想写一篇事件分发的文章总结一下,这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等,这些子View的外层还有ViewGroup,如RelativeLayout,LinearLayout.作为一个开发者,我们会思考,当点击一个按钮,Android系统是怎样确定我点的就是按钮而不是TextView的?然后还正确的响应了按钮的点击事件.内部经过了一系列什么过程呢? 先铺垫一些知识能更加清晰的理解事件分发机制: 1. 通过setC

  • Android事件分发机制(下) View的事件处理

    综述 在上篇文章Android中的事件分发机制(上)--ViewGroup的事件分发中,对ViewGroup的事件分发进行了详细的分析.在文章的最后ViewGroup的dispatchTouchEvent方法调用dispatchTransformedTouchEvent方法成功将事件传递给ViewGroup的子View.并交由子View进行处理.那么现在就来分析一下子View接收到事件以后是如何处理的. View的事件处理 对于这里描述的View,它是ViewGroup的父类,并不包含任何的子元

  • Android从源码的角度彻底理解事件分发机制的解析(下)

    记得在前面的文章中,我带大家一起从源码的角度分析了Android中View的事件分发机制,相信阅读过的朋友对View的事件分发已经有比较深刻的理解了. 还未阅读过的朋友,请先参考Android从源码的角度彻底理解事件分发机制的解析. 那么今天我们将继续上次未完成的话题,从源码的角度分析ViewGroup的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子VewGroup,是And

  • Android事件分发机制(上) ViewGroup的事件分发

    综述 Android中的事件分发机制也就是View与ViewGroup的对事件的分发与处理.在ViewGroup的内部包含了许多View,而ViewGroup继承自View,所以ViewGroup本身也是一个View.对于事件可以通过ViewGroup下发到它的子View并交由子View进行处理,而ViewGroup本身也能够对事件做出处理.下面就来详细分析一下ViewGroup对时间的分发处理. MotionEvent 当手指接触到屏幕以后,所产生的一系列的事件中,都是由以下三种事件类型组成.

随机推荐