android keyevent.java,dispatchKeyEvent简单理解

当在launcher的allapps界面按下按键1(当前设备为带键盘的Android设备)的时候,PhoneWindowManager.java到Launcher.java的处理log如下。

2021-02-24 17:29:58.511 679-831/? I/WindowManager: interceptKeyTq keycode=8 interactive=true keyguardActive=false policyFlags=22000000

2021-02-24 17:29:58.512 679-831/? I/PhoneWindowManager: interceptKeyBeforeQueueing result=1

2021-02-24 17:29:58.514 679-830/? D/WindowManager: interceptKeyTi keyCode=8 down=true repeatCount=0 keyguardOn=false mHomePressed=false canceled=false metaState:0

2021-02-24 17:29:58.514 679-830/? I/PhoneWindowManager: interceptKeyBeforeDispatching result 0

2021-02-24 17:29:58.519 1438-1438/? I/Launcher: inner dispatchKeyEvent ,getCurrentFocus = com.android.launcher3.allapps.AllAppsRecyclerView{6a62e0e VFED..... .F...... 134,87-1146,740 #7f0b001e app:id/apps_list_view}

interceptKeyBeforeQueueing 返回1,interceptKeyBeforeDispatching返回0,事件没有被PhoneWindowManager拦截,走到了Launcher的public boolean dispatchKeyEvent(KeyEvent event)。

由于设备带按键KEYCODE_1-KEYCODE_POUND,要求在launcher界面按下KEYCODE_1-KEYCODE_POUND键会跳转至打电话界面,因此之前在Launcher的onKeyUp中定义了跳转拨号界面。

//vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

@Override

public boolean onKeyUp(int keyCode, KeyEvent event) {

Log.i("Launcher", "onKeyUp keyCode="+keyCode+"--event.action ="+event.getAction());

final boolean handled = super.onKeyUp(keyCode, event);

if (!handled&&shortPress){

switch(keyCode){

case KeyEvent.KEYCODE_0:

//定义了跳转至拨号界面

...............

return true;

case KeyEvent.KEYCODE_1:

//定义了跳转至拨号界面

...............

return true;

...............

case KeyEvent.KEYCODE_POUND:

//定义了跳转至拨号界面

...............

return true;

case KeyEvent.KEYCODE_MENU:

// Ignore the menu key if we are currently dragging or are on the custom content screen

if (!isOnCustomContent() && !mDragController.isDragging()) {

// Close any open floating view

AbstractFloatingView.closeAllOpenViews(this);

// Stop resizing any widgets

mWorkspace.exitWidgetResizeMode();

// Show the overview mode if we are on the workspace

if (mState == State.WORKSPACE && !mWorkspace.isInOverviewMode() &&

!mWorkspace.isSwitchingState()) {

mOverviewPanel.requestFocus();

showOverviewMode(true, true /* requestButtonFocus */);

}

}

return true;

}

}

return super.onKeyUp(keyCode, event);

}

但是,这里还有另一个问题,当显示了launcher的search_container_all_apps.xml的时候(即显示了 搜索应用这部分UI,因为大部分时候都会隐藏这个UI),

按下KEYCODE_1-KEYCODE_POUND按键时,例如按下KEYCODE_1,ExtendedEditText会先获取焦点,弹出软键盘,并在ExtendedEditText中显示输入的1,然后再跳转至拨号界面。

来看看当走到Launcher的dispatchKeyEvent中之后发生了什么。

//vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

Log.i(TAG, "inner dispatchKeyEvent ,getCurrentFocus "+getCurrentFocus());

return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);

}

按键的一次按下和抬起,会产生一次Action_Down和Action_Up。

当按键按下按键后,都产生 Action_Down ,抬起产生Action_Up。

在frameworks/base/core/java/android/app/Activity.java和frameworks/base/core/java/android/view/KeyEvent.java中加log会发现,首先会走到Launcher的dispatchKeyEvent,然后走到event.dispatch(this, decor != null ? decor.getKeyDispatcherState() : null, this),然后走到KeyEvent的public final boolean dispatch(Callback receiver, DispatcherState state,Object target)中,由于按键按下action是ACTION_DOWN,因此走到boolean res = receiver.onKeyDown(mKeyCode, this);

这里要对KeyEvent事件分发流程有一定的了解。

出处:https://www.cnblogs.com/chengliu/p/4130608.html

事件处理流程

1)InputManager负责读取事件并把事件送到frameworks的java层

2)WindowManagerService里会有一个InputMonitor类来监听事件变化并做相应的分发处理。

3)在WindowManagerService会有一个WindowManagerPolicy来做消息拦截处理。

4)WindowManagerService会把消息发给最上面运行的窗口接收

也就是说,当一个按键按下,产生KeyEvent事件,实际是由InputManager接收事件,传递给InputMonitor,然后会调用PhoneWindowManager中的interceptKeyBeforeDispatching和interceptKeyBeforeDispatching,判断是否拦截,当不拦截的时候,就会把KeyEvent往下传递,传递给最上面运行的窗口,也就是一个DecorView(实际是一个FrameLayout)。

然后走到了。

I/WindowManager: interceptKeyTq keycode=8 interactive=true keyguardActive=false policyFlags=22000000

I/PhoneWindowManager: interceptKeyBeforeQueueing result=1

D/WindowManager: interceptKeyTi keyCode=8 down=true repeatCount=0 keyguardOn=false mHomePressed=false canceled=false metaState:0

I/PhoneWindowManager: interceptKeyBeforeDispatching result 0

