main.xml如下:
<!-- 自定义布局中,放置一个自定义控件 --> <cn.c.MyFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <cn.c.MyLinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <cn.c.MyButton android:layout_width="200dip" android:layout_height="200dip" android:text="自定义Button" android:textColor="@android:color/black" /> </cn.c.MyLinearLayout> </cn.c.MyFrameLayout>
MainActivity如下:
package cn.c; import android.os.Bundle; import android.app.Activity; import android.view.MotionEvent; //Demo描述: //分析Android事件分发和处理机制 //总结: //1 ViewGroup继承自View // 事件的传递方向为:从最外层(Activity)传递至最内层(某个View) // 事件的消费方向为:从最内层(某个View)传递至最外层(Activity) // 该两个方向是相反的 //2 ViewGroup中事件处理的流程是: // dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent // View中事件处理的流程是: // dispatchTouchEvent->onTouchEvent //3 ViewGroup中onInterceptTouchEvent默认值是false // 表示未拦截 // ViewGroup中onTouchEvent默认值是false // 表示未消费 // View中onTouchEvent返回默认值是true // 表示已消费 //测试方法: //在界面中点击Button那么所展现流程和资料1中的图2基本一致 //为什么说基本一致呢?因为在MyButton中的onTouchEvent()方法默认返回的是true //即消费掉了事件所以只有在MyButton中的onTouchEvent()方法中执行,不会再回传到 //MyLinearLayout和MyFrameLayout的onTouchEvent()方法.所以图2中关于onTouchEvent() //的回溯是不准确的. //测试结果: //05-12 10:57:25.397: I/System.out(10133): ===> MainActivity 中调用 onCreate() //05-12 10:57:25.405: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): ===> MainActivity 中调用 dispatchTouchEvent() //05-12 10:57:28.835: I/System.out(10133): ===> super.dispatchTouchEvent()默认返回true //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): ===> MainActivity 中调用 onUserInteraction() //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): 外层MyFrameLayout 中调用 dispatchTouchEvent() //05-12 10:57:28.835: I/System.out(10133): super.dispatchTouchEvent()默认返回true 表示继续分发 //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): 外层MyFrameLayout 中调用 onInterceptTouchEvent() //05-12 10:57:28.835: I/System.out(10133): super.onInterceptTouchEvent()默认返回false 表示不拦截 //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): 内层MyLinearLayout 中调用 dispatchTouchEvent() //05-12 10:57:28.835: I/System.out(10133): super.dispatchTouchEvent()默认返回true 表示继续分发 //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): 内层MyLinearLayout 中调用 onInterceptTouchEvent() //05-12 10:57:28.835: I/System.out(10133): super.onInterceptTouchEvent()默认返回false 表示不拦截 //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): 自定义Button 中调用 dispatchTouchEvent() //05-12 10:57:28.835: I/System.out(10133): super.dispatchTouchEvent默认返回true //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.835: I/System.out(10133): 自定义Button 中调用 onTouchEvent()--->ACTION_DOWN //05-12 10:57:28.835: I/System.out(10133): super.onTouchEvent()默认返回true //05-12 10:57:28.835: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): ===> MainActivity 中调用 dispatchTouchEvent() //05-12 10:57:28.858: I/System.out(10133): ===> super.dispatchTouchEvent()默认返回true //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): 外层MyFrameLayout 中调用 dispatchTouchEvent() //05-12 10:57:28.858: I/System.out(10133): super.dispatchTouchEvent()默认返回true 表示继续分发 //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): 外层MyLinearLayout 中调用 onInterceptTouchEvent() //05-12 10:57:28.858: I/System.out(10133): super.onInterceptTouchEvent()默认返回false 表示不拦截 //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): 内层MyLinearLayout 中调用 dispatchTouchEvent() //05-12 10:57:28.858: I/System.out(10133): super.dispatchTouchEvent()默认返回true 表示继续分发 //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): 内层MyLinearLayout 中调用 onInterceptTouchEvent() //05-12 10:57:28.858: I/System.out(10133): super.onInterceptTouchEvent()默认返回false 表示不拦截 //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): 自定义Button 中调用 dispatchTouchEvent() //05-12 10:57:28.858: I/System.out(10133): super.dispatchTouchEvent默认返回true //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //05-12 10:57:28.858: I/System.out(10133): 自定义Button 中调用 onTouchEvent()--->ACTION_UP //05-12 10:57:28.858: I/System.out(10133): super.onTouchEvent()默认返回true //05-12 10:57:28.858: I/System.out(10133): -------------------------------------------------- //结果分析: //该结果为点击一次Button后的输出.展示了两次(down和up)完整的dispatchTouchEvent()过程 //两次都是从最上层的Activity的dispatchTouchEvent()传递到了最下层的MyButton的 //dispatchTouchEvent(),此时已到最下层,所以调用其最下层(即MyButton)的onTouchEvent //处理点击事件 //假若想看到图2中onTouchEvent()的回溯,可将MyButton中onTouchEvent() //返回false.这样就可以看到一次完整的关于down事件的回溯. //测试结果如下: //05-12 11:33:39.616: I/System.out(10657): ===> MainActivity 中调用 onCreate() //05-12 11:33:39.616: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): ===> MainActivity 中调用 dispatchTouchEvent() //05-12 11:33:46.436: I/System.out(10657): ===> super.dispatchTouchEvent()默认返回true //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): ===> MainActivity 中调用 onUserInteraction() //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 外层MyFrameLayout 中调用 dispatchTouchEvent() //05-12 11:33:46.436: I/System.out(10657): super.dispatchTouchEvent()默认返回true 表示继续分发 //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 外层MyFrameLayout 中调用 onInterceptTouchEvent() //05-12 11:33:46.436: I/System.out(10657): super.onInterceptTouchEvent()默认返回false 表示不拦截 //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 内层MyLinearLayout 中调用 dispatchTouchEvent() //05-12 11:33:46.436: I/System.out(10657): super.dispatchTouchEvent()默认返回true 表示继续分发 //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 内层MyLinearLayout 中调用 onInterceptTouchEvent() //05-12 11:33:46.436: I/System.out(10657): super.onInterceptTouchEvent()默认返回false 表示不拦截 //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 自定义Button 中调用 dispatchTouchEvent() //05-12 11:33:46.436: I/System.out(10657): super.dispatchTouchEvent默认返回true //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 自定义Button 中调用 onTouchEvent()--->ACTION_DOWN //05-12 11:33:46.436: I/System.out(10657): super.onTouchEvent()默认返回true //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 内层MyLinearLayout 中调用 onTouchEvent()--->ACTION_DOWN //05-12 11:33:46.436: I/System.out(10657): super.onTouchEvent()默认返回false 表示未消费事件 //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): 外层MyFrameLayout 中调用 onTouchEvent()--->ACTION_DOWN //05-12 11:33:46.436: I/System.out(10657): super.onTouchEvent()默认返回false 表示未消费事件 //05-12 11:33:46.436: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.436: I/System.out(10657): ===> MainActivity 中调用 onTouchEvent()--->ACTION_DOWN //05-12 11:33:46.436: I/System.out(10657): super.onTouchEvent()默认返回false 表示未消费事件 //05-12 11:33:46.436: I/System.out(10657): --------至此一次完整的down的OnTouchEvent()回溯---------------- //05-12 11:33:46.452: I/System.out(10657): ===> MainActivity 中调用 dispatchTouchEvent() //05-12 11:33:46.452: I/System.out(10657): ===> super.dispatchTouchEvent()默认返回true //05-12 11:33:46.452: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.452: I/System.out(10657): ===> MainActivity 中调用 onTouchEvent()--->ACTION_MOVE //05-12 11:33:46.452: I/System.out(10657): super.onTouchEvent()默认返回false 表示未消费事件 //05-12 11:33:46.452: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.468: I/System.out(10657): ===> MainActivity 中调用 dispatchTouchEvent() //05-12 11:33:46.475: I/System.out(10657): ===> super.dispatchTouchEvent()默认返回true //05-12 11:33:46.475: I/System.out(10657): -------------------------------------------------- //05-12 11:33:46.475: I/System.out(10657): ===> MainActivity 中调用 onTouchEvent()--->ACTION_UP //05-12 11:33:46.475: I/System.out(10657): super.onTouchEvent()默认返回false 表示未消费事件 //05-12 11:33:46.475: I/System.out(10657): -------------------------------------------------- //结果分析: //为什么只有一次完整的down事件的onTouchEvent()的回溯而没有move事件和up事件相关的 //onTouchEvent()的回溯?????? //此时我们注意到从最上层(MainActvity)到最下层(MyButton)每一层的onTouchEvent()返回 //的均为false.具体原因参见参考资料3 //重要参考资料: //1 http://www.eoeandroid.com/forum.php?mod=viewthread&tid=162460 // 该资料中第二个图非常重要. //2 http://www.dewen.org/q/2438 // 该资料中亦有流程图 //3 http://blog.csdn.net/ponderforever/article/details/7082787 // 在该资料的第四段提到: // 如果事件传递到某一层的子 view的onTouchEvent上了,这个方法返回了 false, // 那么这个事件会从这个view往上传递,都是其对应的onTouchEvent来接收. // 而如果传递到最上面的onTouchEvent也返回 false的话,这个事件就会"消失",而且接收不到下一次事件. // (我说的一次事件指的是 down 到 up 之间的一系列事件) // 针对该段最后一句话:(我说的一次事件指的是 down 到 up 之间的一系列事件) // 的补充说明:比如在此Demo的MainActivity的onTouchEvent()中返回false(即默认值)并且 // MyButton的onTouchEvent()也返回false(默认的是true)那么外层的MyFrameLayoue // 和内层的LinearLayout只能获取到down事件而move和up事件是不能获取的 // 这点非常的重要. //4 http://blog.csdn.net/ddna/article/details/5473293 //5 http://blog.csdn.net/ddna/article/details/5451722 //6 http://www.sctarena.com/Article/Article.asp?nid=2802 public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); System.out.println("===> MainActivity 中调用 onCreate()"); System.out.println("--------------------------------------------------"); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("===> MainActivity 中调用 dispatchTouchEvent()"); System.out.println("===> super.dispatchTouchEvent()默认返回true"); System.out.println("--------------------------------------------------"); return super.dispatchTouchEvent(ev); } @Override public void onUserInteraction() { System.out.println("===> MainActivity 中调用 onUserInteraction()"); System.out.println("--------------------------------------------------"); super.onUserInteraction(); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("===> MainActivity 中调用 onTouchEvent()--->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("===> MainActivity 中调用 onTouchEvent()--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("===> MainActivity 中调用 onTouchEvent()--->ACTION_UP"); default: break; } System.out.println("super.onTouchEvent()默认返回false 表示未消费事件"); System.out.println("--------------------------------------------------"); return super.onTouchEvent(event); } }
MyFrameLayout如下:
package cn.c; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.FrameLayout; public class MyFrameLayout extends FrameLayout{ public MyFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("外层MyFrameLayout 中调用 dispatchTouchEvent()"); System.out.println("super.dispatchTouchEvent()默认返回true 表示继续分发"); System.out.println("--------------------------------------------------"); return super.dispatchTouchEvent(ev); //return false; } //覆写自ViewGroup @Override public boolean onInterceptTouchEvent(MotionEvent ev) { System.out.println("外层MyFrameLayout 中调用 onInterceptTouchEvent()"); System.out.println("super.onInterceptTouchEvent()默认返回false 表示不拦截"); System.out.println("--------------------------------------------------"); return super.onInterceptTouchEvent(ev); //return true; } //注意: //1 ViewGroup是View的子类 //2 ViewGroup中onTouchEvent()方法默认返回的是false @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("外层MyFrameLayout 中调用 onTouchEvent()--->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("外层MyFrameLayout 中调用 onTouchEvent()--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("外层MyFrameLayout 中调用 onTouchEvent()--->ACTION_UP"); default: break; } System.out.println("super.onTouchEvent()默认返回false 表示未消费事件"); System.out.println("--------------------------------------------------"); return super.onTouchEvent(event); } }
MyLinearLayout如下:
package cn.c; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.LinearLayout; public class MyLinearLayout extends LinearLayout { public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { System.out.println("内层MyLinearLayout 中调用 dispatchTouchEvent()"); System.out.println("super.dispatchTouchEvent()默认返回true 表示继续分发"); System.out.println("--------------------------------------------------"); return super.dispatchTouchEvent(ev); } //覆写自ViewGroup @Override public boolean onInterceptTouchEvent(MotionEvent ev) { System.out.println("内层MyLinearLayout 中调用 onInterceptTouchEvent()"); System.out.println("super.onInterceptTouchEvent()默认返回false 表示不拦截"); System.out.println("--------------------------------------------------"); return super.onInterceptTouchEvent(ev); } //注意: //1 ViewGroup是View的子类 //2 ViewGroup中onTouchEvent()方法默认返回的是false @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("内层MyLinearLayout 中调用 onTouchEvent()--->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("内层MyLinearLayout 中调用 onTouchEvent()--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("内层MyLinearLayout 中调用 onTouchEvent()--->ACTION_UP"); default: break; } System.out.println("super.onTouchEvent()默认返回false 表示未消费事件"); System.out.println("--------------------------------------------------"); return super.onTouchEvent(event); } }
MyButton如下:
package cn.c; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.Button; public class MyButton extends Button{ public MyButton(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { System.out.println("自定义Button 中调用 dispatchTouchEvent()"); System.out.println("super.dispatchTouchEvent默认返回true"); System.out.println("--------------------------------------------------"); return super.dispatchTouchEvent(event); } //注意: //在View的子类中onTouchEvent()方法默认返回的是true @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("自定义Button 中调用 onTouchEvent()--->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("自定义Button 中调用 onTouchEvent()--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("自定义Button 中调用 onTouchEvent()--->ACTION_UP"); break; default: break; } System.out.println("super.onTouchEvent()默认返回true"); System.out.println("--------------------------------------------------"); return super.onTouchEvent(event); } }