美文网首页
按键事件如何在Activity中的分发

按键事件如何在Activity中的分发

作者: _水蓝 | 来源:发表于2018-01-15 17:02 被阅读0次

    参考来自:http://blog.csdn.net/mountains2001/article/details/47018455

    Android 中Activity能收到的事件有很多种,例如:Key、Touch、trackball等等。但是基本流程大概是一致的。本文将探寻activity中的事件流程,至于事件如何传递到activity,将另起篇幅介绍。

    activity的事件入口
    public boolean dispatchKeyEvent(KeyEvent event) {
            onUserInteraction();
            Window win = getWindow();
            if (win.superDispatchKeyEvent(event)) {
                return true;
            }
            View decor = mDecor;
            if (decor == null) decor = win.getDecorView();
            return event.dispatch(this, decor != null 
                    ? decor.getKeyDispatcherState() : null, this);
        }
    

    以上代码是activity的dispatchKeyEvent()方法,该方法就是key事件的入口。至于其它的touch、trackball等事件,也都有类似的dispatch方法作为一类事件的入口。

    activity收到事件之后,会获取当前的window,将该事件传递给window。如果window的superDispatchKeyEvent()方法捕获该事件,则该事件传递结束。否则,将调用KeyEvent的dispatch()方法。

    window的处理流程
     /**
         * Used by custom windows, such as Dialog, to pass the key press event
         * further down the view hierarchy. Application developers should
         * not need to implement or call this.
         *
         */
        public abstract boolean superDispatchKeyEvent(KeyEvent event);
    

    Window接口的superDispatchKeyEvent()的api说明,已经明确指出,事件会通过此方法进入view层级中。其具体实现是在PhoneWindow中。

    @Override
        public boolean superDispatchKeyEvent(KeyEvent event) {
            return mDecor.superDispatchKeyEvent(event);
        }
    

    这个mDecor是PhoneWindow的根视图,直接继承于FrameLayout, Decor View 挂载到PhoneWindow 上面。Decor View的分发如下:

    
        public boolean superDispatchKeyEvent(KeyEvent event) {
            // Give priority to closing action modes if applicable.
            if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                final int action = event.getAction();
                // Back cancels action modes first.
                if (mPrimaryActionMode != null) {
                    if (action == KeyEvent.ACTION_UP) {
                        mPrimaryActionMode.finish();
                    }
                    return true;
                }
            }
    
            return super.dispatchKeyEvent(event);
        }
    
    

    至此,开始按照view 层级分发。由 FrameLayout 开始,一层层分发下去。

    View层级的事件处理

    事件在View层级中传递,主要流程在ViewGroup和View中。先看ViewGroup的dispatchKeyEvent:

     @Override
        public boolean dispatchKeyEvent(KeyEvent event) {
    
            if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
                    == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
                if (super.dispatchKeyEvent(event)) {
                    return true;
                }
            } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
                    == PFLAG_HAS_BOUNDS) {
                if (mFocused.dispatchKeyEvent(event)) {
                    return true;
                }
            }
    
            return false;
        }
    

    如果ViewGroup自身已经获得焦点,则调用View的dispatchKeyEvent();否则将查找焦点子View,将事件传递给焦点子View处理。

    再看View的dispatchKeyEvent()

    public boolean dispatchKeyEvent(KeyEvent event){
            // Give any attached key listener a first crack at the event.
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
                return true;
            }
    
            if (event.dispatch(this, mAttachInfo != null
                    ? mAttachInfo.mKeyDispatchState : null, this)) {
                return true;
            }
    
            if (mInputEventConsistencyVerifier != null) {
                mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
            }
            return false;
        }
    

    如果View设置了KeyListener,并且是enable状态,则该事件先由KeyListener处理。KeyListener返回true,则事件结束。
    否则调用KeyEvent的dispatch()方法:

    public final boolean dispatch(Callback receiver, DispatcherState state,
                Object target) {
            switch (mAction) {
                case ACTION_DOWN: {
                    mFlags &= ~FLAG_START_TRACKING;
                    boolean res = receiver.onKeyDown(mKeyCode, this);
                    return res;
                }
                case ACTION_UP:
                    if (state != null) {
                        state.handleUpEvent(this);
                    }
                    return receiver.onKeyUp(mKeyCode, this);
                case ACTION_MULTIPLE:
                    final int count = mRepeatCount;
                    final int code = mKeyCode;
                    if (receiver.onKeyMultiple(code, count, this)) {
                        return true;
                    }
                    if (code != KeyEvent.KEYCODE_UNKNOWN) {
                        mAction = ACTION_DOWN;
                        mRepeatCount = 0;
                        boolean handled = receiver.onKeyDown(code, this);
                        if (handled) {
                            mAction = ACTION_UP;
                            receiver.onKeyUp(code, this);
                        }
                        mAction = ACTION_MULTIPLE;
                        mRepeatCount = count;
                        return handled;
                    }
                    return false;
            }
            return false;
        }
    

    这个dispatch()方法的第一个参数是KeyEvent.Callback:

    public interface Callback {
            boolean onKeyDown(int keyCode, KeyEvent event);
    
            boolean onKeyUp(int keyCode, KeyEvent event);
    
            boolean onKeyLongPress(int keyCode, KeyEvent event);
    
            boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
        }
    

    它有2个方法onKeyDown()和onKeyUp()。View和Activity都实现了这个接口。

    KeyEvent的dispatch()方法将判断按键是up还是down,然后传递分发给callback(View 的onKeyDown、onKeyUp)。View在onKeyDown()和onKeyUp()中,主要处理了enter按键,设置和取消了press状态,触发click事件,并在一定条件下,触发long press状态。

    如果在Callback中返回true,则事件结束;否则,事件流程回溯到起点activity的dispatchKeyEvent()中的第二部分。即调用的KeyEvent的dispatch()方法,事件将进入activity的onKeyDown()和onKeyUp()中。这2个函数是在应用中可以接收到事件的最后机会。

    事件流程总结

    Activity 先在Dispatch 中接收到事件,然后交由Window 处理。Window 再交给DecorView,从而进入View 层级处理。
    View层级中直接将事件转发给当前焦点view处理,而焦点view则依次转给自己的OnKeyListener回调、onKeyDown、onKeyUp处理。
    如果焦点view未处理,则回到了activity的dispatch函数,进入第二步流程onKeyDown、onKeyUp处理。这中间的节点是应用开发可以控制到的。至于事件如何进入activity、ime和activity如何事件交互,且看后续文章。

    相关文章

      网友评论

          本文标题:按键事件如何在Activity中的分发

          本文链接:https://www.haomeiwen.com/subject/ooaroxtx.html