-->

How to hide or add menu items for Flavor?

2019-07-02 02:17发布

问题:

I have an app that has 3 different flavors, full, part1 and part2.

All different flavors have different package names, so I can ship them as different apps.

Now I want that only part1 gets a menu item called Reload. The other 2 flavors shouldn't have this menu item. Is this possible?

I tried the following with the menu resources:

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

Where main_activity.xml for part1 is:

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

And main_activity.xml for main is:

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

However, if I build the app in any other build variant than part1, I get a compilation error in my MainActivity where I need to react to the menu selection:

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

It's quite obvious why that is. But do you have any suggestion what the solution is to customize menus for different build flavors?

回答1:

Create a MainActivity in the main source folder where you handle the normal common code. Create another MainActivity in the part1 source folder where you override onOptionsItemSelected where it's not a problem to have references to R.id.action_reload. That should work.



回答2:

There's another way - create value file with boolean resource, with different value for each flavor e.g.:

main/res/values/bool.xml :

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

part1/res/values/bool.xml :

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

and then in your menu reource set the visibility value dependent on resource:

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


回答3:

If You don't want to copy whole class file, but to detect flavour or setting for any flavour and adjust it then do this:

In gradle file create config field:

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

then build gradle. You can access this config field in Activity like this:

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


回答4:

You can also create another xml file in the menu folder and create the same resource id inside it, for example:

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

And then in the dummy menu create an item with the same id. You just won't use it because it will never be selected, as it never was inflated.



回答5:

You can define the content of the onOptionsItemSelected(MenuItem item) method in a helper and then using flavors load the needed helper :

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