View的事件分发牵涉到以下三个方法:
/*用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。*/
dispatchTouchEvent(MotionEvent event);
/*是dispatchTouchEvent的内部方法,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列中,此方法不会被再次调用,返回结果表示是否拦截当前事件*/
onInterceptTouchEvent(MotionEvent event);
/*在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件*/
onTouchEvent(MotionEvent event);
//上述方法的区别,可以用如下伪代码来表示:
public boolean
根据一个Button的点击事件来看下事件的分发,代码如下:
一个继承Button的自定义Button,并写好相应事件的log
public class MyButton extends Button {
private static final String TAG = MyButton.class.getSimpleName();
public MyButton(Context context,AttributeSet attrs) {
super(context,attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent: ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchEvent: ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent: ACTION_MOVE");
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent: ACTION_DOWN" );
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "dispatchTouchEvent: ACTION_UP" );
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent: ACTION_MOVE" );
break;
}
return super.dispatchTouchEvent(event);
}
}
xml中:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.sunfuyi.studytest.view.MyButton
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="button"></com.sunfuyi.studytest.view.MyButton>
</LinearLayout>
Activity中:
public class TestViewActivity extends Activity {
private static final String TAG = "MyButton";
TestView tv;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testview);
MyButton button = (MyButton) findViewById(R.id.button1);
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouch ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouch ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouch ACTION_UP");
break;
default:
break;
}
return false;
}
});
}
}
我们点击button,并查看log,得到结果如下:
执行顺序
首先:<b>dispatchTouchEvent</b>
其次:<b>setOnTouchListener的onTouch回调</b>
再次:<b>onTouchEvent</b>
04-05 02:49:52.168 6673-6673/com.sunfuyi.studytest E/MyButton: dispatchTouchEvent: ACTION_DOWN
04-05 02:49:52.168 6673-6673/com.sunfuyi.studytest E/MyButton: onTouch ACTION_DOWN
04-05 02:49:52.168 6673-6673/com.sunfuyi.studytest E/MyButton: onTouchEvent: ACTION_DOWN
04-05 02:49:52.254 6673-6673/com.sunfuyi.studytest E/MyButton: dispatchTouchEvent: ACTION_UP
04-05 02:49:52.254 6673-6673/com.sunfuyi.studytest E/MyButton: onTouch ACTION_UP
04-05 02:49:52.254 6673-6673/com.sunfuyi.studytest E/MyButton: onTouchEvent: ACTION_UP
下面我们来看看源码:
/**
* Pass the touch screen motion event down to the target view, or this
* view if it is the target.
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//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;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
以上代码有点长,我们直接过滤掉无关代码,直接看我们需要的:
if (onFilterTouchEventForSecurity(event)) {
//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;
}
}
以上代码我们来简单分析下,
首先查看 onFilterTouchEventForSecurity(event)方法:
//从源码注释我们可以看出该方法是 (过滤触摸事件的安全策略)
//参数 MotionEvent,需要过滤的事件
//return 如果事件应该被分发,则为真,如果事件应该被丢弃,则为假。
/**
* Filter the touch event to apply security policies.
*
* @param event The motion event to be filtered.
* @return True if the event should be dispatched, false if the event should be dropped.
*
* @see #getFilterTouchesWhenObscured
*/
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
// Window is obscured, drop this touch.
return false;
}
return true;
}
这里默认返回 true,直接进入该if,我们看到。
首先判断mOnTouchListener不为null,并且view是enable的状态,然后 mOnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ;如果onTouch返回的是false,那么第一个if不成立,就会走第二个if;就会调用onTouchEvent(event)方法。
网友评论