按键分析--APP部分-- OnClickListener事件处理流程

5.3         OnClickListener事件处理流程

 

 

5.3.1         OnClickListener注册和使用

 

应用可以使用SetOnClickListener()给一个View控件注册监听器,其实现在View里面,实际就是给View实例的成员类ListenerInfo的成员mOnClickListener赋值,

     public void setOnClickListener(OnClickListener l) {

        if (!isClickable()) {

            setClickable(true);

        }

        getListenerInfo().mOnClickListener = l;

    }

 

OnClickListener只是一个接口,定义如下,

     public interface OnClickListener {

        /**

         * Called when a view has been clicked.

         *

         * @param v The view that was clicked.

         */

        void onClick(View v);

    }

 

 

使用时,

在activity里实现:implements View.OnClickListener,

 

在activity里的view上调用setOnClickListener,activity就可以作为OnClickListener监听器,

 view.setOnClickListener(this);

在后面调用mOnClickListener的时候,进行处理onClick了。

    public boolean performClick() {

        final boolean result;

        final ListenerInfo li = mListenerInfo;

        if (li != null && li.mOnClickListener != null) {

            playSoundEffect(SoundEffectConstants.CLICK);

            li.mOnClickListener.onClick(this);

            result = true;

        } else {

            result = false;

        }

 

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

        return result;

    }

 

 

 

 

5.3.2         OnClickListener栈分析

 

 

对于注册的OnClickListener监听器,不能想当然的根据代码分析,任务认为他们的会在onTouchEvent()方法里面执行,为了明白他们的执行时机,我们在一个Activity上定义一个按钮,再注册OnClickListener,点击按钮的时候会得到如下的调用栈,可以看出OnClickListener走了一条完全不同的执行过程,所以分析代码一定要结合调试,否则会误入歧途,

DialActivity$15.onClick(View) line: 3484       

ImageButton(View).performClick() line: 4240 

View$PerformClick.run() line: 17721     

Handler.handleCallback(Message) line: 730     

ViewRootImpl$ViewRootHandler(Handler).dispatchMessage(Message) line: 92   

Looper.loop() line: 137

ActivityThread.main(String[]) line: 5265

Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]     

Method.invoke(Object, Object...) line: 525       

ZygoteInit$MethodAndArgsCaller.run() line: 760     

ZygoteInit.main(String[]) line: 576

NativeStart.main(String[]) line: not available [native method]      

 

从调用栈可以看出,OnClick()的执行调用源头是Handler.dispatchMessage(),事件是发送给ViewRootImpl的ViewRootHandler实例的,最终调用Handler的handleCallback,根据我们在另外的文章分析的Handler的三种消息处理方式中了解到,这个消息是在应用层Post出来的,而不是HAL层通过JNI方式上传过来。现在我们有两个方向分析问题,一个是当前消息事件向上的分发处理,一个方向是Post消息的源头。

 

1)向上:OnClick的执行过程

handleCallback(msg)处理的消息在生成的时候,会有Callback和run方法,所以这种方式就是直接调用run方法,

     private static void handleCallback(Message message) {

        message.callback.run();

    }

 

 

本例的callback是一个runnable类PerformClick,它的实现主体是

    private final class PerformClick implements Runnable {

        public void run() {

            performClick();

        }

    }

 

    public boolean performClick() {

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);

 

        ListenerInfo li = mListenerInfo;

        if (li != null && li.mOnClickListener != null) {

            playSoundEffect(SoundEffectConstants.CLICK);

            li.mOnClickListener.onClick(this);

            return true;

        }

 

        return false;

    }

从代码里就很好理解了,如果View注册了OnClickListener,则执行其所在的activity的onClick方法,到此流程就结束了。

 

另外,根据代码分析,OnClickListener还有另外一种调用方法,即通过调用callOnClick()来执行onClick,有兴趣的同学可单独分析。

 

2)向下:谁Post了这个消息

   因为我们点击一个控件View的时候,是物理事件,现在onClick处理的是一个间接的应用层消息,我们还要分析物理按键引发的事件和这个消息的关联。

 

这个在前面分析View的onTouchEvent方法时有讲到,在处理MotionEvent.ACTION_UP事件时,会Post这个消息,进而触发OnClickListener的执行。

                case MotionEvent.ACTION_UP:

                        if (!mHasPerformedLongPress) {

                            removeLongPressCallback();

                            if (!focusTaken) {

                                if (mPerformClick == null) {

                                    mPerformClick = new PerformClick();

                                }

                                if (!post(mPerformClick)) {

                                    performClick();

                                }

                            }

                        }

 

 

 

 

 

 

5.3.3         OnClickListener相关分析

 

 

1onClick就传入一个View对象,而 onTouch要传入一个View 对象和 MotionEvent的对象
2
onTouch对控件的操作比onCilck更丰富,比如判断触摸的状态(比如按下,或者放开),和得到点击的位置等等
3
、当对一个控件触摸的时候touch 先调用, onclick touch 的一个扩展实现

简单来理解就是在事件处理流程中,onTouch比onClick先执行,如果onTouch消耗掉事件,onClick就得不到执行。

 

 

Android框架中,对于事件的处理有两种实现机制。

u  基于监听接口

u  基于回调

 

基于监听机制:为android组件绑定特定的事件监听器。事件监听器是视图View类的接口。如:

View.OnClickListener、View.OnLongClickListener、 View.OnKeyListener、View.OnTouchListener、

View.OnFocusChangeListener等。

接口包含一个单独的回调方法。这个方法将在视图中注册的监听器被用户界面操作触发时由Android框架调用。

 

基于回调机制: Android平台中,每个View都有自己的处理事件的回调方法,开发人员可以通过重写View中的这些回调方法来实现需要的响应事件。

如:

onKeyDown()、 onKeyUp()、onFocusChanged()、 onTouchEvent()等。

 

u  基于监听的事件模型分工更明确,事件源、事件监听由两个类分开实现,因此具有更好的可维护性。

u  基于回调的事件处理机制会更好地提高程序的内聚性。适合统一处理事情,比如在一个工程里的所有Button在被按下的时候背景为图片a,在抬起时候背景图片为图片b。

u  Android的事件处理机制保证基于监听的事件监听器会被优先触发。所以基于两种实现机制可以同时使用。

 

 

 

在应用里面,可以按如下方式定义并注册onTouchListener,

     private ImageView mBtnNum8 = null;

     mBtnNum8.setOnTouchListener(mOnTouchListenernumber);

    mOnTouchListenernumber  = getOnTouchClickListener();

private OnTouchListener getOnTouchClickListener() {

      OnTouchListener onTouchListener = new OnTouchListener() {

          public boolean onTouch(View v, MotionEvent event) {

               if(event.getAction() == KeyEvent.ACTION_DOWN){

               }else if((event.getAction() == KeyEvent.ACTION_UP)||(event.getAction() == MotionEvent.ACTION_CANCEL)){

      };

      return onTouchListener;

}

 

 

注册过程实现在View类里,

     public void setOnTouchListener(OnTouchListener l) {

        getListenerInfo().mOnTouchListener = l;

    }

在一个ListenerInfo类的实例里注册监听器,ListenerInfo类主要维护各类监听器。

 

onTouchListener监听器的触发过程在上面流程已经分析过,就不再赘述。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值