View的Touch事件分发
1. 说明
这节课我们就来分析系View的Touch事件分发的顺序。
2. 分析
public class MainActivity extends AppCompatActivity {
private TouchView touch_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touch_view = (TouchView) findViewById(R.id.touch_view);
touch_view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG" , "触摸onTouch() -> " + event.getAction()) ;
return true;
}
});
touch_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG" , "点击onClick -> ") ;
}
});
}
}
public class TouchView extends View {
public TouchView(Context context) {
super(context);
}
public TouchView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public TouchView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG" , "onTouchEvent -> " + event.getAction()) ;
return super.onTouchEvent(event);
}
}
现象一:onTouchListener、onTouch、onClick三个都有的情况下,并且onTouchListener返回false,表示自己不处理事件,直接把事件分发下去给子控件,所以3个方法都会执行;
03-25 08:18:36.589 3344-3344/com.jackchen.view_day010_2 E/TAG: 触摸onTouch() -> 0
03-25 08:18:36.589 3344-3344/com.jackchen.view_day010_2 E/TAG: onTouchEvent -> 0
03-25 08:18:36.672 3344-3344/com.jackchen.view_day010_2 E/TAG: 触摸onTouch() -> 2
03-25 08:18:36.672 3344-3344/com.jackchen.view_day010_2 E/TAG: onTouchEvent -> 2
03-25 08:18:36.851 3344-3344/com.jackchen.view_day010_2 E/TAG: 触摸onTouch() -> 1
03-25 08:18:36.851 3344-3344/com.jackchen.view_day010_2 E/TAG: onTouchEvent -> 1
03-25 08:18:36.852 3344-3344/com.jackchen.view_day010_2 E/TAG: 点击onClick ->
由以上可知:0代表DOWN、2代表MOVE、1代表UP,执行顺序如下:
onTouchListener的DOMN -> onTouch的DOWN ->
onTouchListener的MOVE -> onTouch的MOVE ->
onTouchListener的UP -> onTouch的UP ->
onClick
现象二:onTouchListener、onTouch、onClick三个都有的情况下,并且onTouchListener返回true,表示自己处理事件,就不会把事件分发给子控件,所以就只会执行自己而方法;
03-25 08:25:30.493 23991-23991/? E/TAG: 触摸onTouch() -> 0
03-25 08:25:30.625 23991-23991/? E/TAG: 触摸onTouch() -> 2
03-25 08:25:30.625 23991-23991/? E/TAG: 触摸onTouch() -> 1
执行顺序如下:
onTouchListener的DOWN -> onTouchListener的MOVE -> onTouchListener的UP
现象三:只有 onTouchEvent()、onClickListener(),前提是onTouchEvent返回true,表示自己处理事件,就不会把事件分发下去,所以就只执行自己的方法,不会执行下边的方法;
03-25 09:19:11.648 3005-3005/? E/TAG: onTouchEvent -> 0
03-25 09:19:11.752 3005-3005/? E/TAG: onTouchEvent -> 2
03-25 09:19:11.752 3005-3005/? E/TAG: onTouchEvent -> 1
执行顺序如下:
onTouchEvent的DOWN -> onTouchEvent的MOVE -> onTouchEvent的UP,不会执行onClick事件
onClick不执行的原因:
因为onClick事件是在 View中的 onTouchEvent中的 case MotionEvent.ACTION_UP:里边调用了performClick(),而这里onTouchEvent返回的是true,而不是super.onTouchEvent(event),所以就不会执行View中的方法,所以就不会执行 View中的 onTouchEvent中的 case MotionEvent.ACTION_UP,所以onClick不会调用
现象四: onTouchListener onTouchEvent onClick dispatchTouchEvent 都有,前提是dispatchTouchEvent返回true,那么一个方法都不会执行;如果dispatchTouchEvent返回true,并且添加super.dispatchTouchEvent(event) ;那么现象和现象一是一样的。
public class MainActivity extends AppCompatActivity {
private TouchView touchView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
touchView = (TouchView) findViewById(R.id.touch_view);
touchView.setEnabled(false); //touchView不可用
touchView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG" , "onTouch -> "+event.getAction()) ;
return false;
}
});
touchView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG" , "onClick") ;
}
});
}
}
public class TouchView extends View {
public TouchView(Context context) {
this(context, null);
}
public TouchView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TouchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG" , "onTouchEvent -> "+event.getAction()) ;
return super.onTouchEvent(event);
}
/**
* 复写 dispatchTouchEvent作用就是,在onTouchEvent()方法中可以返回任何值,return false、return true、return super.onTouchEvent(event)都是可以的
* 但是在 dispatchTouchEvent必须返回true,并且添加super.dispatchTouchEvent(event) ,这样的话现象就是现象一
* @param event
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
super.dispatchTouchEvent(event) ;
Log.e("TAG" , "dispatchTouchEvent -> "+event.getAction()) ;
return true;
}
}
3. View和Touch相关的有2个非常重要的方法
3.1>:dispatchTouchEvent()事件分发:
源码分析:
在View源码中的 dispatchTouchEvent()方法中:
public boolean dispatchTouchEvent(MotionEvent event) {
// result:默认是false
boolean result = false;
// ListenerInfo:里边存放了 View的所有的 Listener信息
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;
}
以上中:
boolean result = false;
ListenerInfo 很重要,它里边存放了View的所有的Listener信息,比如:onTouchListener、onClickListener
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
分析以上代码可知:
li为true,li.mOnTouchListener != null 为true
(mViewFlags & ENABLED_MASK) == ENABLED 是否是ENABLE可用的
li.mOnTouchListener.onTouch(this, event):
如果返回false,说明进不去if语句,那么就直接用初始化的result = false的值;
如果返回true,那么 result = true ;
if (!result && onTouchEvent(event)) {
result = true;
}
分析以上代码可知:
对于if (!result && onTouchEvent(event)) :
如果上边的result为false,那么 !result为true,那么就会执行后边的 onTouchEvent()方法了;
如果 result为true,那么 !result为false,那么就不会执行后边的 onTouchEvent()方法了;
到目前来讲,我们都还没有看到点击事件?
在View的onTouchEvent中的 case MotionEvent.ACTION_UP里边调用了 performClick()方法,点击 performClick()进入里边方法后,具体代码是:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
分析:因为onTouchEvent返回true,即return true ,而没有return super.onTouchEvent(event),所以就不会执行View,更不会执行View中的onTouchEvent中的case MotionEvent.ACTION_UP,所以就只会执行 onTouchEvent()的Down、Move、Up事件,不会执行onClick()方法了;
3.2>:onTouchEvent(),这个方法一般都被我们复写,并且直接return true即可;
注意:
注意:自定义View是没有 onInterceptTouchEvent() 拦截方法的
4. 总结如下:
- 如果onTouchListener返回false,表示不处理事件,把事件分发给子控件,让下级处理,执行顺序就是:
onTouchListener、onTouchEvent、onClickListener - 如果onTouchListener返回true,表示自己处理事件,就不会把事件分发给子控件,所以就只会执行onTouchListener