-
触摸事件的类型
触摸事件就是捕获触摸屏幕后产生的事件,比如当点击一个按钮会产生----按钮按下,这是事件一,如果不小心滑动了,这是事件二,当手指抬起的时候,这是事件三。安卓提供了MotionEvent类,封装了触摸事件的类型。- ACTION_DOWN:用户手指的按下操作,一个按下操作代表着一次触摸事件的开始。
- ACTION_MOVE:用户手指移动的操作,一般情况下,手指的轻微移动都会触发该方法。
- ACTION_UP:手指抬起操作,一次抬起代表着一次触摸事件和结束。
-
事件传递的三个方法
在事件传递具体实现之前,我们说下事件传递的三个方法- 事件分发方法
public boolean dispatchTouchEvent(MotionEvent event)
在安卓系统中,所有的触摸事件都是通过这个方法来分发的,在这个方法中,根据当前业务的实现逻辑,来决定是否消费这个事件还是将事件继续分发给子视图处理,方法返回值是true表示将当前事件消费掉,不再继续分发,当方法返回值是super.dispatchTouchEvent表示继续分发事件,当方法返回值是false,由父View消费事件。
- 事件拦截
public boolean onInterceptTouchEvent(MotionEvent event);
这个方法只有在ViewGroup及其子类中才存在,在View和Activity中是不存在的。返回true表示拦截这个事件,不继续分发给子视图,同时由自身的onTouchEvent方法进行消费,返回false或者super.onInterceptTouchEvent表示不对事件进行拦截,会继续将事件传递给子视图。
- 事件处理
public boolean onTouchEvent(MotionEvent event)
该方法返回true表示当前视图可以处理对应的事件,事件将不会向上传递给父视图中的onTouchEvent方法,返回值为false或者super.onTouchEvent(MotionEvent event)表示当前视图不处理这个事件,事件将会传递给父视图中的onTouchEvent方法进行处理。
-
在Android系统中
Activity:可以重写dispatchTouchEvent和onTouchEvent方法。 ViewGroup:可以重写dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个方法。 View:可以重写dispatchTouchEvent和onTouchEvent方法。
-
View的事件传递机制
上面已经说了View中可以重写dispatchTouchEvent和onTouchEvent方法,虽然ViewGroup的父类是View,但View中是没有onInterceptTouchEvent方法的。
我们将自定义一个MyView1,在事件处理相关的方法中打印Log,来观察事件处理的流程。
public class MyView1 extends View{
private static final String TAG = "eventdemo";
public MyView1(Context context) {
super(context);
}
public MyView1(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_UP");
break;
default:
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.i(TAG, getClass().getSimpleName() + " onTouchEvent");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
同时在Activity中设置点击和触摸回调:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {
private static final String TAG = "eventdemo";
private View myView1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myView1 = findViewById(R.id.myview1);
myView1.setOnClickListener(this);
myView1.setOnTouchListener(this);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// Log.i("eventdemo", getClass().getSimpleName() + " dispatchTouchEvent");
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " dispatchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " dispatchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " dispatchEvent ACTION_UP");
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.i("eventdemo", getClass().getSimpleName() + " onTouchEvent");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public void onClick(View v) {
Log.i(TAG, "myView1 onClick");
}
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG, "myView1 onTouch");
return false;
}
}
run代码点击MyView1后打印:
- ViewGroup的事件传递机制
我们将自定义一个MyGroupView1里面包含MyView1,在事件处理方法中打印Log,观察ViewGroup中的事件处理流程
public class MyGroupView1 extends FrameLayout {
private static final String TAG = "eventdemo";
public MyGroupView1(@NonNull Context context) {
super(context);
}
public MyGroupView1(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyGroupView1(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent");
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " dispatchTouchEvent ACTION_UP");
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " onInterceptTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " onInterceptTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " onInterceptTouchEvent ACTION_UP");
break;
default:
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, getClass().getSimpleName() + " onTouchEvent ACTION_UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
run代码点击MyView1后打印:
这里的onInterceptTouchEvent方法没有打印出来,是由于设置了onClickListener消费了事件导致的。
从上面观察的Log看我们可以得出:
- 触摸事件的传递顺序是从Activity到ViewGroup,再由ViewGroup分发给子View的。
- ViewGroup会通过onInterceptTouchEvent方法对事件选择是否拦截,如果该方法返回true,则事件将会被拦截并由ViewGrup中的onTouchEvent处理。否则事件将会继续传递到子View中。
- 在子View中对事件进行消费后,ViewGroup将接收不到任何事件。