Android-点击事件分发机制验证

曾经终败给现在 2022-08-22 12:29 189阅读 0赞

简介


点击事件的事件分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生之后,系统需要这个事件传递给一个具体的View,而这个传递过程就是分发过程。

点击事件的分发过程由三个重要方法共同完成:

  • dispatchTouchEvent 事件分发
  • onInterceptTouchEvent 事件拦截
  • onTouchEvent 事件响应

方法介绍


public boolean dispatchTouchEvent(MotionEvent ev)

用来进行事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受到当前View的onTouchEvent和下级View的方法的影响。

  • 如果return true,表示点击事件被消耗掉。
  • 如果return false,表示点击事件没有被消耗

public boolean onInterceptTouchEvent(MotionEvent ev)

用来判断是否拦截某个事件。

  • 如果return true,表示点击事件被拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理。
  • 如果return false,表示点击事件未被拦截,View上的事件会被传递到子View上,由子View的dispatchTouchEvent来处理。

public boolean onTouchEvent(MotionEvent ev)

用来处理点击事件,返回结果表示是否消耗当前事件。

  • 如果return true,表示点击事件被接收并消费。
  • 如果return false,表示点击事件未被消费。

三个方法之间的关系

public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);

} else {

if(hasChild()){

consume = child.dispatchTouchEvent(ev);

} else {

consume = onTouchEvent(ev);

}

}

return consume;
}

代码实践


界面UI布局

Center

ParentLayout

后面例子,都是在此代码的基础上修改的

public class ParentLayout extends LinearLayout {

public ParentLayout( Context context) { super(context);}

public ParentLayout( Context context, AttributeSet attrs) { super(context, attrs);}

public ParentLayout( Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);}

@Override
public boolean dispatchTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “default: false”));

return super .dispatchTouchEvent(ev);
}

@Override
public boolean onInterceptTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “default: false”));

return super .onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “default: false”));

return super .onTouchEvent(ev);
}
}

ChildView

后面例子,都是在此代码的基础上修改的

public class ChildView extends TextView {

public ChildView( Context context) { super(context);}

public ChildView( Context context, AttributeSet attrs) { super(context, attrs);}

public ChildView( Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);}

@Override
public boolean dispatchTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “default: false”));

return super .dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “default: false”));

return super .onTouchEvent(ev);
}
}

打印结果

点击ChildView(1处),打印Log

Center 1

点击ParentView(2处),打印Log

Center 2

处理过程解释

代码实践这种情况下,这三个方法,默认都发送false。对于三个方法return的介绍参考前面方法介绍。这里,我们以点击ChildView为例子,再次强调点击实践的分发流程:

dispatchTouchEvent方法再探" class="reference-link">Center 3dispatchTouchEvent方法再探

事件分发-false

在ChildView代码基础上,将dispatchTouchEvent方法内容改为如下形式

public class ChildView extends TextView {
. ..
@Override
public boolean dispatchTouchEvent( MotionEvent ev) {
ABLog .e( “Default Return “ + String .valueOf( “false”));
return false;

}

点击ChildView(1处),打印Log

Center 4

事件分发-true

在ChildView代码基础上,将dispatchTouchEvent方法内容改为如下形式

public class ChildView extends TextView {

@Override
public boolean dispatchTouchEvent( MotionEvent ev) {
ABLog .e( “Default Return “ + String .valueOf( “true”));
return true;

}

点击ChildView(1处),打印Log

触发两次Log打印?因为当ChildView的dispatchTouchEvent返回true时,这就表示一系列点击事件都由ChildView处理,所以,两次Log分别为手势的Down和Up事件。而如果返回false时,这就表示一系列点击事件不由ChildView处理,所以,只打印一次。

Center 5

onTouchEvent方法再探

事件响应-false

在ChildView代码基础上,将onTouchEvent方法内容改为如下形式

public class ChildView extends TextView {

@Override
public boolean onTouchEvent( MotionEvent ev) {
ABLog .e( “Default Return “ + String .valueOf( “false”));
return false;
}

点击ChildView(1处),打印Log

和默认效果一样,因为默认也是返回false。

Center 6

事件响应-true

在ChildView代码基础上,将onTouchEvent方法内容改为如下形式

public class ChildView extends TextView {

@Override
public boolean onTouchEvent( MotionEvent ev) {
ABLog .e( “Default Return “ + String .valueOf( “true”));
return true;
}

点击ChildView(1处),打印Log

触发两次Log打印?原因同上,这一系列事件都有ChildView出发。

Center 7

onInterceptTouchEvent方法再探

事件拦截-false

在ParentLayout代码基础上,将onInterceptTouchEvent方法内容改为如下形式

public class ParentLayout extends LinearLayout {

@Override
public boolean onInterceptTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “false”));
return false;

}

点击ChildView(1处),打印Log

Center 8

点击ParentView(2处),打印Log

Center 9

总结一下上面Log情况?Log结果都和一开始全部默认情况相同。onInterceptTouchEvent为false的情况,就是调用super.onInterceptTouchEvent的情况。onInterceptTouchEvent返回false,表示点击事件不拦截,向子View传递。

事件拦截-true 事件响应-false

在ParentLayout代码基础上,将ParentLayout改为如下形式

publicclassParentLayoutextendsLinearLayout {

@Override
publicbooleandispatchTouchEvent(MotionEventev){
ABLog.e(“Default Return “+String.valueOf(“default: false”));

returnsuper.dispatchTouchEvent(ev);
}

@Override
publicbooleanonInterceptTouchEvent(MotionEventev){
ABLog.e(“Default Return “+String.valueOf(“true”));
returntrue;
}

@Override
publicbooleanonTouchEvent(MotionEventev){

ABLog.e(“Default Return “+String.valueOf(“false”));

returnfalse;
}

}

点击ChildView(1处)或者ParentLayout(2处),打印Log

为什么点击ChildView和ParentLayout显示相同Log?因为MotionEvent一系列事件,已经被onInterceptTouchEvent拦截了,所以MotionEvent一系列事件将不会被传递到子View(ChildView)中。

为什么拦截了点击事件,但是Log只出现了一次?因为使用onInterceptTouchEvent拦截了点击事件,只是拦截了点击事件的传递,并没有处理MotionEvent的一系列事件。更进一步说,只有dispatchTouchEvent方法返回true,才表示MotionEvent的一系列事件被处理。onTouchEvent方法返回true,只是间接的对dispatchTouchEvent产生了影响。

Center 10

事件拦截-true 事件响应-true

在ParentLayout代码基础上,将ParentLayout改为如下形式

public class ParentLayout extends LinearLayout {
@Override
public boolean dispatchTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “default: false”));

return super .dispatchTouchEvent(ev);
}

