由FlipperView想到的 关于TouchEvent事件

来源:互联网 发布:php 小论坛开源 编辑:程序博客网 时间:2024/05/17 04:29

原文:    http://chroya.iteye.com/blog/668297


以前写 android ,对事件的处理没有太深入,只是简单的 onTouchEvent 就 ok 了,现在写的 UI ,很多自定义组件,父 view 和子 view 都需要接收事件,然后处理。如果不弄明白它的事件传递机制,很难拥有好的用户体验。

       Touchevent 中,返回值是 true ,则说明消耗掉了这个事件,返回值是 false ,则没有消耗掉,会继续传递下去,这个是最基本的。

        事实上,在Android的GUI系统的中,硬件触发的Event(KeyEvent、 TouchEvent、 TrackballEvent等)最开始是Window拿到了,Window将Event转发给了前台的Activity。但是Activity同样不能马上自己处理掉,而是将Event传递给了它里面的ContentView。

        如果ContentView是一个容器View(继承自ViewGroup类型),它一般都是先判断这个Event落在哪一个Child View上。然后将该Event Dispatch给这个Child View了。

      最终,最里层的Child View拿到了这个Event,而它又没有Child View了。于是它就开始处理Event(也就是响应事件)。


在 View 中跟 Touch 相关的事件有 dispatchTouchEvent , interceptTouchEvnet , onTouchEvent 三种。dispatchTouchEvent 是负责分发事件的,事件从 activity 传递出来之后,最先到达的就是最顶层 view 的dispatchTouchEvent ,然后它进行分发,如果返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。

如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。(我说的一次事件指的是 down 到 up 之间的一系列事件)。但是,如果该方法返回的是true,即touch event被这个子View消耗掉了,则touch event事件不会上传,即父view接受不到touch event,父view的onTouchEvent方法不会被执行。


总结一下,

1.dispatch事件时,是向下分发Event事件。而onTouch是向上响应Event事件

2.如果这一次事件没有人消耗掉,则系统不会给你下一次事件,因为他会认为你这次的事件阻塞了,没必要给下一次。onTouchEvent如果不消耗的话,会从子view传递到父view。

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

今天在用FlipperView来实现滑屏的时候,就遇到了OnTouch事件传递的问题。(这里只是讨论在响应阶段,即在OnTouch()方法中)主要代码如下:

其中,在FlipperView中有三个LinearLayout,每个layout里有1个TextView,通过实现OnGestureListener接口来实现滑屏。

由于在OnFling()方法中必须要有两个MotionEvent()参数,即很显然必须要执行两次OnTouch方法,才能得到两个Event,其中第一个是鼠标按下的TouchEvent,第二个是最后的一个ACTION_MOVE的MOTION_EVENT。

R.java

public final class R {    public static final class anim {        public static final int in=0x7f040000;        public static final int out=0x7f040001;    }    public static final class attr {    }    public static final class drawable {        public static final int icon=0x7f020000;    }    public static final class id {        public static final int flipper=0x7f060003;        public static final int include1=0x7f060004;        public static final int include2=0x7f060005;        public static final int include3=0x7f060006;        public static final int txt1=0x7f060000;        public static final int txt2=0x7f060001;        public static final int txt3=0x7f060002;    }    public static final class layout {        public static final int layout1=0x7f030000;        public static final int layout2=0x7f030001;        public static final int layout3=0x7f030002;        public static final int main=0x7f030003;    }    public static final class string {        public static final int app_name=0x7f050001;        public static final int hello=0x7f050000;    }}

主要用到了id值:

TextView:

        public static final int txt1=0x7f060000;        public static final int txt2=0x7f060001;        public static final int txt3=0x7f060002;
和FlipperView:

 public static final int flipper=0x7f060003;

主Activity的代码:

