玩转Android UI事件

原创
2015/03/13 18:48
阅读数 696

      (本文最早于2012-3-15 17:12日发表于QQ空间 进入我的空间并查找标题“玩转Android UI事件”

      研究了下 Android 可视组件的事件处理机制,本想用文字来阐述,但是发现太复杂,文字不太适合用来表达逻辑,遂改用程序代码来表述。读完本程序,你将会对 Android UI 事件处理机制有一个全新的认识。若能充分利用事件传递的这些特性,你写自定义组件就可以随心所欲,游刃有余了。

package com.test;
import android.view.MotionEvent;
import android.view.View;
/**
 * Android可视组件的触摸事件传递是通过调用dispatchTouchEvent(MotionEvent event)
 * 或dispatchKeyEvent(KeyEvent event)来实现事件的捕获、目标和冒泡三个阶段,调用是从最父层的组件开始,如Activity。
 * 不过这里仅阐述了触摸点击事件,至于键盘事件则比较简单,暂不赘述。
 * 
 * 这是一个对Android事件机制的模拟,读完本程序,你便明白了事件的处理过程。这里包含了几种不同组件中事件的传递和处理方式。
 * 这里作了一个融合,而在实际API类当中,根据不同的类有不同的实现方法,具体请看源码,不过我没来得及看。
 * 
 * 注意不要试图运行本程序,本程序只是表达了执行逻辑,个人认为用程序比用文字的方式表达的更加清晰。
 * 程序是最好的语言嘛,对于程序员来说。
 */
public class Android事件模拟 {
        /**是否应该向子组件传递事件**/
        boolean childConsume = false;
        /**本组件是Activity**/
        boolean isActivity = true | false;
        /**本组件是ViewGroup**/
        boolean isViewGroup = true | false;
        /**本组件是View而不是ViewGroup**/
        boolean isView = true | false;
        /**子组件是View(包括ViewGroup)**/
        boolean childIsView = true | false;
        
        /**有无子组件或者事件源是否在直接或间接子组件上**/
        boolean hasChild = true | false;
        /**模拟子视图组件**/
        Child child = new Child();
        /**外部监听器**/
        View.OnTouchListener touchListener = null;
        View.OnLongClickListener longClickListener = null;
        View.OnClickListener clickListener = null;
        /**本类对象,非Activity**/
        View view = null;
        /**默认状态下触摸事件的逻辑处理方法,当前对象层级视图组件触摸事件处理的入口**/
        public boolean dispatchTouchEvent(MotionEvent event) {
                /**本方法的返回值**/
                boolean b = false;
                if(isActivity) {//如果本对象是Activity。//完全由onTouchEvent()决定本方法的返回值
                        if(event.getAction() == MotionEvent.ACTION_DOWN) {
                                onUserInteraction();
                                childConsume = hasChild ? true : false;
                                if(childConsume) {
                                        if(child.dispatchTouchEvent(event)) {//注意子组件的dispatchTouchEvent(event)处理过程跟本方法相同
                                                childConsume = true;
                                                b = true;
                                        }else {
                                                childConsume = false;
                                                b = onTouchEvent(event);
                                        }
                                }else {
                                        b = onTouchEvent(event);
                                }
                        }else if(childConsume) {//即使后续事件中child.dispatchTouchEvent(event)返回false,也继续传递
                                if(child.dispatchTouchEvent(event)) {
                                        b = true;
                                }else {
                                        b = onTouchEvent(event);
                                }
                        }else {
                                b = onTouchEvent(event);
                        }
                }else if(isViewGroup) {//如果本对象是ViewGroup
                        boolean intercept = false;
                        if(event.getAction() == MotionEvent.ACTION_DOWN) {
                                intercept = onInterceptTouchEvent(event);//不管有没有子组件,本方法都将执行
                                childConsume = (hasChild && ! intercept) ? true : false;
                                if(childConsume) {
                                        if(child.dispatchTouchEvent(event)) {
                                                childConsume = true;
                                                b = true;
                                        }else {
                                                childConsume = false;
                                                if(touchListener != null) {
                                                        b = touchListener.onTouch(view, event);//调用外部监听器
                                                }
                                                if(!b) b = onTouchEvent(event);
                                        }
                                }else {
                                childConsume = false;
                                        if(touchListener != null) {
                                                b = touchListener.onTouch(view, event);
                                        }
                                        if(!b) b = onTouchEvent(event);
                                }
                        }else if(event.getAction() == MotionEvent.ACTION_MOVE) {
                                if(childConsume) {
                                        intercept = onInterceptTouchEvent(event);
                                        if(intercept) {
                                                childConsume = false;
                                                event.setAction(MotionEvent.ACTION_CANCEL);//重点,后续事件被拦截,将变为取消事件并继续传递
                                                child.dispatchTouchEvent(event);
                                                b = true;//此时不论child.dispatchTouchEvent(event)返回什么值,本方法都直接返回true
                                        }else {
                                                b = child.dispatchTouchEvent(event);//即使返回false也直接返回,区别于Activity
                                        }
                                }else {
                                        if(touchListener != null) {
                                                b = touchListener.onTouch(view, event);
                                        }
                                        if(!b) b = onTouchEvent(event);
                                }
                        }else if(event.getAction() == MotionEvent.ACTION_UP) {
                                if(childConsume) {
                                        intercept = onInterceptTouchEvent(event);
                                        if(intercept) {
                                                childConsume = false;
                                                event.setAction(MotionEvent.ACTION_CANCEL);//重点,后续事件被拦截,将变为取消事件并继续传递
                                                child.dispatchTouchEvent(event);
                                                b = true;//此时不管child.dispatchTouchEvent(event)返回什么值,本方法都直接返回true
                                        }else {
                                                b = child.dispatchTouchEvent(event);//即时返回false也直接返回,区别于Activity
                                        }
                                }else {
                                        if(touchListener != null) {
                                                b = touchListener.onTouch(view, event);
                                        }
                                        if(!b) b = onTouchEvent(event);//注意onClick事件是在onTouchEvent()内部处理的
                                }
                        }
                }else if(isView) {//如果本对象是不能添加子组件的View,如:Button、EditText
                        if(touchListener != null) {
                                b = touchListener.onTouch(view, event);
                        }
                        if(!b) b = onTouchEvent(event);
                }
                return b;
        }
        /**Activity类特有,重写它,可用于在事件捕获阶段,事件到达本层容器而未进行任何其他处理之前做些事情**/
        public void onUserInteraction() {}
        /**ViewGroup类特有,其返回值表示是否拦截事件,以决定事件是否继续传递**/
        public boolean onInterceptTouchEvent(MotionEvent event) {
                return true | false;
        }
        /**Activity默认返回false,View默认返回true。返回值会决定后续的事件传递情况。可以根据需要自定义返回值。**/
        public boolean onTouchEvent(MotionEvent event) {
                /*
                * 当Activity收到MotionEvent.ACTION_DOWN事件的冒泡返回,则会启动一个LongClick计时线程,之后若View组件树中
                * 有任何一个组件在延时之内收到MotionEvent.ACTION_UP或MotionEvent.ACTION_CANCEL事件,则计时终止。
                * 因此LongClick事件在两种状况下触发:
                * 
                * a、若父级Activity收到child.dispatchTouchEvent(event)的返回值为false时;
                * b、长按某组件,MotionEvent.ACTION_UP或MotionEvent.ACTION_CANCEL事件发生在LongClick之后时。
                * 
                * LongClick事件触发时,从叶子层组件向根层组件依次调用View.OnLongClickListener.onLongClick(View v)方法。
                * 该方法优先于组件本身默认的longClick相关处理方法,若返回值为false,会使组件继续调用默认的处理方法,
                * 否则不执行默认的处理方法并给View.OnClickListener.onClick(View v)一个不要执行的标识。
                * 但返回值不会影响上层组件对该方法的调用。
                */
                switch(event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                                /* Post本对象到LongClick计时线程 */
                                break;
                        case MotionEvent.ACTION_MOVE:
                                break;
                        case MotionEvent.ACTION_UP:
                                /* 终止LongClick计时线程 */
                                /* 执行View.OnClickListener.onClick(View v)。若View.OnLongClickListener.onLongClick(View v)先执行,
                                * 则根据其返回值决定是否执行View.OnClickListener.onClick(View v) */
                                break;
                }
                return true | false;
        }
        /**添加外部监听器。View类特有(包括ViewGroup),注意无论调用多少次本方法,只有最后一个监听器起作用,即覆盖**/
        public void setOnTouchListener(View.OnTouchListener l) {
                touchListener = l;
        }
        /**添加外部监听器。View类特有(包括ViewGroup),注意无论调用多少次本方法,只有最后一个监听器起作用,即覆盖**/
        public void setOnClickListener(View.OnClickListener l) {
                clickListener = l;
        }
        /**添加外部监听器。View类特有(包括ViewGroup),注意无论调用多少次本方法,只有最后一个监听器起作用,即覆盖**/
        public void setOnLongClickListener(View.OnLongClickListener l) {
                longClickListener = l;
        }
}
class Child extends Android事件模拟 {}




展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部