com.android.launcher3 I/Launcher: dispatchKeyEvent focus =com.android.launcher3.allapps.AllAppsRecyclerView{6a62e0e VFED..... .F...... 134,87-1146,740 #7f0b001e app:id/apps_list_view}

com.android.launcher3 I/AppsSearchContainerLayout: preDispatchKeyEvent request search field

com.android.launcher3 I/Launcher: onKeyDown keycode=8focus =com.android.launcher3.allapps.AllAppsRecyclerView{6a62e0e VFED..... .F...... 134,87-1146,740 #7f0b001e app:id/apps_list_view}

I/dex2oat: /system/bin/dex2oat --input-vdex-fd=18 --output-vdex-fd=19 --compiler-filter=speed-profile --profile-file-fd=22 --classpath-dir=/system/priv-app/MtkDocumentsUI --class-loader-context=PCL[]

I/WindowManager: interceptKeyTq keycode=8 interactive=true keyguardActive=false policyFlags=22000000

I/PhoneWindowManager: interceptKeyBeforeQueueing result=1

D/WindowManager: interceptKeyTi keyCode=8 down=false repeatCount=0 keyguardOn=false mHomePressed=false canceled=false metaState:0

I/PhoneWindowManager: interceptKeyBeforeDispatching result 0

com.android.launcher3 I/Launcher: dispatchKeyEvent focus =com.android.launcher3.allapps.AllAppsRecyclerView{6a62e0e VFED..... .F...... 134,87-1146,740 #7f0b001e app:id/apps_list_view}

com.android.launcher3 I/Launcher: onKeyUp keyCode=8--event.action =1

I/PhoneWindowManager: interceptKeyBeforeDispatching result 0

com.android.launcher3 I/Launcher: dispatchKeyEvent focus =com.android.launcher3.allapps.AllAppsRecyclerView{6a62e0e VFED..... .F...... 134,87-1146,740 #7f0b001e app:id/apps_list_view}

当按键1按下,走到Launcher的dispatchKeyEvent中,再到Activity的dispatchKeyEvent中,再到win.superDispatchKeyEvent(event),然后到了DecorView的superDispatchKeyEvent中。

//frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

@Override

public boolean superDispatchKeyEvent(KeyEvent event) {

return mDecor.superDispatchKeyEvent(event);

}

//frameworks/base/core/java/com/android/internal/policy/DecorView.java

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);

}

dispatchKeyEvent定义在ViewGroup中。

//frameworks/base/core/java/android/view/ViewGroup.java

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onKeyEvent(event, 1);

}

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) {

/// M : add log to help debugging

if (ViewDebugManager.DEBUG_KEY) {

Log.d(TAG, "dispatchKeyEvent to focus child event = " + event + ", mFocused = "

+ mFocused + ",this = " + this);

}

if (mFocused.dispatchKeyEvent(event)) {

return true;

}

}

if (mInputEventConsistencyVerifier != null) {

mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);

}

return false;

}

由于DecorView有焦点(此时焦点在AllAppsRecyclerView上),

DecorView中的mFocused != null,因此会调用ViewGroup的dispatchKeyEvent(KeyEvent event)中的

mFocused.dispatchKeyEvent(event)

dispatchKeyEvent(event)会遍历调用当DecorView下的View中dispatchKeyEvent。

我暂时没有弄明白,这个遍历到底是如何调用的,目前的log信息不足以显示出遍历过程,但是事实就是如此。

//vendor/mediatek/proprietary/packages/apps/Launcher3/res/layout/all_apps.xml

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:launcher="http://schemas.android.com/apk/res-auto"

android:id="@+id/apps_view"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

launcher:revealBackground="@drawable/round_rect_primary">

......................

由于遍历DecorView下的View,并调用dispatchKeyEvent,直至找到带焦点的View,所以一步步走到了AllAppsContainerView中的dispatchKeyEvent中。

//vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

mSearchUiManager.preDispatchKeyEvent(event);

return super.dispatchKeyEvent(event);

}

preDispatchKeyEvent在AppsSearchContainerLayout中重写,调用了focusSearchField。而调用focusSearchField会使ExtendedEditText弹出软键盘,使ExtendedEditText获取焦点,因此,屏蔽掉AppsSearchContainerLayout的preDispatchKeyEvent中的mSearchBarController.focusSearchField();即可。

//vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java

@Override

public void preDispatchKeyEvent(KeyEvent event) {

// Determine if the key event was actual text, if so, focus the search bar and then dispatch

// the key normally so that it can process this key event

if (!mSearchBarController.isSearchFieldFocused() &&

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

final int unicodeChar = event.getUnicodeChar();

final boolean isKeyNotWhitespace = unicodeChar > 0 &&

!Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);

if (isKeyNotWhitespace) {

boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,

event.getKeyCode(), event);

if (gotKey && mSearchQueryBuilder.length() > 0) {

Log.i("AppsSearchContainerLayout", "preDispatchKeyEvent request search field");

//mSearchBarController.focusSearchField();

}

}

}

}

由于是响应ACTION_DOWN,也可以直接将ACTION_DOWN的事件消费掉,不过这样也有可能带来别的问题,虽然没有测出来,还是直接改View中(即AppsSearchContainerLayout)的响应逻辑比较好。

//vendor/mediatek/proprietary/packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

@Override

public boolean dispatchKeyEvent(KeyEvent event) {

Log.i(TAG, "inner dispatchKeyEvent ,getCurrentFocus "+getCurrentFocus());

if ( event.getKeyCode() >= KeyEvent.KEYCODE_0 && event.getKeyCode() <= KeyEvent.KEYCODE_POUND && event.getAction() == KeyEvent.ACTION_DOWN){

return true;

}

return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);

}

出处:Android焦点事件分发与传递机制

要修改ViewGroup焦点事件的分发:

重写view的dispatchKeyEvent方法

给某个子view设置onKeyListener监听

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值