升级到Cordova 5 + Cordova Android 4.0.0后,menubutton不再起作用

11

我最近升级到了Cordova 5,并在版本4.0.0中删除/重新创建了Android平台,卸载/重新安装了所有插件。

我还不得不将Android SDK升级到sdk 22而不是21。

自从升级以来,我无法像 cordova文档中描述的那样捕获menubutton事件。

由于它在最新版本的文档中仍然被引用,我认为它应该仍然有效,而且在发布说明中也没有关于此问题的说明。

返回按钮仍然可以正常工作。

我尝试将target-sdk设置为19,但并没有解决这个问题。

编辑: 我深入挖掘了Cordova源代码,在CordovaWebViewImpl.java中找到了一个可疑的TODO注释:

   public void setButtonPlumbedToJs(int keyCode, boolean override) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_BACK:
                // TODO: Why are search and menu buttons handled separately?
                if (override) {
                    boundKeyCodes.add(keyCode);
                } else {
                    boundKeyCodes.remove(keyCode);
                }
                return;
            default:
                throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
        }
    }

好的,我的回答是“它不应该这样!”

Cordova列出了处理按键代码的列表,但没有添加菜单按钮,之后将按键代码与KeyEvent.KEYCODE_MENU进行比较,只有在跳过按键代码后才会进行比较,因为它不在列表中。

我尝试添加菜单按钮的情况,但结果发现该函数仅使用返回按钮的代码调用。

所以现在我知道为什么它不起作用,但还不知道如何修复它。

编辑02/2016: 根据最新的Jira,菜单按钮的支持现在已在Cordova Android 5.1.0的Java部分中得到修复,但仍未从JavaScript初始化。 目前,如Jira用户Keith Wong所指示,您需要在添加事件侦听器之前添加JavaScript调用:

document.addEventListener("deviceready", function() {
    ...
    navigator.app.overrideButton("menubutton", true);  // <-- Add this line
    document.addEventListener("menubutton", yourCallbackFunction, false);
    ...
}, false);

是的,这就是我要做的...只要我回想起在哪里! - QuickFix
3
以下是需要翻译的内容:https://issues.apache.org/jira/browse/CB-7248?jql=project%20%3D%20CB%20AND%20resolution%20%3D%20Unresolved%20AND%20issuetype%20%3D%20Bug%20ORDER%20BY%20priority%20DESC这是一个指向 Apache Cordova 项目的 JIRA bug 追踪链接,按优先级从高到低列出了所有未解决的 Bug。 - drum
1
谢谢,我发现这个问题已经在一个月前被报告了,但似乎回答的人并没有真正理解这个问题,并认为menubutton只存在于旧的Android 2.3设备上 :( https://issues.apache.org/jira/browse/CB-8921 - QuickFix
非常感謝您提出這個問題,多虧了您,我有了一個很強的線索,我會像JIRA討論區中那位不錯的用戶建議的那樣“禁用boundKeyCodes檢查”,這類問題令人沮喪,我有幾十個類似的補丁,我會在cordova /插件的各個部分通過編程方式應用它們。 - Kaan Soral
1
@Andrew,看看我的最后一次编辑,你只需要调用navigator.app.overrideButton("menubutton", true);就可以让它工作了。 - QuickFix
显示剩余3条评论
3个回答

4

clarent的回答对我没有用,菜单按钮仍然没有响应。

我尝试了几个补丁,另一个建议完全禁用boundKeyCodes检查也没用,因为那样会破坏后退按钮的行为。

恢复旧行为的干净方法如下。boundKeyCodes检查确保只有在实际绑定了自定义事件处理程序时才执行自定义行为。但是,在您应用程序的JS代码中将事件处理程序绑定到“menubutton”不再触发menubutton键代码添加到boundKeyCodes列表中。这是因为setButtonPlumbedToJs方法首先根本没有执行“menubutton”处理程序,即使它执行了,此方法中的switch语句也无法处理KEYCODE_MENU。

您可以很容易地恢复该行为,首先必须应用clarent建议的更改:

  1. 处理KEYCODE_MENU

在CordovaLib/src/org/apache/cordova/CoreAndroid.java(大约在第357行setButtonPlumbedToJs附近)中添加一个case语句,类似于KEYCODE_BACK条目之后:

public void setButtonPlumbedToJs(int keyCode, boolean override) {
  switch (keyCode) {
    case KeyEvent.KEYCODE_VOLUME_DOWN:
    case KeyEvent.KEYCODE_VOLUME_UP:
    case KeyEvent.KEYCODE_BACK:
    case KeyEvent.KEYCODE_MENU:
    // TODO: Why are search and menu buttons handled separately?
      if (override) {
        boundKeyCodes.add(keyCode);
      } else {
        boundKeyCodes.remove(keyCode);
      }
      return;
    default:
      throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
  }
}

确保setButtonPlumbedToJs实际上得到执行。你需要再做两个更改。

  1. 添加框架处理程序

在CordovaLib/src/org/apache/cordova/CoreAndroid.java(大约在第243行,overrideButton处),使该方法看起来像这样(添加最后的else-if子句):

public void overrideButton(String button, boolean override) {

  if (button.equals("volumeup")) {
    webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
  }
  else if (button.equals("volumedown")) {
    webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
  }
  else if (button.equals("menubutton")) {
    webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_MENU, override);
  }
}
  1. 添加JavaScript处理程序调用

