「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」
Android按键事件传递流程(三)
应用层按键事件的传递
事件传递流程
这里主要讲解两部分,mView的创建和事件传递流程。
上节讲解了mView是如何创建的,下面继续讲解他是如何传递的。
DecorView的dispatchKeyEvent
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
final int action = event.getAction();
final boolean isDown = action == KeyEvent.ACTION_DOWN;
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 ((mWindow.mPanelChordingKey > 0) && (mWindow.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 ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
if (!mWindow.isDestroyed()) {
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
: mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
前面的两个判断是对快捷按键的处理。
//...
if (!mWindow.isDestroyed()) {
//cb代表一个Activity
final Window.Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
//...
mFeatureId:代表程序的特征表示或整个屏幕的标识,如果是应用程序则为-1,具体的流程如下:
Activity的oncreate方法->setContentView->PhoneWindow的setContentView-> installDecor()->generateDecor-> new DecorView(getContext(), -1)
如果Activity对象不为空,mFeatureId为-1,调用Activity对象的dispatchKeyEvent方法,如果为空,就调用super.dispatchKeyEvent(event)即父类ViewGroup的dispatchKeyEvent
如果返回值为true,表明事件已经被消耗了,按键事件不会在传递,否则会继续执行
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
将传递到PhoneWindow的onKeyDown、onKeyUp方法。
Activity的dispatchKeyEvent
Activity中的这个方法起到了拦截按键的作用,如果在这一步没有处理,则事件将继续向下分发给view或viewGroup去处理。
public boolean dispatchKeyEvent(KeyEvent event) {
//如果有按键、触摸轨迹球等事件分发给Activity时,会调用
onUserInteraction();
// Let action bars open menus in response to the menu key prioritized over
// the window handling it
final int keyCode = event.getKeyCode();
//如果Menu键且状态栏消耗了该键,则直接返回true
if (keyCode == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
}
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);
}
//...
Window win = getWindow();
if (win.superDispatchKeyEvent(event)) {
return true;
}
//...
通过getWindow返回PhoneWindow对象,将事件传递给PhoneWindow对象,主要是对Back按键松开的一些处理。
如果没有被消耗,则连同其他时间一起传递到父类的ViewGroup,父类没有处理则会传递到KeyEvent的dispatchKeyEvent。