您的位置:首页 > 移动开发 > Android开发

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

2014-07-23 23:53 691 查看
之前一直经常使用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
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG, "Layout_out_0+onInterceptTouchEvent:" + ev.getAction());
return false;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(TAG, "Layout_out_0+onTouchEvent:" + Common.getAction(ev));
return false;
}

@Override
public 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
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TAG,
"layout_inner_0+onInterceptTouchEvent:" + Common.getAction(ev));
return true;
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(TAG, "layout_inner_0+onTouchEvent:" + Common.getAction(ev));
return true;
}

@Override
public 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 {

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

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
Log.e("T", "MainActivity+onTouchEvent:" + Common.getAction(event));
return true;
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("T", "MainActivity+dispatchTouchEvent:" + Common.getAction(event));
return super.dispatchTouchEvent(event);
}
}


差不多结束了,剩下的就是根据不同的场景去改变函数中的返回值了,这样的话就能对TouchEvent进行控制了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