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

How to hide or add menu items for Flavor?

我的应用有 3 种不同的风格,fullpart1part2

所有不同的口味都有不同的包名,所以我可以将它们作为不同的应用程序发布。

现在我只希望 part1 获得名为 Reload 的菜单项。其他 2 种口味不应该有这个菜单项。这可能吗?

我尝试了以下菜单资源:

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

其中 main_activity.xml 对于 part1 是:

<?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>

mainmain_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">
</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);
    }
}

原因很明显。但是您有什么建议可以解决为不同构建风格自定义菜单的解决方案吗?

在处理普通公共代码的主源文件夹中创建一个 MainActivity。在覆盖 onOptionsItemSelected 的 part1 源文件夹中创建另一个 MainActivity,其中引用 R.id.action_reload 不是问题。那应该可以。

您可以在助手中定义 onOptionsItemSelected(MenuItem item) 方法的内容,然后使用 flavors 加载所需的助手:

@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
    }
}

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

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

然后在虚拟菜单中创建一个具有相同 ID 的项目。您只是不会使用它,因为它永远不会被选中,因为它从未被膨胀。

如果您不想复制整个 class 文件,而是要检测风味或任何风味的设置并进行调整,请执行以下操作:

在 gradle 文件中创建配置字段:

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

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

@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"
    }
}

此后resValue可以直接在menu.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:visible="@bool/show_my_menu_item" />
</menu>

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

主要/res/values/bool.xml :

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

第 1 部分/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>