当onPrepareOptionsMenu切换菜单项时,我该如何刷新ActionBar?

63
在我的应用程序中,我经常启用/禁用菜单条目并使它们从onPrepareOptionsMenu中可见。
今天,我开始为一些Android 2.x应用程序添加android:showAsAction菜单属性,以在ActionBar上显示最常用的菜单项。
ActionBar不会立即反映启用/禁用和可见性。我需要点击右侧的菜单下拉列表才能看到这种变化发生。
好的,我知道菜单会触发onPrepareOptionsMenu事件。但是我需要做什么来刷新ActionBar呢?我认为这个更改需要从onOptionsItemSelected中应用,但我不知道我应该调用什么方法。
以下是菜单:
<item
    android:icon="@drawable/ic_menu_mapmode"
    android:id="@+id/men_mapview"
    android:showAsAction="ifRoom|withText"
    android:title="@string/txt_mapview" />

<item
    android:icon="@drawable/ic_menu_mapmode"
    android:id="@+id/men_satelliteview"
    android:showAsAction="ifRoom|withText"
    android:title="@string/txt_satelliteview" />

这里是 onPrepareOptionsMenu 方法:

@Override
public boolean onPrepareOptionsMenu(final Menu menu) {
    MenuItem menuItemMapView = menu.findItem(R.id.men_mapview);
    MenuItem menuItemSatelliteView = menu.findItem(R.id.men_satelliteview);

    if (mapView.isSatellite()) {
        menuItemMapView.setEnabled(true).setVisible(true);
        menuItemmenuItemSatelliteView.setEnabled(false).setVisible(false);
    } else {
        menuItemMapView.setEnabled(false).setVisible(false);
        menuItemmenuItemSatelliteView.setEnabled(true).setVisible(true);
    }

    return super.onPrepareOptionsMenu(menu);
}

这里是onOptionsItemSelected函数

@Override
public boolean onOptionsItemSelected(final MenuItem menuItem) {
    switch (menuItem.getItemId()) {
        case R.id.men_mapview:
            mapView.setSatellite(false);
            mapView.setStreetView(true);
            mapView.invalidate();

            invalidateOptionsMenu(); // This works on Android 3.x devices only
            return true;
        case R.id.men_satelliteview:
            mapView.setSatellite(true);
            mapView.setStreetView(false);
            mapView.invalidate();

            invalidateOptionsMenu(); // This works on Android 3.x devices only
            return true;
    }

    return super.onOptionsItemSelected(menuItem);
}

编辑:如果我添加invalidateOptionsMenu,这将在Android 3.x应用程序上运行,但由于缺少方法,在Android 2.x设备上会崩溃。有什么推荐的正确方法吗?

10个回答

88

我选择的方法是创建一个辅助类。例如:

class VersionHelper
{
    static void refreshActionBarMenu(Activity activity)
    {
        activity.invalidateOptionsMenu();
    }
}

现在,在您上面的代码中,将invalidateOptionsMenu();替换为:

if (Build.VERSION.SDK_INT >= 11)
{
    VersionHelper.refreshActionBarMenu(this);
}

这种方法的功劳归功于CommonsWare(搜索HoneycombHelper,并查看他的书籍-强烈推荐)。


5
你的回答和直接在if(Build.VERSION.SDK_INT >= 11){invalidateOptionsMenu();}中加入版本检查有什么不同?可能把版本检查放在versionHelper方法调用中是可行的吗? - topwik
10
他将帮助类结构化的原因是为了避免因为不向后兼容而导致崩溃。方法 invalidateOptionsMenu() 仅存在于 API 11(蜂巢)及以上版本。因此,如果您的应用程序运行在低于 API 11 的任何版本上,它将崩溃。为了避免这种情况,您需要将相关方法包装在另一个 静态 方法中(例如 refreshActionBarMenu()),并且仅在API> 11上运行时才调用此静态方法(因此在调用静态方法之前进行版本检查)。这可行的原因是 VersionHelper 类不会在您实际使用它之前加载。 - Tony Chan
@Turbo 好的,谢谢你的解释。那么在 else 的情况下应该怎么做呢?是不更新菜单还是有其他旧操作系统下更新菜单项的方法? - topwik
3
@towpse,我正在解决这个问题。我认为在这种情况下,对于else情况,你应该什么也不做。这是因为旧的方法(API11之前)更新菜单的方法是onPrepareOptionsMenu,它会在用户打开菜单时自动调用。因此,为了处理旧操作系统和新的API>11,您将菜单更改的代码放在onPrepareOptionsMenu中(这是为了处理旧操作系统),然后为了处理新操作系统,在事件触发菜单更改时调用invalidateOptionsMenu - Tony Chan
1
如果辅助类可以保护旧版SDK,那么为什么不在方法内部进行版本检查呢? - Brill Pappin
@BrillPappin 我认为这就是CommonsWare的书的重点。如果您在其余代码中进行API检查,则不需要帮助程序。与@Turbo所说的不同,如果它不进入“if”,应用程序不会在旧设备上崩溃。通过使用VersionHelper并将这些if放在其中,您可以使代码更加清晰。 - Ricardo

