项目源码
public class MyLayout extends FrameLayout {
private static final String TAG = MyLayout.class.getName();
public MyLayout(Context context) {
super(context);
}
public MyLayout(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "MyLayout onInterceptTouchEvent() ");
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "MyLayout onTouchEvent() ");
LogEventUtil.logAction(event, TAG);
return true ;
}
}
public class MyButton extends Button {
private static final String TAG = MyButton.class.getName();
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "MyButton onTouchEvent " + super.onTouchEvent(event) );
LogEventUtil.logAction(event, TAG);
//return super.onTouchEvent(event);
return false;
}
}
public class LogEventUtil {
public static void logAction(MotionEvent event, final String tag) {
int action = event.getAction();
switch(action) {
case MotionEvent.ACTION_DOWN:
Log.d(tag, "Action down");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(tag, "Action cancel");
break;
case MotionEvent.ACTION_UP:
Log.d(tag, "Action up");
break;
case MotionEvent.ACTION_MOVE:
Log.d(tag, "Action move");
break;
default:
Log.d(tag, "unknow action");
}
}
}
MyLayout(GroupView) 中方法的说明
* public boolean onInterceptTouchEvent(MotionEvent ev)
默认调用 super.onInterceptTouchEvent(ev),该方法返回 false。不拦截TouchEvent
* public boolean onTouchEvent(MotionEvent event)
默认调用 super.onTouchEvent(ev),该方法返回 false。
根据MyLayout , 运行实例说明,从 onInterceptTouchEvent 中进行说明
* Tag1 点击MyButton 运行路径,正常执行路径
1. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
2. D/com.kf.knowledge.accumulation.ui.event.MyButton: MyButton onTouchEvent
3. D/com.kf.knowledge.accumulation.ui.event.MyButton: Action down
4. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
5. D/com.kf.knowledge.accumulation.ui.event.MyButton: MyButton onTouchEvent
6. D/com.kf.knowledge.accumulation.ui.event.MyButton: Action up
* Tag2 点击MyLayout区域,正常执行路径
1. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
2. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent() false
3. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action down
* Tag3 点击MyButton ,修改 MyLayout中 onInterceptTouchEvent() 方法,返回 **true**,运行路径
1. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
2. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent() false
3. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action down
由上面运行实例,可以得知。MyLayout 装它的容器,的onInterceptTouchEvent 返回是true。
当 MyLayout 的方法 onInterceptTouchEvent 返回是默认情况false,则不拦截该事件的传递。
当 MyLayout 的方法 onInterceptTouchEvent 返回是true,MyButton进行拦截,在OnTouch 的 Down中消费了。
根据MyLayout , 运行实例说明,从onTouchEvent() 中进行说明
* MyButton 中 onTouchEvent() super.onTouchEvent(event) ,默认返回:true
* MyLayout 中 onTouchEvent() super.onTouchEvent(event) ,默认返回:false
* MyLayout 中 onInterceptTouchEvent() super.onInterceptTouchEvent() ,默认返回:false
* Tag4 设定 MyLayout 中 onInterceptTouchEvent 返回 **true** , onTouchEvent() 返回 **true**
1. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
2. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent()
3. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action down
4. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent()
5. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action up
> 由这个实例可以得到,这个点击事件完全是给MyLayout消费了。
* Tag5 MyLayout使用默认,MyButton onTouchEvent 返回**false**
1. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
2. D/com.kf.knowledge.accumulation.ui.event.MyButton: MyButton onTouchEvent true
3. D/com.kf.knowledge.accumulation.ui.event.MyButton: Action down
4. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent()
5. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action down
> 可以看到:容器没有拦截 -> 传递给控件 -> 控件使用了 onTouchEvent事件 -> 控件返回false,也就是不在消费该事件流了 ->
传递给容器 -> 容器同样返回false,也不消费其他事件流 -> 传递给上一层的容器 -> 都不消费 -> End
* Tag6 MyLayout 消费事件。MyLayout onTouchEvent() 返回 **true** , MyButton onTouchEvent 返回**false**
1. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onInterceptTouchEvent()
2. D/com.kf.knowledge.accumulation.ui.event.MyButton: MyButton onTouchEvent true
3. D/com.kf.knowledge.accumulation.ui.event.MyButton: Action down
4. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent()
5. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action down
6. D/com.kf.knowledge.accumulation.ui.event.MyLayout: MyLayout onTouchEvent()
7. D/com.kf.knowledge.accumulation.ui.event.MyLayout: Action up
> 可以看到:容器没有拦截 -> 传递给控件 -> 控件使用了 onTouchEvent事件 ->
控件返回false,也就是不在消费该事件流了 ->传递给容器 -> 容器同样返回true,消费其他事件流 -> End
Android中点击传递事件的设计实现
* 点击MyLayout区域,这个流程可以看出。在API中实现的,所有都没有拦截,默认都不消费方法。
* 一个正常的点击事件包括:ACTION_DOWN -> ACTION_MOVE -> ACTION_UP
* **首先监测父类是否使用onInterceptTouchEvent拦截** ,如果拦截后,所有事件都在当前的容器中消费事件。
* 全面理解事件传递的执行与消费
1. 容器拦截后,使用该事件,需要在onTouchEvent()中返回true,使用其所有事件。Tag4 运行事件中表现出来。
2. 容器拦截后,不使用该事件,需要在onTouchEvent()中返回false,那么只会消费 ACTION_DOWN事件。Tag3 运行事件中表现出来。
3. 容器不拦截,控件使用所有事件。Tag1 运行事件中表现出来。
4. 容器不拦截,控件不使用事件。Tag5 运行事件中表现出来。
5. 容器不拦截,容器控件消费事件。控件不消费事件。Tag6 运行事件中表现出来。
源码下载地址
https://github.com/forwardfly/AndroidAccumulation.git