AndroidにおけるkeyEventのメッセージ処理
14959 ワード
1. ViewRootImpl.deliverKeyEvent(QueuedInputEvent q)
1. mViewが空またはmAddedがfalseの場合、finishInputEventが直接呼び出されます.
2. mView.dispatchKeyEventPreIme(event)は、IMEに渡す前にいくつかの前処理を行う.ビューでは、入力ウィンドウが存在する場合、キーメッセージが入力ウィンドウに送信されます.入力ウィンドウがこのイベントを処理していない場合にのみ、本物のビューに送信されます.したがって、入力方式でイベントを切り取る前にメッセージを処理する場合は、この方法を再ロードして特定のキーメッセージを処理することができる.
3. IMEウィンドウがあれば、これをIMEに渡して処理します.imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
4. deliverKeyEventPostIme(q); メッセージを本物のビューに送信します.
1.2 ViewRootImpl.deliverKeyEventPostIme(q);
1. mView.dispatchKeyEvent(event); イベントをビューに送信します.mViewはDecorViewなので、DecorViewのdispatchKeyEventを呼び出す方法です.
1.2.1 DecorView.dispatchKeyEvent(KeyEvent event)
1. Downイベントの場合は、まずShortcutイベント、すなわちショートカットボタンまたは特殊イベントかどうかを判断し、dispatchKeyShortcutEventとperformPanelShortcutを呼び出していくつかの処理を行い、戻ってくる.
2. ショートカットボタンでない場合はCallback Activityのcbを呼び出す.dispatchKeyEvent(event)で処理します.ActivityのdispatchKeyEventでもPhoneWindowのsuperDispatchKeyEventを呼び出し、PhoneWindowは結局mDecorを呼び出す.superDispatchKeyEvent(event);
1.2.1.2 DecorView.mDecor.superDispatchKeyEvent(event);
1. 親クラスのdispatchKeyEventを呼び出します.
1.2.1.2.1 ViewGroup.dispatchKeyEvent(KeyEvent event)
1. mPrivateFlagsは、現在のViewのプロパティを示すビューで定義されています.ここかなFOCUSEDとHAS_BOUNDSは、現在のViewがfocusedでboundsがあるかどうかを示します.一般的なビューとビューグループにはHAS_がありますBOUNDS、mFocusedは、現在のViewGroupのfocusのViewを記録します.
例えば、AがBを含む場合、BがCを含む場合、CがViewである場合、CのmPrivateFlagsはFOCUSED|HAS_であるBOUNDSではBのmPrivateFlagsがFOCUSED、mFocusedがC、同理AのmPrivateFlagsもFOCUSED、mFocusedがBです.
2. だから一般的には、ViewGroupはmFocusedを通過します.dispatchKeyEvent()は、EventをViewに再帰的に伝えます.
1.2.1.2.1.2 View.dispatchKeyEvent()
mFocusedがView Groupの場合、1.2.1.2.1が呼び出されます.Viewの場合は、ViewのdispatchKeyEventを呼び出します.
1. Listenerが登録されている場合は、ListenerのonKeyを直接呼び出して対応させ、返します.
2. Listenerが登録されていない場合はKeyEventを呼び出す.dispatchは最後の処理をします
1.2.1.2.1.2.2 KeyEvent.dispatch
1. KeyEvent.dispatchはActionのタイプに応じてreceiverの対応するonXXX関数を呼び出して処理します.(receiverは通常View)
2. すべてのViewがこのKeyEventを処理していない場合、ActivityのonXXX関数は最終的にKeyEventによって呼び出されます.
1. mViewが空またはmAddedがfalseの場合、finishInputEventが直接呼び出されます.
2. mView.dispatchKeyEventPreIme(event)は、IMEに渡す前にいくつかの前処理を行う.ビューでは、入力ウィンドウが存在する場合、キーメッセージが入力ウィンドウに送信されます.入力ウィンドウがこのイベントを処理していない場合にのみ、本物のビューに送信されます.したがって、入力方式でイベントを切り取る前にメッセージを処理する場合は、この方法を再ロードして特定のキーメッセージを処理することができる.
3. IMEウィンドウがあれば、これをIMEに渡して処理します.imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
4. deliverKeyEventPostIme(q); メッセージを本物のビューに送信します.
private void deliverKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
}
if ((q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
finishInputEvent(q, false);
return;
}
if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
// Perform predispatching before the IME.
if (mView.dispatchKeyEventPreIme(event)) {
finishInputEvent(q, true);
return;
}
// Dispatch to the IME before propagating down the view hierarchy.
// The IME will eventually call back into handleImeFinishedEvent.
if (mLastWasImTarget) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final int seq = event.getSequenceNumber();
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+ seq + " event=" + event);
imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
return;
}
}
}
// Not dispatching to IME, continue with post IME actions.
deliverKeyEventPostIme(q);
}
1.2 ViewRootImpl.deliverKeyEventPostIme(q);
1. mView.dispatchKeyEvent(event); イベントをビューに送信します.mViewはDecorViewなので、DecorViewのdispatchKeyEventを呼び出す方法です.
private void deliverKeyEventPostIme(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
... ...
// If the key's purpose is to exit touch mode then we consume it and consider it handled.
if (checkForLeavingTouchModeAndConsume(event)) {
finishInputEvent(q, true);
return;
}
// Make sure the fallback event policy sees all keys that will be delivered to the
// view hierarchy.
mFallbackEventHandler.preDispatchKeyEvent(event);
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
finishInputEvent(q, true);
return;
}
// If the Control modifier is held, try to interpret the key as a shortcut.
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.isCtrlPressed()
&& event.getRepeatCount() == 0
&& !KeyEvent.isModifierKey(event.getKeyCode())) {
if (mView.dispatchKeyShortcutEvent(event)) {
finishInputEvent(q, true);
return;
}
}
// Apply the fallback event policy.
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
finishInputEvent(q, true);
return;
}
// Handle automatic focus changes.
if (event.getAction() == KeyEvent.ACTION_DOWN) {
int direction = 0;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (event.hasNoModifiers()) {
direction = View.FOCUS_LEFT;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (event.hasNoModifiers()) {
direction = View.FOCUS_RIGHT;
}
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (event.hasNoModifiers()) {
direction = View.FOCUS_UP;
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (event.hasNoModifiers()) {
direction = View.FOCUS_DOWN;
}
break;
case KeyEvent.KEYCODE_TAB:
if (event.hasNoModifiers()) {
direction = View.FOCUS_FORWARD;
} else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
direction = View.FOCUS_BACKWARD;
}
break;
}
if (direction != 0) {
View focused = mView.findFocus();
if (focused != null) {
View v = focused.focusSearch(direction);
if (v != null && v != focused) {
// do the math the get the interesting rect
// of previous focused into the coord system of
// newly focused view
focused.getFocusedRect(mTempRect);
if (mView instanceof ViewGroup) {
((ViewGroup) mView).offsetDescendantRectToMyCoords(
focused, mTempRect);
((ViewGroup) mView).offsetRectIntoDescendantCoords(
v, mTempRect);
}
if (v.requestFocus(direction, mTempRect)) {
playSoundEffect(SoundEffectConstants
.getContantForFocusDirection(direction));
finishInputEvent(q, true);
return;
}
}
// Give the focused view a last chance to handle the dpad key.
if (mView.dispatchUnhandledMove(focused, direction)) {
finishInputEvent(q, true);
return;
}
}
}
}
// Key was unhandled.
finishInputEvent(q, false);
}
1.2.1 DecorView.dispatchKeyEvent(KeyEvent event)
1. Downイベントの場合は、まずShortcutイベント、すなわちショートカットボタンまたは特殊イベントかどうかを判断し、dispatchKeyShortcutEventとperformPanelShortcutを呼び出していくつかの処理を行い、戻ってくる.
2. ショートカットボタンでない場合はCallback Activityのcbを呼び出す.dispatchKeyEvent(event)で処理します.ActivityのdispatchKeyEventでもPhoneWindowのsuperDispatchKeyEventを呼び出し、PhoneWindowは結局mDecorを呼び出す.superDispatchKeyEvent(event);
@Override
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 ((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;
}
}
}
if (!isDestroyed()) {
final Callback cb = getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
return true;
}
}
return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
: PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
1.2.1.2 DecorView.mDecor.superDispatchKeyEvent(event);
1. 親クラスのdispatchKeyEventを呼び出します.
public boolean superDispatchKeyEvent(KeyEvent event) {
if (super.dispatchKeyEvent(event)) {
return true;
}
// Not handled by the view hierarchy, does the action bar want it
// to cancel out of something special?
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
final int action = event.getAction();
// Back cancels action modes first.
if (mActionMode != null) {
if (action == KeyEvent.ACTION_UP) {
mActionMode.finish();
}
return true;
}
// Next collapse any expanded action views.
if (mActionBar != null && mActionBar.hasExpandedActionView()) {
if (action == KeyEvent.ACTION_UP) {
mActionBar.collapseActionView();
}
return true;
}
}
return false;
}
1.2.1.2.1 ViewGroup.dispatchKeyEvent(KeyEvent event)
1. mPrivateFlagsは、現在のViewのプロパティを示すビューで定義されています.ここかなFOCUSEDとHAS_BOUNDSは、現在のViewがfocusedでboundsがあるかどうかを示します.一般的なビューとビューグループにはHAS_がありますBOUNDS、mFocusedは、現在のViewGroupのfocusのViewを記録します.
例えば、AがBを含む場合、BがCを含む場合、CがViewである場合、CのmPrivateFlagsはFOCUSED|HAS_であるBOUNDSではBのmPrivateFlagsがFOCUSED、mFocusedがC、同理AのmPrivateFlagsもFOCUSED、mFocusedがBです.
2. だから一般的には、ViewGroupはmFocusedを通過します.dispatchKeyEvent()は、EventをViewに再帰的に伝えます.
public boolean dispatchKeyEvent(KeyEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 1);
}
if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
if (super.dispatchKeyEvent(event)) {
return true;
}
} else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
if (mFocused.dispatchKeyEvent(event)) {
return true;
}
}
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
}
return false;
}
1.2.1.2.1.2 View.dispatchKeyEvent()
mFocusedがView Groupの場合、1.2.1.2.1が呼び出されます.Viewの場合は、ViewのdispatchKeyEventを呼び出します.
1. Listenerが登録されている場合は、ListenerのonKeyを直接呼び出して対応させ、返します.
2. Listenerが登録されていない場合はKeyEventを呼び出す.dispatchは最後の処理をします
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;
}
1.2.1.2.1.2.2 KeyEvent.dispatch
1. KeyEvent.dispatchはActionのタイプに応じてreceiverの対応するonXXX関数を呼び出して処理します.(receiverは通常View)
2. すべてのViewがこのKeyEventを処理していない場合、ActivityのonXXX関数は最終的にKeyEventによって呼び出されます.
public final boolean dispatch(Callback receiver, DispatcherState state,
Object target) {
switch (mAction) {
case ACTION_DOWN: {
mFlags &= ~FLAG_START_TRACKING;
if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state
+ ": " + this);
boolean res = receiver.onKeyDown(mKeyCode, this);
if (state != null) {
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
if (DEBUG) Log.v(TAG, " Start tracking!");
state.startTracking(this, target);
} else if (isLongPress() && state.isTracking(this)) {
try {
if (receiver.onKeyLongPress(mKeyCode, this)) {
if (DEBUG) Log.v(TAG, " Clear from long press!");
state.performedLongPress(this);
res = true;
}
} catch (AbstractMethodError e) {
}
}
}
return res;
}
case ACTION_UP:
if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state
+ ": " + this);
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;
}