68

感谢被接受的答案。我正在使用ActionBarActivity类。在这个类中,你可以使用

supportInvalidateOptionsMenu();

4
给这个人一个饼干……我不能将菜单的可见性设置为假的原因是我一直只使用invalidateOptionsMenu(),直到找到了你的答案。谢谢,老兄。 - Cjames
4
这应该是被选中的答案。 - Guy
由于工具栏中的操作(图标)现在是由TextView而不是ImageView内部表示(我理解为什么),因此我必须手动重新着色TextView内部的复合可绘制对象,并另外分配一个TouchListener,以便在按下/激活时重新着色它们。所以基本上我只是给图标上色。有类似情况的人吗?使菜单无效对我没有用... - milosmns
1
该方法已被弃用。 - Amir Dora.

15

在 Gingerbread 上,调用此方法后 onCreateOptionsMenu() 方法不会被调用。 - lemuel
1
是的。这是因为preHoneycomb不支持屏幕按钮,需要一个硬件菜单按钮。所以当用户按下菜单按钮时,会调用onCreateOptionMenu()方法。invalidateOptionsMenu专注于刷新支持旧菜单硬件按钮的actionbar 3.0+。 - Urizev

11
保存菜单的引用并调用:
this.menu.clear();
this.onCreateOptionsMenu(this.menu);

3
在API 11之前的设备上,通过编程方式切换操作栏中选项菜单项的可见性的正确答案。 - Mike Repass

4

4

根据以上“Klaasvaak”回答,我正在使用它的子菜单。这对我有效:

// Declare and save the menu as global, so it can be called anywhere.
Menu absTopSubMenus;

public boolean onCreateOptionsMenu(Menu menu) {

absTopSubMenus = menu;  // Used for re-drawing this menu anywhere in the codes.

// The remainder of your code below
}

然后,要重新绘制它,只需调用:
// Redraw the top sub-menu
absTopSubMenus.clear();
onCreateOptionsMenu(absTopSubMenus);

谢谢,它节省了我很多时间! - hetsgandhi

3

我不确定您是否已经看到了它,但如果您广泛使用操作栏,并计划支持较低的API(大于8),请查看actionBarSherlock库。它将使您无需为较低和较高的API分别编写操作栏代码,然后您只需要运行:

runOnUiThread(new Runnable(){
  @Override
  public void run(){
    supportInvalidateOptionsMenu();
  }
});

3

获取当前活动并使其选项菜单无效。

完成任务。


2

感谢@Klaasvaak向我们展示了这里的方法。我使用以下内容,可在API Level 11之前和之后都有效:

private void invalidOptionsMenuHelper() {
    if (Build.VERSION.SDK_INT >= 11) {
        invalidateOptionsMenu();

    } else if (mOptionsMenu != null) {
        mOptionsMenu.clear();
        onCreateOptionsMenu(mOptionsMenu);
    }
}

当然,你必须保存对菜单的引用(在这种情况下是mOptionsMenu),我通过以下方式实现:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    mOptionsMenu = menu;

    //... create and manage the menu normally
}

0

同时确保您没有调用

myAppCompatActivity.setToolbarTitle("some dynamic title");

在您刷新菜单后不久。

我遇到了这样的问题,即使有足够的空间显示它们,可绘制对象也不会显示出来。一旦我进行方向更改,可绘制对象就会出现。?

总之:

MainActivity:

public class MainActivity extends AppCompatActivity {

    private boolean showMenu = true;

    public void setShowMenu(boolean show) {
        showMenu = show;
        supportInvalidateOptionsMenu();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.my_menu, menu);
        menu.findItem(R.id.menu_share).setVisible(showMenu);
        // menu.findItem(...
        return true;
    }
}

FragmentNoMenu:

public abstract class FragmentNoMenu extends Fragment {

    protected MainActivity mainActivity;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        mainActivity = (MainActivity) getActivity();
        if (mainActivity != null) {
            mainActivity.setShowMenu(false);
        }
    }

}

带菜单的片段:

public abstract class FragmentWithMenu extends Fragment {

    protected MainActivity mainActivity;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        mainActivity = (MainActivity) getActivity();
        if (mainActivity != null) {
            mainActivity.setShowMenu(true);
        }
    }

}

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