如何为Flavor隐藏或添加菜单项?

6
我有一个应用程序,有三种不同的版本:fullpart1part2
每个版本都有不同的包名,因此我可以将它们作为不同的应用程序发布。
现在我只希望part1版本有一个名为Reload的菜单项,其他两个版本不应该有这个菜单项。这可能吗?
我尝试了使用菜单资源来实现以下操作:
app
|
+-src
  |
  +-full
  |
  +-main
  | |
  | +-res
  |   |
  |   +-menu
  |     |
  |     +-main_activity.xml
  |
  +-part1
  | |
  | +-res
  |   |
  |   +-menu
  |     |
  |     +-main_activity.xml
  |
  +-part2

part1main_activity.xml 位于:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/action_reload"
        android:icon="@drawable/ic_reload"
        android:title="@string/action_reload"
        app:showAsAction="always"/>
</menu>

main_activity.xml 中的 main 如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
</menu>

然而,如果我在除part1之外的任何其他构建变体中构建应用程序,则在需要对菜单选择做出反应的MainActivity中会出现编译错误:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.action_reload: // Compile error: This item is not available
            // TODO reload
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

很明显这是为什么。但是你有没有建议如何解决为不同的构建风格自定义菜单的问题?

1
你可以创建一个BaseActivity,其中包含通用代码和不同的MainActivity,每个口味都有不同版本的onOptionsItemSelected - Blackbelt
5个回答

13

还有另一种方法 - 使用布尔资源创建值文件,为每个风味设置不同的值,例如:

main/res/values/bool.xml :

 <resources>
        <bool name="show_reload">false</bool>
    </resources>

第一部分/res/values/bool.xml :

    <?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="show_reload">true</bool>
</resources>

然后在你的菜单资源中,根据资源设置可见性值:

<menu ..>
    <item ..
      android:visible="@bool/show_reload"
      ..
    />
</menu>

2
这是最佳解决方案。 - Keith Loughnane

12

如果您不想复制整个类文件,只想检测任何风味的设置并进行调整,则可以执行以下操作:

在 gradle 文件中创建 config 字段:

defaultConfig {
    ...
    buildConfigField "boolean", "SHOW_MY_MENU_ITEM", "true"
}
productFlavors {
    FooFlavour {
        ...
        buildConfigField "boolean", "SHOW_MY_MENU_ITEM", "false"
    }
}

然后构建Gradle。您可以在Activity中像这样访问此config字段:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.foo_menu, menu);
    if (!BuildConfig.SHOW_MY_MENU_ITEM) {
        MenuItem myItem = menu.findItem(R.id.my_menu_item);
        myItem.setVisible(false);
    }
    return super.onCreateOptionsMenu(menu);
}

类似的方法,但在我看来更好的方法是使用resValue,首先在gradle文件中创建它:

defaultConfig {
    ...
    resValue "bool", "show_my_menu_item", "true"
}
productFlavours {
    FooFlavour {
        ...
        resValue "bool", "show_my_menu_item", "false"
    }
}

在此之后,可以直接在menu.xml中访问resValue:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        ...
        android:visible="@bool/show_my_menu_item" />
</menu>

1
依我拙见,这是一个更好的答案。在您的菜单选项中创建所有选项,然后在产品风味中删除不需要的选项比拥有不同版本的MainActivity要容易得多。 - Simon Hutton
1
使用不同的方法更新答案,这次使用resValue。 - Darek Deoniziak

3

您还可以在菜单文件夹中创建另一个xml文件,并在其中创建相同的资源ID,例如:

app
|
+-src
  |
  +-full
  |
  +-main
  | |
  | +-res
  |   |
  |   +-menu
  |     |
  |     +-main_activity.xml
  |     +-dummy_menus.xml

然后在虚拟菜单中创建具有相同id的项。您不会使用它,因为它永远不会被选中,因为它从未被填充。


1
这不是一个坏答案。我评论过的两个答案都比被接受的答案好。 - Simon Hutton
我最近读到了另一个可能解决同样问题的答案:您可以在values文件夹中添加一个ids.xml文件,在其中声明仅id,正如我之前所述,从未真正使用它。 - Omaraf

1
在主源文件夹中创建一个名为MainActivity的文件,用于处理常规通用代码。在part1源文件夹中创建另一个名为MainActivity的文件,在其中覆盖onOptionsItemSelected方法,这样引用R.id.action_reload就不会有问题了。这样应该能够正常工作。

1
这不是一个好的解决方案,因为你最终会得到两个MainActivity类。@sashk0发布了一个适当和优雅的解决方案。 - Tomek Gozdek

0

您可以在帮助程序中定义onOptionsItemSelected(MenuItem item)方法的内容,然后使用不同的风味加载所需的帮助程序:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    HelperPart.selectItem(this, item);
}


// Helper loaded for flavor "part1"
static class MenuHelper{ 
    public static boolean selectItem(Activity act, MenuItem item){
        switch (item.getItemId()) {
            case R.id.action_reload: 
                // TODO reload
                return true;
            default:
                return act.onOptionsItemSelected(item);
        }
    }
}

// Helper loaded for flavor "part2" and "full"
static class MenuHelper{
    public static boolean selectItem(Activity act, MenuItem item){
        // Do nothing
    }
}

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