Android事件传递机制

欢迎指教!!!

http://blog.csdn.net/hemeng2009/article/details/40076463

由于点击屏幕空白处隐藏软键盘中接触到了dispatchtouchevent,onInterceptTouchEvent,ontouchevent,故而对这方面知识进行了详细了解。

一、ViewViewGroup

 

所有的UI控件例如ButtonTextView都是继承于View,而所有的布局控件、容器控件继承于ViewGroup

二、事件

       Android中,事件主要包括点按、长按、拖拽、滑动等,点按又包括单击和双击,另外还包括单指操作和多指操作。所有这些都构成了Android中的事件响应。总的来说,所有的事件都由如下三个部分作为基础:

·       按下(ACTION_DOWN

·       移动(ACTION_MOVE

·       抬起(ACTION_UP

三、事件响应函数(回调函数)

View.java                                                                        ViewGroup.java

public booleandispatchTouchEvent(MotionEvent event) public boolean dispatchTouchEvent(MotionEvent event)

public booleanonTouchEvent(MotionEvent event)           public boolean onTouchEvent(MotionEvent event)                                                                                                          public boolean onInterceptTouchEvent(MotionEvent event)

我们可以看出ViewGroup多了一个onInterceptTouchEvent方法,返回值均为boolean

      返回值: 事件传递,传递的过程就是一个接一个,具体就是根据响应函数的返回值(true/false)判断事件是否继续传递下去。在Android中,所有的事件都是从开始经过传递到完成事件的消费,这些方法的返回值决定了事件------继续往下传/还是被拦截了/被消费了。

       参:MotionEvent继承于InputEvent,用于标记各种动作事件。之前提到的ACTIONDOWNACTIONMOVEACTION_UP都是MotinEvent中定义的常量。我们通过MotionEvent传进来的事件类型来判断接收的是哪一种类型的事件。

       能:

·       dispatchTouchEvent方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。

·       onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。

·       onInterceptTouchEventViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在AndroidView中是不能再包含子View的(iOS可以)。

四、应用

例子关注与不同组件的调用顺序,实际中根据需要选择。

view:

新建一个类RTButton继承Button,用来实现我们对按钮事件的跟踪。

RTButton.java

public classRTButton extends Button {

        public RTButton(Context context,AttributeSet attrs) {

               super(context, attrs);

        }

        //基于回调函数实现组件的方法重载(2个)

        @Override

        public boolean dispatchTouchEvent(MotionEvent event) {

               switch (event.getAction()) {

               //下面用****************************代替switch内容

                               caseMotionEvent.ACTION_DOWN:

                                         System.out.println("RTButton---onTouch---DOWN");

                                         break;

                                 caseMotionEvent.ACTION_MOVE:

                                         System.out.println("RTButton---onTouch---MOVE");

                                         break;

                                 caseMotionEvent.ACTION_UP:

                                        System.out.println("RTButton---onTouch---UP");

                                         break;

                                 default:

                                         break;

 

               }

               returnsuper.dispatchTouchEvent(event);

        }

        @Override

        public boolean onTouchEvent(MotionEventevent) {

               switch (event.getAction()) {

               ******************************

               }

               return super.onTouchEvent(event);

        }

}

activity_main.xml在根布局下放入自定义的按钮RTButton

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/myLayout"

   android:layout_width="match_parent"

   android:layout_height="match_parent" >

    <com.ryantang.eventdispatchdemo.RTButton

        android:id="@+id/btn"

       android:layout_width="match_parent"

       android:layout_height="wrap_content"

        android:text="Button"/>

</LinearLayout>

 

Activity

Activity中为RTButton设置onTouchonClick的监听器来跟踪事件传递的过程,另外,Activity中也有一个dispatchTouchEvent方法和一个onTouchEvent方法,重写他们并输出打印信息。

MainActivity.java

public classMainActivity extends Activity {

        private RTButton button;

        @Override

        protected void onCreate(BundlesavedInstanceState) {

                 super.onCreate(savedInstanceState);

                 setContentView(R.layout.activity_main);

                 button =(RTButton)this.findViewById(R.id.btn);

//完全可以做一个layout的监听,对layout加入id,然后和button相同即可,参看

//http://blog.csdn.net/hemeng2009/article/details/40076463

                 //基于button监听实现事件响应

                 button.setOnTouchListener(newOnTouchListener() {

//onTouch方法

                         @Override

                         public boolean onTouch(View v, MotionEvent event) {

                                  switch(event.getAction()) {

                                  ***************************

                                  }

                                  return false;

                         }

                 });

                 //click事件监听

                 button.setOnClickListener(newOnClickListener() {

                         @Override

                         public voidonClick(View v) {

                                  System.out.println("RTButtonclicked!");

                         }

                 });    

        }

 

        //activity中的俩个方法----适用于全屏操作

        @Override

        publicboolean dispatchTouchEvent(MotionEvent event) {

                 switch(event.getAction()) {

                 *************************************

                 }

                 returnsuper.dispatchTouchEvent(event);

        }

        @Override

        publicboolean onTouchEvent(MotionEvent event) {

                 switch(event.getAction()) {

                 ************************************

                 }

                 returnsuper.onTouchEvent(event);

        }

}

点击按钮--结果:

首先执行了ActivitydispatchTouchEvent方法进行事件分发,dispatchTouchEvent方法的返回值是super.dispatchTouchEvent(event),因此调用了父类方法,我们进入Activity.java的源码中看看具体实现。

dispatchTouchEvent方法开始只处理了ACTIONDOWN事件,因为一个事件以down为开始。

//

Android事件拦截

    

Android嵌套布局事件传递

嵌套布局:RTLayout(ViewGroup)继承于LinearLayout,重写dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent方法。

那么,事件是先传递到View呢,还是先传递到ViewGroup的?

RTLayout.java

public class RTLayout extends LinearLayout {

        public RTLayout(Context context,AttributeSet attrs) {

                 super(context, attrs);

        }

        @Override

        public booleandispatchTouchEvent(MotionEvent event) {

                 switch (event.getAction()) {

                ********************************************

                 }

                 returnsuper.dispatchTouchEvent(event);

        }

        @Override

        public boolean onInterceptTouchEvent(MotionEvent event) {

                 switch (event.getAction()) {

               *********************************************

                 }

                 returnsuper.onInterceptTouchEvent(event);

        }

        @Override

        public boolean onTouchEvent(MotionEventevent) {

                 switch (event.getAction()) {

                ************************************************

                 }

                 returnsuper.onTouchEvent(event);

        }

}

同时,在布局文件中为RTButton添加一个父布局,指明为自定义的RTLayout,修改后的布局文件如下。

activity_main.xml

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

   xmlns:tools="http://schemas.android.com/tools"

   android:layout_width="match_parent"

   android:layout_height="match_parent" >

 //嵌套布局引入

    <com.ryantang.eventdispatchdemo.RTLayout

        android:id="@+id/myLayout"

       android:layout_width="match_parent"

       android:layout_height="match_parent" >

        <com.ryantang.eventdispatchdemo.RTButton

            android:id="@+id/btn"

           android:layout_width="match_parent"

           android:layout_height="wrap_content"

            android:text="Button"/>

   </com.ryantang.eventdispatchdemo.RTLayout>

</LinearLayout>


最后,我们在Activity中也为RTLayout设置onTouchonClick事件,在MainActivity中添加如下代码。

MainActivity.java

//采用的事件监听的方法

//同样可以为主layout监听。

        rtLayout.setOnTouchListener(newOnTouchListener() {

                         @Override

                         public booleanonTouch(View v, MotionEvent event) {

                                  switch(event.getAction()) {

                                  ***************************************

                                  }

                                  return false;

                         }

                 });         

        rtLayout.setOnClickListener(newOnClickListener() {          

                         @Override

                         public voidonClick(View v) {

                                  System.out.println("RTLayoutclicked!");

                         }

                 });

代码修改完毕后,编译运行工程,同样,点击按钮,查看日志输出结果如下:


从日志输出结果我们可以看到,嵌套了RTLayout以后,事件传递的顺序变成了Activity->RTLayout->RTButton,这也就回答了前面提出的问题,Android中事件传递是从ViewGroup传递到View的,而不是反过来传递的。

从输出结果第三行可以看到,执行了RTLayoutonInterceptTouchEvent方法,该方法的作用就是判断是否需要拦截事件,我们到ViewGroup的源码中看看该方法的实现。

ViewGroup.java

public boolean onInterceptTouchEvent(MotionEvent ev) {

        return false;

    }

该方法的实现很简单,只返回了一个false如果onInterceptTouchEvent返回false则不拦截,如果返回true则拦截当前事件。

 

总结

现总结如下:

        根据上面的内容,我们知道,点击屏幕空白处,隐藏键盘,实现对布局文件的监听即可。

·       Android中事件传递按照从上到下进行层级传递,事件处理从Activity开始到ViewGroup再到View

·       事件传递方法包括dispatchTouchEventonTouchEventonInterceptTouchEvent,其中前两个是ViewViewGroup都有的,最后一个是只有ViewGroup才有的方法。这三个方法的作用分别是负责事件分发、事件处理、事件拦截。

·       onTouch事件要先于onClick事件执行,onTouch在事件分发方法dispatchTouchEvent中调用,而onClick在事件处理方法onTouchEvent中被调用,onTouchEvent要后于dispatchTouchEvent方法的调用。

·       ps:http://www.infoq.com/cn/articles/android-event-delivery-mechanism

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值