事件分发 网上很多 人都分析过 让自己更加深刻 我也试试
先来一个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 这个挺复杂 我要组织几天的语言 才能说清楚