在platform_www/cordova.js(大约在第1532行,引导程序附近),更改此行:

cordova.addDocumentEventHandler('menubutton');

转换为:

var menuButtonChannel = cordova.addDocumentEventHandler('menubutton');
menuButtonChannel.onHasSubscribersChange = function() {
  exec(null, null, APP_PLUGIN_NAME, "overrideButton", ['menubutton', this.numHandlers == 1]);
};

一旦将事件处理程序添加到“menubutton”,这将触发框架的overrideButton方法。

就是这样。我还将此解决方案作为评论添加到https://issues.apache.org/jira/browse/CB-8921,并可能很快提交拉取请求。


每次重新添加Android平台(基本上是每次升级Cordova和/或平台)时,所有内容都将被重建并丢失。 - andreszs

0

现在使用cordova-android 5.1,代码已经改变,我的补丁不再起作用了(遗憾的是,在这个版本中没有补丁,菜单按钮仍然无法工作)。

为了能够升级平台而不必每次都重新审查代码,我寻找了一种新的方法来使菜单按钮再次工作。

在cordova android 5.1中,事实证明所有与按钮工作有关的java代码都在这里,只是菜单按钮键从未添加到boundKeyCoded数组中。

事实证明,这个数组需要通过javascript调用来填充(对于返回按钮和音量按钮已经完成,但对于搜索按钮或菜单按钮则没有)。

缺失的代码类似于:

exec(null, null, APP_PLUGIN_NAME, 'overrideButton', ['menubutton' , true]);

(一个JS调用Java函数CoreAndroid.java中的overrideButton来告诉其将菜单按钮键添加到boundKeyCodes数组中。)

我认为这个调用应该被加入到platform.js中,但由于platform.js用于构建cordova.js文件,在平台添加期间,我决定创建一个after_platform_add hook来修补cordova.js文件。

这个钩子的优点是无需更改Java代码,在使用像Crosswalk这样的不同Webview的情况下也能正常工作。

因此,首先在config.xml文件中的android部分添加钩子:

<platform name="android">
    ....
    ....
    <hook type="after_platform_add" src="scripts/android/patch_menubutton.js" />
    ....
    ....
</platform>

然后,在脚本文件夹中添加钩子文件patch_menubutton.js

#!/usr/bin/env node
module.exports = function(ctx) {
    var fs = ctx.requireCordovaModule('fs'),
        path = ctx.requireCordovaModule('path');
    var CordovaJSPath = path.join(ctx.opts.projectRoot, 'platforms/android/platform_www/cordova.js');
    var data = fs.readFileSync(CordovaJSPath, 'utf8');
    var result = data.replace(new RegExp("cordova\\.addDocumentEventHandler\\('menubutton'\\);", "g"), "cordova.addDocumentEventHandler('menubutton'); exec(null, null, APP_PLUGIN_NAME, 'overrideButton', ['menubutton' , true]);");
    fs.writeFileSync(CordovaJSPath, result, 'utf8');
}

它寻找菜单按钮事件处理程序的初始化,并追加对overrideButton函数的调用,就像FewKinG答案的最后部分所描述的那样。


现在这个变得没用了,因为你只需要从JavaScript中调用navigator.app.overrideButton("menubutton", true);来初始化按钮。 - QuickFix

0
只需在函数setButtonPlumbedToJs中添加一行:case KeyEvent.KEYCODE_MENU:
public void setButtonPlumbedToJs(int keyCode, boolean override) {
    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_BACK:
        case KeyEvent.KEYCODE_MENU:

所以在 onDispatchKeyEvent 中,switch 语句会起作用:

      } else if (boundKeyCodes.contains(keyCode)) {
                String eventName = null;
                switch (keyCode) {
                    case KeyEvent.KEYCODE_VOLUME_DOWN:
                        eventName = "volumedownbutton";
                        break;
                    case KeyEvent.KEYCODE_VOLUME_UP:
                        eventName = "volumeupbutton";
                        break;
                    case KeyEvent.KEYCODE_SEARCH:
                        eventName = "searchbutton";
                        break;
                    case KeyEvent.KEYCODE_MENU:
                        eventName = "menubutton";
                        break;
                    case KeyEvent.KEYCODE_BACK:
                        eventName = "backbutton";
                        break;
                }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接