@Override
public boolean onInterceptTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “true”));
return true;
}

@Override
public boolean onTouchEvent( MotionEvent ev){
ABLog .e( “Default Return “ + String .valueOf( “true”));
return true;
}

}

点击ChildView(1处)或者ParentLayout(2处),打印Log

为什么Log和之前看到的不一样?对于这次的Log,其实还是包含了点击事件Down和Up。在这我们用分割线分开,第一部分,出现了dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent三个函数;第二部分,因为onInterceptTouchEvent(拦截方法),只被调用一次哦,所以只出现了dispatchTouchEvent, onTouchEvent两个函数。

Center 11

View中Listener的优先级


主要探究OnTouchListener和OnClickListener还有onTouchEvent三个方法之间的关系:

Center 12

界面

Center 13

ChildView

public class ChildView extends TextView {

public ChildView( Context context) {
super(context);
}

public ChildView( Context context, AttributeSet attrs) {
super(context, attrs);
}

public ChildView( Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);}

@Override
public boolean onTouchEvent( MotionEvent ev) {
ABLog .e( “Default Return “ + String .valueOf( “false”));
return super .onTouchEvent(ev);
}
}

MainActivity

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate(savedInstanceState);
setContentView( R . layout . activity_main);

ChildView view = ( ChildView )findViewById( R . id . myChildView);
view . setOnClickListener( new View . OnClickListener () {
@Override
public void onClick ( View v ) {
ABLog . e( “OnClickListener” );
}
});

view . setOnTouchListener( new View . OnTouchListener () {
@Override
public boolean onTouch ( View v , MotionEvent event ) {
ABLog . e( “setOnTouchListener” );
return false ;
}
});
}

}

点击ChildView(1处),打印Log

前一部分,为Action_Down触发;后一部分,为Action_Up触发。

Center 14

onTouch方法再探

将onTouch方法返回值改为true,表示处理点击时间的一系列事件,将setOnTouchListener方法改为:

view .setOnTouchListener( new View. OnTouchListener() {
@Override
public boolean onTouch( View v, MotionEvent event) {

ABLog .e( “setOnTouchListener”);

return true;

}
});

点击ChildView(1处),打印Log

点击时间在OnTouch方法中被处理,所以不会传到onTouchEvent方法中去了。因此更加不会传到OnClickListener监听方法中去了。

一个为Action_Down触发,一个为Action_Up触发。

Center 15

发表评论

表情:
评论列表 (有 0 条评论,189人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android事件分发机制

    在触摸屏幕的过程中,要涉及到和控件的交互,如何处理多个控件之间的事件处理,保证正常的交互效果。我们今天来看事件分发机制。 零、事件分发的一些基础知识 什么是事件?

    相关 Android事件分发机制

    在触摸屏幕的过程中,要涉及到和控件的交互,如何处理多个控件之间的事件处理,保证正常的交互效果。我们今天来看事件分发机制。 零、事件分发的一些基础知识 什么是事件?

    相关 Android事件分发机制

    Android事件分发机制    就是一个触摸事件发生了,从一个窗口传递到下一个视图,在传递到另一个视图,最后被消费的过程,在android中还是比较复杂的传递流程:   

    相关 Android 事件分发机制

    前言 说到这个事件分发机制呢,我觉得一直以来都是我的弱项,可能它太抽象了,也与我在实际项目中没怎么使用到过,也没自定义过view有着很大的关系。虽然在面试过程中事件分发是必不

    相关 Android事件分发机制

    事件分发机制 在我们要详细了解这个东西的时候,首先要对它有一个最基础的认知,事件分发是干嘛的?分发的对象又是什么?接下来以一段伪代码来给大家解释什么是事件分发和它分发的对