android 事件分发 View

事件分发 网上很多 人都分析过  让自己更加深刻 我也试试
先来一个View的  Button 为例子

从简单的开始   分析 View 的  

一个点击  一个Ontouch  




看看结果 


onclick 在ontouch之后被调用

为什么呢  可以看看  源码  从View  看去
  这里 我们就要想到  触摸一个控件  内部 是什么过程  看过网上别人写 说一定会 调用该控件的dispatchTouchEvent方法

    想要知道为什么  我也不知道 android内核 决定的 这是高手玩的    我是网上搜查了一些资料   说是这样的  

你触摸屏幕 linux内核的一个input子系统来管理 InputManager 报告触摸事件 将处理此事传给 Android的
一个Service   WindowManagerService  
经过一系列的***** 到了 ViewRoot.dispatchPointer()中 
    忽略内核中的过程  就是  触摸控件 --->内核处理--->WindowManagerService(一系列处理)----> ViewRoot.dispatchPointer()

然后我就在想  ViewRoot 是什么   我点击ViewRoot 看不到源码  估计Source里面没有 我就奇怪了  拿出神奇everything 发现了 一个 

进去一看   我想 这货估计是替代了ViewRoot   google 果不其然   不晓得什么时候更新的(android更新有点快) 
然后 在google的时候  一不小心 就 看别人介绍了这个类(介绍的人还蛮多) 

作用是向 DecorView  发送事件

DecorView  这是什么    来神器 分析仪



是他最顶层的View



就把ViewRootImpl 当做一个连接的中间人 吧    正好他是继承Handler 通信的也能解释的通

现在  看他的 那个 方法  

这里 我们就知道了 他是继承 Handler  那必须是 handlerMessge方法来处理 至于怎么到handlerMessage的  后面打算 写下Handler Looper 机制  里面有 上次不小心看了  这里不要深究 



进去 这个方法   


看啊看  看啊看  有点多  但是 我们心里 有个底 应该快到 DecorView了上面不是说了  向它来发一些信息来完成交互  所以 带着这点想法 继续看下去 

发现一个惊天大秘密  幸好认识这个 单词 刚才用过 View hierarchy  视图结构 那这个mView 估计就是我们要找的 





这个mView 是一个View 那就说明 每个控件都是去调用View的  然后我们差不多 走完了底层  来到我们看得到的  

进入 View的源码里面的这个方法

  

之后 到了 dispatchTouchEvent  这里  




看下这三个条件吧  网上别人写了  我就截图过来了 guolin大神的



发现一个 东西   ontouch(this,event) ;  也就是 我们button的ontouch  我们外面是返回的false  所以这里 我们没有满足条件  进去了下面的 触摸行为

在onTouchEvent  这个方法中  我们在看 
  


这里 就是 让我们看到 那个按钮按下去的 显示


在往下看  说了 是执行 那个click的行为  


现在 再进去 这个 performClick()方法 

看到 这里 我泪牛满面  卧槽  真的是这里  执行了 然后 返回 true 事件被消费  


再看看这些结果 就是这样啊  


现在 改为 返回 true  发现 onclick 没有执行  你 down一下 后面返回一次 true 不执行ontouchevent 就不会进入那个点击的方法



从源码 我们 会发现  
/**
     * Implement this method to handle touch screen motion events.
     *
     * @param event The motion event.
     * @return True if the event was handled, false otherwise.
     */
    public boolean onTouchEvent(MotionEvent event) {
        final int viewFlags = mViewFlags;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }

        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }


        if ((<span style="color:#ff0000;">(viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)</span>) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true);
                       }

                        if (!mHasPerformedLongPress) {
                            // This is a tap, so remove the longpress check
                            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)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }
                        removeTapCallback();
                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true);
                        checkForLongClick(0);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    setPressed(false);
                    removeTapCallback();
                    removeLongPressCallback();
                    break;

                case MotionEvent.ACTION_MOVE:
                    final int x = (int) event.getX();
                    final int y = (int) event.getY();

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();

                            setPressed(false);
                        }
                    }
                    break;
            }
          <span style="color:#ff0000;">  return true;</span>
        }

        return false;
    }


只要这个 


条件 满足控件能被点击  就会进入if 里面 这个的最后 面 是  返回的一个true  

所以 可以得出 一个结论  只有 走到这个true 这个ontouchevent才会被消费 

但是 发现 两个问题  
第一个 本以为 在ACtion_Down 后面 返回 false  事件应该不会被消费  但是事实不是如此  <在分析中>
然后我仔细 看了这下面的这段 源码 
   case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true);
                        checkForLongClick(0);
                    }
                    break;

这几种方法 能被执行的如下  


最后 还是这个方法  postDelayed   

所以 不管你在 Action——down 里面 返回 false  or true 对里面的步骤都没得影响   其实这么想 因为这里面的是ontouchEvent方法 我们现在是监听ontouch监听  所以这两个不同  开始混淆了  

所以 他该怎么执行 还是怎么执行 执行完了 break  跳到  整个返回true  触发效果
如果你在继承这个ImageView 然后 复写这个ontouchEvent方法  然后再action_down返回false  会发现 执行到这里 后面的就不会执行 整个事件结束 没次只会down 


第二个问题   同样的 在继承ImageView 的ontouchEvent方法里面 会发现 不会进入  都是我一开始  弄混了 应该 在这个ontouchEvent 来处理 这个才是  真正处理 三个行为的 方法 

所以说 网上 看别人的 自己要实验 才能肯定  时间可以磨灭一切 就算以前是对的 现在可能都是错的  



这里就分析了View的  后面来分析 更加复杂的ViewGroup  这个挺复杂  我要组织几天的语言 才能说清楚 
  











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值