按键事件分发分析之一(八)

1,按键事件

手机上有各种各样的实体按键,比如音量上下键,开机键等等。

按键事件接口(KeyEvent):

ACTION_DOWN   // 按键按下

ACTION_UP       // 按键抬起

ACTION_MULTIPLE  // 复合键

每一个按键唯一不同的是keycode,所有的键值都定义在

frameworks/base/core/java/android/view/KeyEvent.java  文件中.

2,按键分发处理

分发处理流程如下,


ViewRootImpl的内部类ViewPostImeInputStage的processKeyEvent方法会调用View的dispatchKeyEvent方法,

mView. dispatchKeyEvent (event);

mView变量是在setview方法中赋值的,对于应用窗口来说, mView变量指向PhoneWindow的内部类DecorView对象。

public boolean dispatchKeyEvent(KeyEvent event) {
            final int keyCode = event.getKeyCode();
            final int action = event.getAction();
            final boolean isDown = action == KeyEvent.ACTION_DOWN;
              //按下时,处理panel的快捷键
            if (isDown && (event.getRepeatCount() == 0)) {
                // First handle chording of panel key: if a panel key is held
                // but not released, try to execute a shortcut in it.
                if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
                    boolean handled = dispatchKeyShortcutEvent(event);
                    if (handled) {
                        return true;
                    }
                }

                // If a panel is open, perform a shortcut on it without the
                // chorded panel key
                if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
                    if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
                        return true;
                    }
                }
            }
              // activity和Dialog都是Callback接口的具体实现,也是本文的重点
            if (!isDestroyed()) {
                final Callback cb = getCallback();
                final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
                        : super.dispatchKeyEvent(event);
                if (handled) {
                    return true;
                }
            }
                  // 如果都未处理,最后PhoneWindow本身来处理
            return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
                    : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
        }

如果activity和view都未处理,则会调用PhoneWindow的onKeyDown和onKeyUp函数。主要处理一些和系统相关的按键,比如音量上下键,照相机按键

Activity的dispatchKeyEvent方法如下,

public boolean dispatchKeyEvent(KeyEvent event) {
        onUserInteraction();
          // 1,处理菜单事件
        // Let action bars open menus in response to the menu key prioritized over
        // the window handling it
        if (event.getKeyCode() == KeyEvent.KEYCODE_MENU &&
                mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
            return true;
        }
         // 2,回调给PhoneWindow处理
        Window win = getWindow(); // 实质是PhoneWindow对象
        if (win.superDispatchKeyEvent(event)) {
            return true;
        }
        View decor = mDecor;
        if (decor == null) decor = win.getDecorView();
        // 3,最后按键事件本身来处理
        return event.dispatch(this, decor != null
                ? decor.getKeyDispatcherState() : null, this);
    }

由此可见,如果开发的activity中重写了dispatchKeyEvent方法,那么按键事件就不会分发到View树了。

   PhoneWindow的superDispatchKeyEvent方法直接调用DecorView的superDispatchKeyEvent方法, superDispatchKeyEvent方法如下,

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;
                }
            }
// DecorView的父类是FrameLayout,最后调用ViewGroup中的dispatchKeyEvent方法,终于走到View树中了。
            return super.dispatchKeyEvent(event);
        }

ViewGroup的dispatchKeyEvent方法如下,

public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 1);
        }
         // 直接调用view的dispatchKeyEvent方法
        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) {
              // 调用获取焦点的子类进行处理,该子类可能还是一个ViewGroup,所以可能会有递归调用。
            if (mFocused.dispatchKeyEvent(event)) {
                return true;
            }
        }

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
        }
        return false;
    }

View的dispatchKeyEvent方法如下,

public boolean dispatchKeyEvent(KeyEvent event) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onKeyEvent(event, 0);
        }

        // 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中的处理主要分为2个部分,

1,如果view中OnKeyListener对象不为空,则调用其onKey方法,

mListenerInfo是View的静态内部类ListenerInfo对象, 其mOnKeyListener变量指向view内部接口OnKeyListener, OnKeyListener定义如下,

public interface OnKeyListener { 
        boolean onKey(View v, int keyCode, KeyEvent event);
    }

开发时,设置view的按键事件监听方法如下,

startClientBtn.setOnKeyListener(new OnKeyListener(){
			@Override
			public boolean onKey(View arg0, int arg1, KeyEvent arg2) {
				
				return false;
			}
		});

按照上面的逻辑,setOnKeyListener方法肯定会给ListenerInfo的mOnKeyListener变量赋值, setOnKeyListener方法如下,

public void setOnKeyListener(OnKeyListener l) {
        getListenerInfo().mOnKeyListener = l; // 为mOnKeyListener对象赋值
    }
ListenerInfo getListenerInfo() {
        if (mListenerInfo != null) {
            return mListenerInfo;
        }
        mListenerInfo = new ListenerInfo();
        return mListenerInfo;
    }

2,如果view中没有监听按键事件,则调用KeyEvent的dispatch方法,并且传入了View对象,在activity中,传入的是activity对象。

下一篇文章分析KeyEvent如何处理按键事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值