Android TouchEvent事件传递机制
事件机制参考地址:
http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html
http://www.blogjava.net/TiGERTiAN/archive/2011/02/22/344869.html
http://blog.csdn.net/morgan_xww/article/details/9372285
跟touch事件相关的3个方法:
public boolean dispatchTouchEvent(MotionEvent ev); //用来分派event
public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event
public boolean onTouchEvent(MotionEvent ev); //用来处理event
拥有这三个方法的类:
Activity类: | Activity | dispatchTouchEvent(); onTouchEvent(); |
View容器(ViewGroup的子类): | FrameLayout、LinearLayout…… ListView、ScrollVIew…… |
dispatchTouchEvent(); onInterceptTouchEvent(); onTouchEvent(); |
View控件(非ViewGroup子类): | Button、TextView、EditText…… | dispatchTouchEvent(); onTouchEvent(); |
三个方法的用法:
dispatchTouchEvent() | 用来分派事件。 其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法 |
onInterceptTouchEvent() | 用来拦截事件。 ViewGroup类中的源码实现就是{return false;}表示不拦截该事件, 事件将向下传递(传递给其子View); 若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递, 事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法 |
onTouchEvent() | 用来处理事件。 返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View); 返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理 |
【注】:ViewGroup的某些子类(GridView、ScrollView...)重写了onInterceptTouchEvent()方法,当发生ACTION_MOVE事件时,返回true进行拦截。
为了演示,重写了4个类:
总统 --> MyActivity
省长 --> MyFrameLayout
市长 --> MyLinearLayout
农民 --> MyTextView
【举个通俗易懂的例子】:
总统对省长说:我要吃红烧鱼
省长对市长说:你做个红烧鱼
市长对县长说:你做个红烧鱼
县长对农民说:你做个红烧鱼
……(农民做呀做,没做出来)
农民说:我尽力了,但真心不会做呀,饶了我吧
县长说:你个笨蛋,下次不找你了,看我来做
……(县长做呀做,没做出来)
县长对市长说:我尽力了,非常抱歉,我不会做
市长说:你个废物,要你何用,只能我自己来做了
……(市长做呀做,做成功了)
市长对省长说:红烧鱼做好了
省长说:不错,下次有事还找你
省长对总统说:红烧鱼做好了
总统说:不错,下次有事还找你
---------------------------
总统对省长说:我要吃水煮鱼
省长对市长说:你做个水煮鱼
市长说:县长连红烧鱼都搞不定,这次就不找他了,我自己亲自来做
……(市长做呀做,又成功了)
市长对省长说:水煮鱼做好了
省长说:不错,下次有事还找你
省长对总统说:水煮鱼做好了
总统说:不错,下次有事还找你
---------------------------
- 按常理,领导都会把任务向下分派,一旦下面的人把事情做不好,就不会再把后续的任务交给下面的人来做了,只能自己亲自做,如果自己也做不了,就只能告诉上级不能完成任务,上级又会重复他的过程。
- 另外,领导都有权利拦截任务,对下级隐瞒该任务,而直接自己去做,如果做不成,也只能向上级报告不能完成任务。
【2】把TextView的clickable属性手动改成true,或者直接重写onTouchEvent()方法,使其返回true,程序输出如下:
事件传递示意图:
【3】手动重写LinearLayout的onInterceptTouchEvent()方法,使其返回true,拦截事件,再重写onTouchEvent()方法,返回true,程序输出:
事件传递示意图:
(1)这
一系列的传递流程都是dispatchTouchEvent()方法来控制的,如果不人为地干预,事件将由上自下依次传递(因为默认是返回false不会
拦截的),传递到最底层的View,就由它的onTouchEvent()方法来处理事件,若处理成功返回true,若处理失败返回false,事件依次
向上传递,每个View都调用自己的onTouchEvent()方法来处理事件,若处理成功就终止传递,若处理失败就继续向上传递。
(2)经过人为的干预,若在向下传递的过程中被拦截了,即onInterceptTouchEvent()方法返回true,则事件将停止向下传递,直接由当前的onTouchEvent()方法来处理,若处理成功则OK,若处理不成功,则事件会向上传递。
(3)另
外,dispatchTouchEvent()方法中还有“记忆”的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它
会记录该事件是否被它下面的View给处理成功了,(怎么能知道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那
就说明下面的View都没能成功处理该事件);当第二次事件向下传递到该View,该View的dispatchTouchEvent()方法机会判断,
若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功,那么这次的事件就不会向下传递了,
该View直接调用自己的onTouchEvent()方法来处理该事件。
(4)“记
忆”功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记
忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的
ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时
候,还是会传递到该View的。
1 public class MyActivity extends Activity { 2 3 @Override 4 public void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.main); 7 } 8 9 @Override 10 public boolean dispatchTouchEvent(MotionEvent ev) { 11 Log.d("d", "【总统】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派"); 12 return super.dispatchTouchEvent(ev); 13 } 14 15 @Override 16 public boolean onTouchEvent(MotionEvent ev) { 17 boolean bo = false; 18 Log.d("d", "【总统】任务<" + Util.actionToString(ev.getAction()) + "> : 下面都解决不了,下次再也不能靠你们了,哼…只能自己尝试一下啦。能解决?" + bo); 19 return bo; 20 } 21 }
1 public class MyFrameLayout extends FrameLayout 2 { 3 public MyFrameLayout(Context context, AttributeSet attrs){ 4 super(context, attrs); 5 } 6 7 @Override 8 public boolean dispatchTouchEvent(MotionEvent ev) { 9 Log.d("d", "【省长】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派"); 10 return super.dispatchTouchEvent(ev); 11 } 12 13 @Override 14 public boolean onInterceptTouchEvent(MotionEvent ev) { 15 boolean bo = false; 16 Log.d("d", "【省长】任务<" + Util.actionToString(ev.getAction()) + "> : 拦截吗?" + bo); 17 return bo; 18 } 19 20 @Override 21 public boolean onTouchEvent(MotionEvent ev) { 22 boolean bo = false; 23 Log.d("d", "【省长】任务<" + Util.actionToString(ev.getAction()) + "> : 市长是个废物,下次再也不找你了,我自己来尝试一下。能解决?" + bo); 24 return bo; 25 } 26 }
1 public class MyLinearLayout extends LinearLayout{ 2 3 public MyLinearLayout(Context context, AttributeSet attrs) { 4 super(context, attrs); 5 } 6 7 @Override 8 public boolean dispatchTouchEvent(MotionEvent ev) { 9 Log.d("d", "【市长】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派"); 10 return super.dispatchTouchEvent(ev); 11 } 12 13 @Override 14 public boolean onInterceptTouchEvent(MotionEvent ev) { 15 boolean bo = false; 16 Log.d("d", "【市长】任务<" + Util.actionToString(ev.getAction()) + "> : 拦截吗?" + bo); 17 return bo; 18 } 19 20 @Override 21 public boolean onTouchEvent(MotionEvent ev) { 22 boolean bo = false; 23 Log.d("d", "【市长】任务<" + Util.actionToString(ev.getAction()) + "> : 农民真没用,下次再也不找你了,我自己来尝试一下。能解决?" + bo); 24 return bo; 25 } 26 }
1 public class MyTextView extends TextView 2 { 3 public MyTextView(Context context, AttributeSet attrs){ 4 super(context, attrs); 5 } 6 7 @Override 8 public boolean dispatchTouchEvent(MotionEvent ev){ 9 Log.d("d", "【农民】任务<" + Util.actionToString(ev.getAction()) + "> : 需要分派,我下面没人了,怎么办?自己干吧"); 10 return super.dispatchTouchEvent(ev); 11 } 12 13 @Override 14 public boolean onTouchEvent(MotionEvent ev){ 15 boolean bo = true; 16 Log.d("d", "【农民】任务<" + Util.actionToString(ev.getAction()) + "> : 自己动手,埋头苦干。能解决?" + bo); 17 return bo; 18 } 19 }
原文地址:http://blog.csdn.net/morgan_xww/article/details/9372285
时间传递机制强化(直接上代码,能拖动的ImageView(默认的点击事件是false)和Button(默认点击事件是true))
1 public class MainActivity extends Activity implements OnTouchListener { 2 3 private ImageView img; 4 5 private int lastX, lastY; 6 7 private int screenWidth, screenHeight; 8 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 setContentView(R.layout.activity_main); 13 img = (ImageView) findViewById(R.id.img); 14 // 获取屏幕的宽和高 15 DisplayMetrics dm = getResources().getDisplayMetrics(); 16 screenWidth = dm.widthPixels; 17 screenHeight = dm.heightPixels; 18 System.out.println("screenWidth-->>" + screenWidth 19 + " screenHeight-->>" + screenHeight); 20 img.setOnTouchListener(new OnTouchListener() { 21 22 @Override 23 public boolean onTouch(View v, MotionEvent event) { 24 System.out.println("这是ImageView的onTouch"); 25 switch (event.getAction()) { 26 case MotionEvent.ACTION_DOWN: 27 lastX = (int) event.getRawX(); 28 lastY = (int) event.getRawY(); 29 30 /** 31 * 如果这里返回true 或者让ImageView 32 * 的android:onClick设置成true(表示down时间处理成功了) 33 * 在结合时间传递机制就知道为什么了Down-->>move-->>up 34 */ 35 return false; 36 37 case MotionEvent.ACTION_MOVE: 38 int dx = (int) (event.getRawX() - lastX); 39 int dy = (int) (event.getRawY() - lastY); 40 41 int left = v.getLeft() + dx; 42 int top = v.getTop() + dy; 43 int right = v.getRight() + dx; 44 int bottom = v.getBottom() + dy; 45 46 if (left <= 0) { 47 // 到达父View的左边界 48 left = 0; 49 right = left + v.getWidth(); 50 } 51 52 if (right >= screenWidth) { 53 right = screenWidth; 54 left = right - v.getWidth(); 55 } 56 57 if (top <= 0) { 58 top = 0; 59 bottom = top + v.getHeight(); 60 } 61 62 if (bottom >= screenHeight) { 63 bottom = screenHeight; 64 top = bottom - v.getHeight(); 65 } 66 67 v.layout(left, top, right, bottom); 68 lastX = (int) event.getRawX(); 69 lastY = (int) event.getRawY(); 70 case MotionEvent.ACTION_UP: 71 72 System.out.println("释放后的位置-->>event.getX():" + event.getX() 73 + " event.getY():" + event.getY() 74 + " event.getRawX():" + event.getRawX() 75 + " event.getRawY():" + event.getRawY()); 76 77 break; 78 79 default: 80 81 break; 82 } 83 84 return false; 85 } 86 87 }); 88 89 findViewById(R.id.btn).setOnTouchListener(new OnTouchListener() { 90 91 @Override 92 public boolean onTouch(View v, MotionEvent event) { 93 System.out.println("这是Button的onTouch"); 94 switch (event.getAction()) { 95 case MotionEvent.ACTION_DOWN: 96 System.out 97 .println("ACTION_DOWNACTION_DOWNACTION_DOWNACTION_DOWNACTION_DOWN"); 98 lastX = (int) event.getRawX(); 99 lastY = (int) event.getRawY(); 100 break; 101 102 case MotionEvent.ACTION_MOVE: 103 System.out.println("MOVEMOVE"); 104 int dx = (int) (event.getRawX() - lastX); 105 int dy = (int) (event.getRawY() - lastY); 106 107 int left = v.getLeft() + dx; 108 int top = v.getTop() + dy; 109 int right = v.getRight() + dx; 110 int bottom = v.getBottom() + dy; 111 112 if (left <= 0) { 113 // 到达父View的左边界 114 left = 0; 115 right = left + v.getWidth(); 116 } 117 118 if (right >= screenWidth) { 119 right = screenWidth; 120 left = right - v.getWidth(); 121 } 122 123 if (top <= 0) { 124 top = 0; 125 bottom = top + v.getHeight(); 126 } 127 128 if (bottom >= screenHeight) { 129 bottom = screenHeight; 130 top = bottom - v.getHeight(); 131 } 132 133 v.layout(left, top, right, bottom); 134 lastX = (int) event.getRawX(); 135 lastY = (int) event.getRawY(); 136 137 break; 138 case MotionEvent.ACTION_UP: 139 140 System.out.println("释放后的位置-->>event.getX():" + event.getX() 141 + " event.getY():" + event.getY() 142 + " event.getRawX():" + event.getRawX() 143 + " event.getRawY():" + event.getRawY()); 144 break; 145 146 default: 147 148 break; 149 } 150 return false; 151 } 152 153 }); 154 155 } 156 157 @Override 158 public boolean onTouch(View v, MotionEvent event) { 159 System.out.println("这是Activity的onTouch"); 160 return false; 161 } 162 163 }
activity_main.xml
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 tools:context=".MainActivity" > 7 8 <ImageView 9 android:id="@+id/img" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:contentDescription="图片" 13 android:focusable="true" 14 android:focusableInTouchMode="true" 15 android:src="@drawable/ic_launcher" > 16 </ImageView> 17 18 <Button 19 android:id="@+id/btn" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:text="拖动试一试" /> 23 24 </LinearLayout>