升级到AppCompat v22.1.0,现在按下菜单键时不会触发onKeyDown和onKeyUp
Upgraded to AppCompat v22.1.0 and now onKeyDown and onKeyUp are not triggered when menu key is pressed
我刚刚升级了我的应用程序以使用新发布的 v22.1.0 AppCompat,现在按下菜单键时不会触发 onKeyDown
和 onKeyUp
。其他键正确触发 onKeyDown
和 onKeyUp
,但是当我按下菜单键时没有任何反应。如果我降级到 v22.0.0,一切 returns 都能正常工作。
我该如何解决?
8 月 23 日更新
此has been fixed再次出现在v23.0.0 的appcompat-v7 支持库中。更新到最新版本以查看此修复。
7 月 19 日更新
不幸的是,AppCompat v22.2.1 中断了 onKeyDown
和 onKeyUp
事件 again。我刚刚更新了 AppCompatActivityMenuKeyInterceptor
以支持 v22.1.x 和 v22.2.1
5 月 29 日更新
此 has been fixed 在 v22.2.0 的 appcompat-v7 支持库中。更新到最新版本以查看此修复。
不幸的是,AppCompat v22.1.0 拦截了 onKeyDown
和 onKeyUp
事件,并且在按下菜单键时不会传播它们。唯一可能的解决方案涉及使用反射在 AppCompat 之前拦截 onKeyDown
和 onKeyUp
事件。
将此 class 添加到您的项目中:
public class AppCompatActivityMenuKeyInterceptor {
private static final String FIELD_NAME_DELEGATE = "mDelegate";
private static final String FIELD_NAME_WINDOW = "mWindow";
public static void intercept(AppCompatActivity appCompatActivity) {
new AppCompatActivityMenuKeyInterceptor(appCompatActivity);
}
private AppCompatActivityMenuKeyInterceptor(AppCompatActivity activity) {
try {
Field mDelegateField = AppCompatActivity.class.getDeclaredField(FIELD_NAME_DELEGATE);
mDelegateField.setAccessible(true);
Object mDelegate = mDelegateField.get(activity);
Class mDelegateClass = mDelegate.getClass().getSuperclass();
Field mWindowField = null;
while (mDelegateClass != null) {
try {
mWindowField = mDelegateClass.getDeclaredField(FIELD_NAME_WINDOW);
break;
} catch (NoSuchFieldException ignored) {
}
mDelegateClass = mDelegateClass.getSuperclass();
}
if (mWindowField == null)
throw new NoSuchFieldException(FIELD_NAME_WINDOW);
mWindowField.setAccessible(true);
Window mWindow = (Window) mWindowField.get(mDelegate);
Window.Callback mOriginalWindowCallback = mWindow.getCallback();
mWindow.setCallback(new AppCompatWindowCallbackCustom(mOriginalWindowCallback, activity));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private class AppCompatWindowCallbackCustom extends WindowCallbackWrapper {
private WeakReference<AppCompatActivity> mActivityWeak;
public AppCompatWindowCallbackCustom(Window.Callback wrapped, AppCompatActivity appCompatActivity) {
super(wrapped);
mActivityWeak = new WeakReference<AppCompatActivity>(appCompatActivity);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
AppCompatActivity appCompatActivity = mActivityWeak.get();
if (appCompatActivity != null && keyCode == KeyEvent.KEYCODE_MENU) {
if (appCompatActivity.dispatchKeyEvent(event))
return true;
}
return super.dispatchKeyEvent(event);
}
}
}
在 activity 的 onCreate
中调用 AppCompatActivityMenuKeyInterceptor.intercept(this)
:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initialize the interceptor
AppCompatActivityMenuKeyInterceptor.intercept(this);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Now onKeyDown is called also for KEYCODE_MENU
if (keyCode == KeyEvent.KEYCODE_MENU) {
//do your stuff
//return false if you want to propagate the
//KeyEvent to AppCompat, return true otherwise
return false;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Now onKeyUp is called also for KEYCODE_MENU
if (keyCode == KeyEvent.KEYCODE_MENU) {
//do your stuff
//return false if you want to propagate the
//KeyEvent to AppCompat, return true otherwise
return false;
}
return super.onKeyUp(keyCode, event);
}
}
如果您使用 ProGuard 或 DexGuard,请将这些规则添加到您的配置中:
-keepclassmembers class android.support.v7.app.AppCompatActivity {
private android.support.v7.app.AppCompatDelegate mDelegate;
}
-keepclassmembers class android.support.v7.app.AppCompatDelegateImplBase {
final android.view.Window mWindow;
}
现在您的 activity 也可以接收菜单键的 onKeyDown
和 onKeyUp
事件。
代替onKeyUp()
或onKeyDown()
,可以简单地使用dispatchKeyEvent()
。从android-developers.blogspot.com.
看下面的代码
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0) {
// Tell the framework to start tracking this event.
getKeyDispatcherState().startTracking(event, this);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
getKeyDispatcherState().handleUpEvent(event);
if (event.isTracking() && !event.isCanceled()) {
// DO BACK ACTION HERE
return true;
}
}
return super.dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
}
我刚刚升级了我的应用程序以使用新发布的 v22.1.0 AppCompat,现在按下菜单键时不会触发 onKeyDown
和 onKeyUp
。其他键正确触发 onKeyDown
和 onKeyUp
,但是当我按下菜单键时没有任何反应。如果我降级到 v22.0.0,一切 returns 都能正常工作。
我该如何解决?
8 月 23 日更新
此has been fixed再次出现在v23.0.0 的appcompat-v7 支持库中。更新到最新版本以查看此修复。
7 月 19 日更新
不幸的是,AppCompat v22.2.1 中断了 onKeyDown
和 onKeyUp
事件 again。我刚刚更新了 AppCompatActivityMenuKeyInterceptor
以支持 v22.1.x 和 v22.2.1
5 月 29 日更新
此 has been fixed 在 v22.2.0 的 appcompat-v7 支持库中。更新到最新版本以查看此修复。
不幸的是,AppCompat v22.1.0 拦截了 onKeyDown
和 onKeyUp
事件,并且在按下菜单键时不会传播它们。唯一可能的解决方案涉及使用反射在 AppCompat 之前拦截 onKeyDown
和 onKeyUp
事件。
将此 class 添加到您的项目中:
public class AppCompatActivityMenuKeyInterceptor {
private static final String FIELD_NAME_DELEGATE = "mDelegate";
private static final String FIELD_NAME_WINDOW = "mWindow";
public static void intercept(AppCompatActivity appCompatActivity) {
new AppCompatActivityMenuKeyInterceptor(appCompatActivity);
}
private AppCompatActivityMenuKeyInterceptor(AppCompatActivity activity) {
try {
Field mDelegateField = AppCompatActivity.class.getDeclaredField(FIELD_NAME_DELEGATE);
mDelegateField.setAccessible(true);
Object mDelegate = mDelegateField.get(activity);
Class mDelegateClass = mDelegate.getClass().getSuperclass();
Field mWindowField = null;
while (mDelegateClass != null) {
try {
mWindowField = mDelegateClass.getDeclaredField(FIELD_NAME_WINDOW);
break;
} catch (NoSuchFieldException ignored) {
}
mDelegateClass = mDelegateClass.getSuperclass();
}
if (mWindowField == null)
throw new NoSuchFieldException(FIELD_NAME_WINDOW);
mWindowField.setAccessible(true);
Window mWindow = (Window) mWindowField.get(mDelegate);
Window.Callback mOriginalWindowCallback = mWindow.getCallback();
mWindow.setCallback(new AppCompatWindowCallbackCustom(mOriginalWindowCallback, activity));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private class AppCompatWindowCallbackCustom extends WindowCallbackWrapper {
private WeakReference<AppCompatActivity> mActivityWeak;
public AppCompatWindowCallbackCustom(Window.Callback wrapped, AppCompatActivity appCompatActivity) {
super(wrapped);
mActivityWeak = new WeakReference<AppCompatActivity>(appCompatActivity);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
AppCompatActivity appCompatActivity = mActivityWeak.get();
if (appCompatActivity != null && keyCode == KeyEvent.KEYCODE_MENU) {
if (appCompatActivity.dispatchKeyEvent(event))
return true;
}
return super.dispatchKeyEvent(event);
}
}
}
在 activity 的 onCreate
中调用 AppCompatActivityMenuKeyInterceptor.intercept(this)
:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initialize the interceptor
AppCompatActivityMenuKeyInterceptor.intercept(this);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Now onKeyDown is called also for KEYCODE_MENU
if (keyCode == KeyEvent.KEYCODE_MENU) {
//do your stuff
//return false if you want to propagate the
//KeyEvent to AppCompat, return true otherwise
return false;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Now onKeyUp is called also for KEYCODE_MENU
if (keyCode == KeyEvent.KEYCODE_MENU) {
//do your stuff
//return false if you want to propagate the
//KeyEvent to AppCompat, return true otherwise
return false;
}
return super.onKeyUp(keyCode, event);
}
}
如果您使用 ProGuard 或 DexGuard,请将这些规则添加到您的配置中:
-keepclassmembers class android.support.v7.app.AppCompatActivity {
private android.support.v7.app.AppCompatDelegate mDelegate;
}
-keepclassmembers class android.support.v7.app.AppCompatDelegateImplBase {
final android.view.Window mWindow;
}
现在您的 activity 也可以接收菜单键的 onKeyDown
和 onKeyUp
事件。
代替onKeyUp()
或onKeyDown()
,可以简单地使用dispatchKeyEvent()
。从android-developers.blogspot.com.
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0) {
// Tell the framework to start tracking this event.
getKeyDispatcherState().startTracking(event, this);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
getKeyDispatcherState().handleUpEvent(event);
if (event.isTracking() && !event.isCanceled()) {
// DO BACK ACTION HERE
return true;
}
}
return super.dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
}