关于Android触摸事件TouchEvent的传递及截取,研究心得。

来源:互联网 发布:系统网络协议栈有损坏 编辑:程序博客网 时间:2024/06/04 18:57

之前一直经常使用Touch的相关操作,但是对其中的具体细节一直没有详细的研究一下,今天研究了一下,感觉思路有点清晰了很多,再次记录一下。

其中关于Android触摸事件相关的函数有:dispatchTouchEvent() | onInterceptTouchEvent()|onTouchEvent()

由函数的名字也大概能够知道具体的作用是什么了:dispatchTouchEvent()负责触摸事件的分发,onInterceptTouchEvent()决定事件是不是在当前的View下拦截,onTouchEvent()负责触摸事件的处理消化。

先说一下传递的机制和顺序吧。

我们建立一个这样的场景,就是在一个Activity中有一个View,名叫:Layout_out_0,在Layout_out_0中还有嵌套在里面的一个View,叫Layout_inner_0,相信大家也能够想象是个什么东西吧。


当你触摸屏幕的时候,首先产生事件的顺序为:DOWN——>MOVE(这个事件的产生和你触摸时的触摸时间的长度有关,如果时间极短,就不会产生,如果一直按住,就会一直产生MOVE)——>UP(手指抬起的时候);当然这个顺序是固定的,事件的处理也是一个传递消化完毕之后,才开始第二个事件的传递消化,很固定。

好进入重点,DOWN事件产生首先经由Activity的dispatchTouchEvent(),在Activity中是没有方法onInterceptTouchEvent()方法的,也就无法使产生的事件被拦截了,接着进入等级仅次于Activity的容器组件View中,也是经由dispatchTouchEvent()(不过我在测试程序中尝试把该函数的返回值super.dispatchTouchEvent()修改为false或者是true结果发现导致事件无法正常的分发,所以不建议大家重写该回调函数),在经过onInterceptTouchEvent()决定是不是要拦截该事件,如果onInterceptTouchEvent()返回true则表示要拦截,那么事件就不会再继续往子控件传递了(否则继续重复上面的步骤,再次进入子控件的dispatchTouchEvent()——>onInterceptTouchEvent()),直接进入当前组件的onTouchEvent()当中进行事件的处理,如果onTouchEvent()返回true,则表示事件在此被消化,不会往父控件传递了,如果返回的是false表示事件还尚未消化,继续返回到父控件的onTouchEvent()(注意,是直接进入父控件的onTouchEvent()当中)

通过上面的描述不知道大家有没有高明白其中的逻辑呢。

那我在形象的说一下啊吧,简单画了一个思维导图,当不进行任何的消息拦截,即在某一层上的onInterceptTouchEvent()返回true,也不进行任何消息的消化,即在某一层上的onTouchEvent()返回true,的这种情况下,消息的传递是这样的:

如果在某一层上的onInterceptToucEvent()中进行了拦截,也就是返回了true,那么消息就不会继续往下层子控件走了。

比如说如果在Layout_out_0中进行了拦截,那么消息就会进入Layout_out_0中的onTouchEvent()中,进行消息的处理,如果Layout_out_0中的onTouchEvent()返回的是false,那么意思就是并没有把消息消化,那么会返回上层父类容器中的onTouchEvent()也就是Activtiy中的;如果Layout_out_0中的onTouchEvent()返回的true,表示的就是在这儿已经把消息消化掉了,就不会再继续向父类的onTouchEvent进行传递了。

好了,大家懂了吗?


说完了DOWN事件,那么在说一下MOVE和UP,这两个事件是根据DOWN的消化地点决定,比如说消化地点在Layout_out_0中,那么MOVE和UP的路线是Activity的dispatchTouchEvent()——>Layout_out_0的dispatchTouchEvent()——>Layout_out_0的onInterceptTouchEvent()——>Layout_out_0的onTouchEvent()——>结束,无论这边的onInterceptTouchEvent()拦截还是不拦截都是这样的(都不会继续往子控件Layout_inner_0中传递了),这样做感觉逻辑上确实很有说服力,就是在哪儿消化的就只到传递到哪儿,能够节约很多的资源和时间。


然后如果有人想亲自测试一下这个过程,我把我的代码贴上,仅供参考。


布局文件activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="wrap_content"    tools:context="${relativePackage}.${activityClass}" >    <com.example.toucheventtest.Layout_out_0        android:id="@+id/out0"        android:layout_width="300dp"        android:layout_height="300dp"        android:background="#ff0000ff" >        <com.example.toucheventtest.Layout_inner_0            android:id="@+id/inner0"            android:layout_width="200dp"            android:layout_height="200dp"            android:background="#ffff0000" >        </com.example.toucheventtest.Layout_inner_0>    </com.example.toucheventtest.Layout_out_0></RelativeLayout>

Layout_out_0.java:

package com.example.toucheventtest;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.RelativeLayout;public class Layout_out_0 extends RelativeLayout {public static final String TAG = "T";public Layout_out_0(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.e(TAG, "Layout_out_0+onInterceptTouchEvent:" + ev.getAction());return false;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {Log.e(TAG, "Layout_out_0+onTouchEvent:" + Common.getAction(ev));return false;}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.e(TAG,"layout_out_0+dispatchTouchEvent:"+Common.getAction(ev));return super.dispatchTouchEvent(ev);}}

Layout_inner_0.java:

package com.example.toucheventtest;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.widget.RelativeLayout;public class Layout_inner_0 extends RelativeLayout {public static final String TAG = "T";public Layout_inner_0(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {Log.e(TAG,"layout_inner_0+onInterceptTouchEvent:" + Common.getAction(ev));return true;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {Log.e(TAG, "layout_inner_0+onTouchEvent:" + Common.getAction(ev));return true;}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {Log.e(TAG, "layout_inner_0+dispatchTouchEvent:" + ev.getAction());return super.dispatchTouchEvent(ev);}}

Common类:

package com.example.toucheventtest;import android.view.MotionEvent;public class Common {public static String getAction(MotionEvent event) {String actionname = "none";switch (event.getAction()) {case MotionEvent.ACTION_DOWN:actionname = "down";break;case MotionEvent.ACTION_MOVE:actionname = "move";break;case MotionEvent.ACTION_UP:actionname = "up";break;default:break;}return actionname;}}

MainActivity.java:

package com.example.toucheventtest;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.MotionEvent;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubLog.e("T", "MainActivity+onTouchEvent:" + Common.getAction(event));return true;}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {Log.e("T", "MainActivity+dispatchTouchEvent:" + Common.getAction(event));return super.dispatchTouchEvent(event);}}

差不多结束了,剩下的就是根据不同的场景去改变函数中的返回值了,这样的话就能对TouchEvent进行控制了。

0 0
原创粉丝点击