import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.GestureDetector;import android.view.GestureDetector.OnGestureListener;import android.view.MotionEvent;import android.view.View;import android.view.View.OnTouchListener;import android.view.ViewManager;import android.widget.TextView;import android.widget.ViewFlipper;public class ViewFliperTestActivity extends Activity implements OnGestureListener,OnTouchListener{private GestureDetector mDector;private ViewFlipper mFlipper;private TextView mTextView1;private TextView mTextView2;private TextView mTextView3;private static final String TAG = "ViewFliperTestActivity";    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        mDector = new GestureDetector(this);                mFlipper = (ViewFlipper) findViewById(R.id.flipper);        mFlipper.setOnTouchListener(this);//        mFlipper.setOnTouchListener(new OnTouchListener() {////@Override//public boolean onTouch(View v, MotionEvent event) {//// TODO Auto-generated method stub//Log.i(TAG,"mFlipper.onTouch()");//mDector.onTouchEvent(event);//return true;//}//});                mTextView1 = (TextView)findViewById(R.id.txt1);        mTextView2 = (TextView)findViewById(R.id.txt2);        mTextView3 = (TextView)findViewById(R.id.txt3);                mTextView1.setOnTouchListener(this);        mTextView2.setOnTouchListener(this);        mTextView3.setOnTouchListener(this);    }@Overridepublic boolean onDown(MotionEvent e) {// TODO Auto-generated method stubreturn false;}@Overridepublic void onShowPress(MotionEvent e) {// TODO Auto-generated method stub}@Overridepublic boolean onSingleTapUp(MotionEvent e) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,float distanceY) {// TODO Auto-generated method stubreturn false;}@Overridepublic void onLongPress(MotionEvent e) {// TODO Auto-generated method stub}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {Log.i(TAG,"onFling() in activity");if(e1.getX() > e2.getX()){mFlipper.showNext();}else if(e2.getX() > e1.getX()){mFlipper.showPrevious();}return false;}// .........................................//蓝色部分://@Override//public boolean onTouchEvent(MotionEvent event) {//// TODO Auto-generated method stub//Log.i(TAG,"onTouch in activity");//boolean value = mDector.onTouchEvent(event);//Log.i(TAG,"mDector.onTouchEvent(event)="+String.valueOf(value));//return value;//}//........蓝色部分......................................@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO Auto-generated method stubLog.i(TAG,"onTouch() in activity");Log.i(TAG,"view id = "+Integer.toHexString(v.getId()));mDector.onTouchEvent(event);//...........................红色部分..........................//if(v.getId() == R.id.flipper){ return true; }//...........................红色部分...............................return false ;}}


在将红色部分注释掉后,在屏幕上滑动时,在log里有如下的结果:

说明:在OnTouch里返回false 时,先是执行txt1 的 OnTouch ,由于返回false, Event往上传递,直到在FlipperView中再次实现OnTouch,但是在拖动过程中,不再产生OnTouchEvent事件 。验证得到:如果所有的OnTouch都返回false,则,Event事件就会消失,下次将接收不到TouchEvent。也就是说,在整个过程中,只接受了一个TouchEvent事件,无法触发onFling(),也就无法实现滑屏。

不注释掉红色部分时,有如下结果:

说明:此时,在TextView 上,执行了一次OnTouch,返回false后,将TouchEvent往上传递,由FlipperView消耗了该Event事件,返回true.此后,在整个滑动过程中,TouchEvent事件再不分派给TextView控件,而是直接分派给FlipperView事件,最后触发onFling方法,实现滑屏。

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

在讨论下:  Activity的onTouchEvent 事件与View 的 onTouchEvent事件的区别:

1.  Activity 的 OnTouchEvent 

Called when a touch screen event was not handled by any of the views under it. This is most useful to process touch events that happen outside of your window bounds, where there is no view to receive it.

当我将上面代码中,蓝色的部分不注释掉,但是红色的代码注释掉(onTouch都返回false)时,如下结果:

说明:TouchEvent事件由Activity的一直在执行,就相当于在整个layout上再嵌套了一个layout,执行的都是这个layout的OnTouch(),但是很重要的一点是: 在该onTouchEvent()的方法中,不管返回的是true 或false 都是可以的,最后都能执行onFling().这是为什么呢??????

而当把蓝色的部分和红色的布冯都不注释掉时,将只触发FlipperView的onTouch(),即不再将TouchEvent事件往上传递了,如下:

说明:Activity的onTouchEvent()事件不再执行,即TouchEvent事件由FlipperView消耗掉了,不再向上传递。

总结:

1. Activity的onTouchEvent() 相当于是在layou之上的Window层的onTouchEvent(),当TouchEvent由Window里的View或ViewGroup消耗掉时,就不再调用它,如果没有,则调用它

2.Activity的onTouchEvent()不管返回的是true还是false,TouchEvent事件都不会消失,还会分配第二次的Event事件

原创粉丝点击