【问题标题】:Upgraded to AppCompat v22.1.0 and now onKeyDown and onKeyUp are not triggered when menu key is pressed升级到 AppCompat v22.1.0 现在按下菜单键时不会触发 onKeyDown 和 onKeyUp
【发布时间】:2015-07-03 08:12:33
【问题描述】:

我刚刚升级了我的应用程序以使用新发布的 v22.1.0 AppCompat,现在按下菜单键时不会触发 onKeyDownonKeyUp。其他键正确触发onKeyDownonKeyUp,但是当我按下菜单键时没有任何反应。如果我降级到 v22.0.0,一切都会恢复正常。

我该如何解决?

【问题讨论】:

  • 回答您自己的问题?同时?
  • 是的,我在 Stack Overflow 的帮助中心听从了 article 的建议
  • AppCompat v22.2.0 上的菜单键似乎现在可以正确触发 onKeyDown 和 onKeyUp 事件。
  • Chris Banes 的 stated 版本 v22.2 解决了这个问题。

标签: android android-support-library onkeydown onkeyup


【解决方案1】:

8 月 23 日更新

这个has been fixed 再次出现在appcompat-v7 支持库的v23.0.0 中。更新到最新版本来解决这个问题。


7 月 19 日更新

不幸的是,AppCompat v22.2.1 破坏了onKeyDownonKeyUp 事件again。我刚刚更新了AppCompatActivityMenuKeyInterceptor 以支持 v22.1.x 和 v22.2.1


5 月 29 日更新

这个has been fixed 在appcompat-v7 支持库的v22.2.0 中。更新到最新版本来解决这个问题。


不幸的是,AppCompat v22.1.0 拦截了onKeyDownonKeyUp 事件,并且在按下菜单键时不会传播它们。唯一可能的解决方案是在 AppCompat 之前使用反射拦截 onKeyDownonKeyUp 事件。

将此类添加到您的项目中:

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

在您的活动的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;
}

现在您的活动也可以接收菜单键的onKeyDownonKeyUp 事件。

【讨论】:

  • 我在混淆我的代码后遇到了这个问题。保留支持库似乎有所帮助。如果这看起来不对,请对此发表评论,但我添加到我的 proguard-rules.pro -keep class android.support.v7.** { *; } -keep interface android.support.v7.** { *; }
  • @DaiwikDaarun 是的,您的规则是正确的,但您保留了所有支持库。如果你想要一个特定的规则,你可以看到更新的答案。感谢您指出这一点
  • 谢谢你,你拯救了我的一天
  • 很遗憾,这个问题和所有更新都专门针对KEYCODE_MENU,而不是KEYCODE_BACK
  • 对不起,先生,对于那些不使用 AppCompat 的人?我没有成功捕获 KeyUp 和 KeyDown。一些建议将不胜感激。谢谢。
【解决方案2】:

可以简单地使用dispatchKeyEvent(),而不是onKeyUp()onKeyDown()。从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);
    }
}

【讨论】:

  • 很遗憾,您的解决方案不起作用。在使用 v7 支持库 v22.1.0、v22.1.1 和 v22.2.1 时按下菜单键时不会传播 dispatchKeyEvent。这是一个已知错误,已在最新版本的 v7 支持库中修复。
  • 这意味着硬件菜单按键甚至在 22.0.1 上都不起作用
  • 正如您在标题中看到的那样,这个问题专门针对 v22.1.0 及更高版本(v22.1.0、v22.1.1 和 v22.2.1)
  • 在最新的 SDK 上非常适合我。
猜你喜欢
  • 2015-06-29
  • 1970-01-01
  • 2022-07-18
  • 1970-01-01
  • 2014-02-18
  • 2013-12-08
  • 2015-06-30
相关资源
最近更新 更多