正文 ###聊聊UI事件传递html
触摸的位置、多指触摸等
ACTION_DOWN = 0
按下ACTION_UP = 1
抬起ACTION_MOVE = 2
移动ACTION_CANCEL = 3
动做取消ACTION_OUTSIDE = 4
动做超出边界ACTION_POINTER_DOWN = 5
已有一个点被按住,此时再按下一个点ACTION_POINTER_UP = 6
多个点被按住,非最后放开的点都会调用上图简略关系以下: android
× 以前一直觉得事件是从子布局开始传递到父布,由于以直观的角度咱们先碰到的是子布局获得错误的事件顺序:view1 --> ViewGroup2 --> ViewGroup1
git
√ 后来才知道事件是从父布局传递到子布局,是由父布局判断点击位置上面有子布局而后向子布局传递。若是事件向子布局传递没有被拦截和消费,那么事件又会向父布局传递。正确的没有被拦截和消费的事件顺序:Activity --> ViewGroup1 --> ViewGroup2 --> View1 --> ViewGroup2 --> ViewGroup1 --> Activity
github
如下的Log为当手指对view1点击、滑动、抬起时,
发生的一系列事件传递(0.按下;1.抬起; 2.移动):
复制代码
E/MainActivity: ----------- dispatchTouchEvent = 0
E/ViewGroup1: ------------- dispatchTouchEvent = 0
E/ViewGroup1: ------------- onInterceptTouchEvent = 0
E/ViewGroup2: ------------- dispatchTouchEvent = 0
E/ViewGroup2: ------------- onInterceptTouchEvent = 0
E/View1: ------------------ dispatchTouchEvent = 0
E/View1: ------------------ onTouchEvent = 0
E/ViewGroup2: ------------- onTouchEvent = 0
E/ViewGroup1: ------------- onTouchEvent = 0
E/MainActivity: ----------- onTouchEvent = 0
E/MainActivity: ----------- dispatchTouchEvent = 2
E/MainActivity: ----------- onTouchEvent = 2
E/MainActivity: ----------- dispatchTouchEvent = 2
E/MainActivity: ----------- onTouchEvent = 2
E/MainActivity: ----------- dispatchTouchEvent = 2
E/MainActivity: ----------- onTouchEvent = 2
E/MainActivity: ----------- dispatchTouchEvent = 1
E/MainActivity: ----------- onTouchEvent = 1
E/MainActivity: ----------- dispatchTouchEvent = 1
E/MainActivity: ----------- onTouchEvent = 1
复制代码
观察 |
---|
能够看出事件由外层大布局到内部子布局传进去,在从子布局传出去(Activity --> ViewGroup1 --> ViewGroup2 --> View1 --> ViewGroup2 --> ViewGroup1 --> Activity) |
由此log还能够看出:当按下的事件没有被拦截,那么全部状态的事件都由Activity进行处理 |
经过dispatchTouchEvent对事件进行拦截,当返回值为true
的时候拦截事件bash
拦截后事件将不会传到子布局 如今以ViewGroup1为例: 让ViewGroup1中的dispatchTouchEvent直接返回true 当手指对View1点击、移动、抬起时 发生的一系列事件传递(0.按下;1.抬起; 2.移动)app
复制代码
E/MainActivity: ---------------- dispatchTouchEvent = 0 E/ViewGroup1: ------------------ dispatchTouchEvent = 0 E/MainActivity: ---------------- dispatchTouchEvent = 2 E/ViewGroup1: ------------------ dispatchTouchEvent = 2 E/MainActivity: ---------------- dispatchTouchEvent = 2 E/ViewGroup1: ------------------ dispatchTouchEvent = 2 E/MainActivity: ---------------- dispatchTouchEvent = 2 E/ViewGroup1: ------------------ dispatchTouchEvent = 2 E/MainActivity: ---------------- dispatchTouchEvent = 1 E/ViewGroup1: ------------------ dispatchTouchEvent = 1 ```ide
观察 |
---|
能够看出事件传递到ViewGroup1后被拦截,没有被任何布局消费 |
也就是说事件还没被消费就被拦截会致使触摸无效 |
咱们能够在dispatchTouchEvent判断哪些状况须要拦截,哪些不须要拦截就放事件过去(以上直接返回了true拦截了全部状况的事件) |
经过onInterceptTouchEvent获取事件,当返回值为true
的时候获取事件布局
获取事件后会调用onTouchEvent方法,调用这个方法后,若是咱们设置了OnTouchListener,那么触摸监听将会被调用。 如今以ViewGroup2为例: 让ViewGroup2中的onInterceptTouchEvent直接返回true 当手指对View1点击、移动、抬起时 发生的一系列事件传递(0.按下;1.抬起; 2.移动)ui
E/MainActivity: ---------------- dispatchTouchEvent = 0
E/ViewGroup1: ------------------ dispatchTouchEvent = 0
E/ViewGroup1: ------------------ onInterceptTouchEvent = 0
E/ViewGroup2: ------------------ dispatchTouchEvent = 0
E/ViewGroup2: ------------------ onInterceptTouchEvent = 0
E/ViewGroup2: ------------------ onTouchEvent = 0
E/ViewGroup1: ------------------ onTouchEvent = 0
E/MainActivity: ---------------- onTouchEvent = 0
E/MainActivity: ---------------- dispatchTouchEvent = 2
E/MainActivity: ---------------- onTouchEvent = 2
E/MainActivity: ---------------- dispatchTouchEvent = 2
E/MainActivity: ---------------- onTouchEvent = 2
E/MainActivity: ---------------- dispatchTouchEvent = 2
E/MainActivity: ---------------- onTouchEvent = 2
E/MainActivity: ---------------- dispatchTouchEvent = 1
E/MainActivity: ---------------- onTouchEvent = 1
复制代码
观察问题 |
缘由 |
解决 |
---|---|---|
哎呀呀~!为啥我获取到了的事件以后,移动和抬起手指的事件被MainActivity吃了!愤怒!! |
原来onTouchEvent若是处理按下事件DOWN的时候没有返回true。若是onTouchEvent处理DOWN时候返回false,则表示没有消费事件,事件将会回到父布局,而且后续事件将不会再传递过来。 |
onTouchEvent方法中判断为按下DOWN事件的时候,返回true即下面要说的消费 |
上边已经提到过,就是获取事件遗留下来一个问题:获取到了按下事件,为啥没继续获取到后续的事件?就是由于按下时onTouchEvent没有返回true,致使事件重新回到父布局,也就是没有消费事件。this
如今接着以ViewGroup2为例:
仍是让ViewGroup2中的onInterceptTouchEvent直接返回true
添加:在onTouchEvent方法中添加判断if (event.getAction() == MotionEvent.ACTION_DOWN) {return true;}
当手指对View1点击、移动、抬起时
发生的一系列事件传递(0.按下;1.抬起; 2.移动)
复制代码
复制代码
E/MainActivity: ---------------- dispatchTouchEvent = 0 E/ViewGroup1: ------------------ dispatchTouchEvent = 0 E/ViewGroup1: ------------------ onInterceptTouchEvent = 0 E/ViewGroup2: ------------------ dispatchTouchEvent = 0 E/ViewGroup2: ------------------ onInterceptTouchEvent = 0 E/ViewGroup2: ------------------ onTouchEvent = 0 E/MainActivity: ---------------- dispatchTouchEvent = 2 E/ViewGroup1: ------------------ dispatchTouchEvent = 2 E/ViewGroup1: ------------------ onInterceptTouchEvent = 2 E/ViewGroup2: ------------------ dispatchTouchEvent = 2 E/ViewGroup2: ------------------ onTouchEvent = 2 E/MainActivity: ---------------- onTouchEvent = 2 E/MainActivity: ---------------- dispatchTouchEvent = 2 E/ViewGroup1: ------------------ dispatchTouchEvent = 2 E/ViewGroup1: ------------------ onInterceptTouchEvent = 2 E/ViewGroup2: ------------------ dispatchTouchEvent = 2 E/ViewGroup2: ------------------ onTouchEvent = 2 E/MainActivity: ---------------- onTouchEvent = 2 E/MainActivity: ---------------- dispatchTouchEvent = 1 E/ViewGroup1: ------------------ dispatchTouchEvent = 1 E/ViewGroup1: ------------------ onInterceptTouchEvent = 1 E/ViewGroup2: ------------------ dispatchTouchEvent = 1 E/ViewGroup2: ------------------ onTouchEvent = 1 E/MainActivity: ---------------- onTouchEvent = 1
| `观察`|
|-|
|`由上边log能够看出,如今在ViewGroup2中的onTouchEvent的按下事件返回一个true后,按下事件并无在传递回父布局中,使得后续事件都将能获得`|
|`能够看出当后续事件传递过来时,ViewGroup2已经没有再次调用onInterceptTouchEvent方法`|
|`咱们只是将按下DOWN的事件返回true,因此除了按下事件其余移动或抬起的事件activity都也能获取到。当onTouchEvent无论三七二十一直接返回一个true时,activity就不会获取到事件`|


### Button获取事件是怎么回事?
- 如今将View1换成Button1,其余的恢复最初状态,先来看看触摸的log
复制代码
E/MainActivity: ------------- dispatchTouchEvent = 0 E/ViewGroup1: --------------- dispatchTouchEvent = 0 E/ViewGroup1: --------------- onInterceptTouchEvent = 0 E/ViewGroup2: --------------- dispatchTouchEvent = 0 E/ViewGroup2: --------------- onInterceptTouchEvent = 0 E/Button1: ------------------ dispatchTouchEvent = 0 E/Button1: ------------------ onTouchEvent = 0 E/MainActivity: ------------- dispatchTouchEvent = 2 E/ViewGroup1: --------------- dispatchTouchEvent = 2 E/ViewGroup1: --------------- onInterceptTouchEvent = 2 E/ViewGroup2: --------------- dispatchTouchEvent = 2 E/ViewGroup2: --------------- onInterceptTouchEvent = 2 E/Button1: ------------------ dispatchTouchEvent = 2 E/Button1: ------------------ onTouchEvent = 2 E/MainActivity: ------------- dispatchTouchEvent = 2 E/ViewGroup1: --------------- dispatchTouchEvent = 2 E/ViewGroup1: --------------- onInterceptTouchEvent = 2 E/ViewGroup2: --------------- dispatchTouchEvent = 2 E/ViewGroup2: --------------- onInterceptTouchEvent = 2 E/Button1: ------------------ dispatchTouchEvent = 2 E/Button1: ------------------ onTouchEvent = 2 E/MainActivity: ------------- dispatchTouchEvent = 1 E/ViewGroup1: --------------- dispatchTouchEvent = 1 E/ViewGroup1: --------------- onInterceptTouchEvent = 1 E/ViewGroup2: --------------- dispatchTouchEvent = 1 E/ViewGroup2: --------------- onInterceptTouchEvent = 1 E/Button1: ------------------ dispatchTouchEvent = 1 E/Button1: ------------------ onTouchEvent = 1
- 在来看看序列图

- 这一看,这不是和上面那张图`当ViewGroup2中onTouchEvent直接返回true时`的效果同样的吗?也就是说button默认就是直接获取了事件,没有让事件返回主布局中。
- 等等还有一个!!!你们都知道布局有个属性**clickable**吧!当设置它的值为true时,使得这个布局事件如button所述!
- 更深刻的理解的话这里博客已经介绍的很详细了
- [Android事件分发机制彻底解析,带你从源码的角度完全理解(上)](http://blog.csdn.net/guolin_blog/article/details/9097463)
- [Android事件分发机制彻底解析,带你从源码的角度完全理解(下)](http://blog.csdn.net/guolin_blog/article/details/9153747)
### 实际的应用
-
来一个简单的应用
xml布局
复制代码
activity代码
复制代码
package com.examples.customtouch;
import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.CheckBox;
/**
Created by Dave Smith
Double Encore, Inc.
Date: 9/25/12
TouchListenerActivity */ public class TouchListenerActivity extends Activity implements View.OnTouchListener {
/* Views to display last seen touch event */ CheckBox mLockBox;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.touch_listener);
mLockBox = (CheckBox) findViewById(R.id.checkbox_lock);
findViewById(R.id.selection_first).setOnTouchListener(this);
findViewById(R.id.selection_second).setOnTouchListener(this);
findViewById(R.id.selection_third).setOnTouchListener(this);
复制代码
}
@Override public boolean onTouch(View v, MotionEvent event) { /* * Consume the events here so the buttons cannot process them * if the CheckBox in the UI is checked */ Log.e("TouchListenerActivity", getNameForEvent(event)); return mLockBox.isChecked(); }
@Override public boolean onTouchEvent(MotionEvent event) { Log.e("onTouchEvent", getNameForEvent(event)); return super.onTouchEvent(event); }
private String getNameForEvent(MotionEvent event) { String action = ""; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: action = "ACTION_DOWN"; break; case MotionEvent.ACTION_CANCEL: action = "ACTION_CANCEL"; break; case MotionEvent.ACTION_MOVE: action = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: action = "ACTION_UP"; break; default: return null; }
return String.format("%s\n%.1f, %.1f", action, event.getX(), event.getY());
复制代码
} }

|`状态`|`描述`|
|:-:|:-:|
|`当Lock Selection没有勾选时`|`下边的单选能正常选择`|
|`当Lock Selection勾选时`|`下边的单选没法点击`|
- 你们是否疑惑了,为何复选框选中状态,ontouch返回了true反而不能点击了。为何不是返回false没法点击,返回true时才能点击呢?其实这些控件默承认以点击的都是默认获取事件的,如上面说的button为何获取事件同样,因此返回true和false和预想的结果相反。
#所用知识和资料
1. Android studio插件plantUml画序列图和类图
2. [PlantUML快速指南](http://archive.3zso.com/archives/plantuml-quickstart.html#sec-5-3) 和 [PlantUML官网](http://plantuml.com/classes.html)
3. [Android事件分发机制彻底解析,带你从源码的角度完全理解(上)](http://blog.csdn.net/guolin_blog/article/details/9097463)
[Android事件分发机制彻底解析,带你从源码的角度完全理解(下)](http://blog.csdn.net/guolin_blog/article/details/9153747)
4. [公共技术点之 View 事件传递](http://a.codekk.com/detail/Android/Trinea/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20View%20%E4%BA%8B%E4%BB%B6%E4%BC%A0%E9%80%92)
5. [最后的那个例子来自于这儿](https://github.com/devunwired/custom-touch-examples)
复制代码