Android - 正确使用invalidateOptionsMenu()

问题描述 投票:28回答:8

我一直在搜索invalidateOptionsMenu(),我知道它的作用。但我想不出这个方法可能有用的任何现实生活中的例子。

我的意思是,例如,假设我们想要为我们的MenuItem添加一个新的ActionBar,我们可以简单地从onCreateOptionsMenu(Menu menu)获取菜单并在任何按钮的动作中使用它。

现在我真正的问题是,遵循使用invalidateOptionsMenu()的唯一方法?

bool _OtherMenu;
protected override void OnCreate (Bundle bundle)
{
    _OtherMenu = false;
    base.OnCreate (bundle);
    SetContentView (Resource.Layout.Main);
    Button button = FindViewById<Button> (Resource.Id.myButton);
    button.Click += delegate
    {
        if(_OtherMenu)
            _OtherMenu = false;
        else
            _OtherMenu = true;

        InvalidateOptionsMenu ();
    };
}

public override bool OnCreateOptionsMenu (IMenu menu)
{
    var inflater = this.SupportMenuInflater;
    if(_OtherMenu)
        inflater.Inflate (Resource.Menu.another_menu, menu);
    else
        inflater.Inflate (Resource.Menu.main_activity_menu, menu);

    return base.OnCreateOptionsMenu (menu);
}

单击按钮,将出现另一个菜单。再次单击该按钮,将出现上一个菜单。

附:对不起C#语法。

android android-actionbar xamarin android-menu android-optionsmenu
8个回答
62
投票

invalidateOptionsMenu()习惯说Android,菜单内容已经改变,菜单应该重新绘制。例如,单击在运行时添加另一个菜单项的按钮,或隐藏菜单项组。在这种情况下,您应该调用invalidateOptionsMenu(),以便系统可以在UI上重绘它。此方法是操作系统调用onPrepareOptionsMenu()的信号,您可以在其中实现必要的菜单操作。此外,在活动(片段)创建期间仅调用OnCreateOptionsMenu()一次,因此此方法无法处理运行时菜单更改。

所有都可以在documentation找到:

在系统调用onCreateOptionsMenu()之后,它会保留您填充的菜单的实例,并且不会再次调用onCreateOptionsMenu(),除非由于某种原因菜单失效。但是,您应该仅使用onCreateOptionsMenu()来创建初始菜单状态,而不是在活动生命周期中进行更改。

如果要根据活动生命周期中发生的事件修改选项菜单,可以在onPrepareOptionsMenu()方法中执行此操作。此方法会将Menu对象传递给当前存在,以便您可以对其进行修改,例如添加,删除或禁用项目。 (片段还提供onPrepareOptionsMenu()回调。)

在Android 2.3.x及更低版本中,每次用户打开选项菜单(按菜单按钮)时,系统都会调用onPrepareOptionsMenu()。

在Android 3.0及更高版本中,当操作栏中显示菜单项时,选项菜单被视为始终打开。当事件发生并且您想要执行菜单更新时,必须调用invalidateOptionsMenu()以请求系统调用onPrepareOptionsMenu()。


18
投票

在应用程序生命周期中使用此命令重新加载新菜单:

ActivityCompat.invalidateOptionsMenu(getActivity());

2
投票

你需要覆盖方法onPrepareOptionsMenu(),用相同的方法编写动作菜单的更新代码,如果你使用的是片段,那么在setHasOptionsMenu(true);中添加onCreateView()

希望它能帮到你


1
投票

对我有用的最佳方式如下所示

  1. 将菜单的初始状态放在onCreateOptionsMenu(...)中
  2. 使用invalidateOptionsMenu()强制onCreateOptionsMenu(...)和onPrepareOptionsMenu(...)
  3. 在onPrepareOptionsMenu(...)中,调用menu.clear()从菜单中删除所有项目。
  4. 仍然在onPrepareOptionsMenu(...)中将动态菜单更改为清除后

希望这可以帮助...


1
投票

我发现的一个用途是迫使onResumeonCreateOptionsMenu/onPrepareOptionsMenu之间的操作顺序。自然顺序(至少从平台22开始)似乎会翻转,特别是在重新定位设备时。

invalidateOptionsMenu()中调用onResume(),你将保证在onResume之后调用onPrepareOptionsMenu(之前可能另外调用它)。例如,这将允许基于onResume中检索的数据启用菜单项。


0
投票

编辑:这是一个更好的答案问题。

invalidateOptionsMenu()的一个很好的用途是当我们有ListView和删除所有MenuItem所以当ListView是空的时候我们应该使用invalidateOptionsMenu()去除删除所有MenuItem

以下是与此答案相关的问题:Question


0
投票
/**
 * Set a hint for whether this fragment's menu should be visible.  This
 * is useful if you know that a fragment has been placed in your view
 * hierarchy so that the user can not currently seen it, so any menu items
 * it has should also not be shown.
 *
 * @param menuVisible The default is true, meaning the fragment's menu will
 * be shown as usual.  If false, the user will not see the menu.
 */
public void setMenuVisibility(boolean menuVisible) {
    if (mMenuVisible != menuVisible) {
        mMenuVisible = menuVisible;
        if (mHasMenu && isAdded() && !isHidden()) {
            mHost.onSupportInvalidateOptionsMenu();
        }
    }
}

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_edit"
    android:icon="@drawable/ic_edit"
    android:title="Edit Task"
    app:showAsAction="always" />

<item
    android:id="@+id/action_delete"
    android:icon="@drawable/ic_delete"
    android:title="Delete Task"
    app:showAsAction="always" />

<item
    android:id="@+id/action_check"
    android:icon="@drawable/ic_check"
    android:title="Check Task"
    app:showAsAction="always" />

<item
    android:id="@+id/action_uncheck"
    android:icon="@drawable/ic_undo"
    android:title="Uncheck Task"
    app:showAsAction="always" />
</menu>

示例片段中的代码:

private boolean isMenuItemChecked;

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    setMenuVisibility(false);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.my_menu, menu);
    super.onCreateOptionsMenu(menu, inflater);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    try {
        menu.findItem(R.id.action_check).setVisible(!isMenuItemChecked);
        menu.findItem(R.id.action_uncheck).setVisible(isMenuItemChecked);
    }
    catch(Exception e) {
        Log.e(TAG, "onPrepareOptionsMenu error");
    }
}

public void loadUi(boolean isMenuItemChecked) {
    this.isMenuItemChecked = isMenuItemChecked;
    setMenuVisibility(true);
}

0
投票

它已经过时了,但希望这有助于将来有所帮助。

我在现实生活场景中发现的一个用途:

假设您有一个存储在数据库中的项目列表,并且您有2个活动:

  1. DisplayActivity:从数据库获取这些对象后显示这些对象。
  2. EditActivity:用于编辑现有项目并将其保存到数据库中。

您决定从DisplayActivity到EditActivity有几个选项:

  • 第一步:将全新的项目添加到数据库中。
  • 第二:编辑现有项目。

为了不通过复制代码重复自己,您决定将EditActivity用于这两个目的。

因此,您希望根据每个目的自定义“选项菜单”。在这种情况下,您将使用onCreateOptionsMenu()构建默认选项菜单,并在编辑现有项目时保持原样;和invalidateOptionsMenu()它是什么时候创建新的项目;在这种情况下,onPrepareOptionsMenu()会自动触发以自定义您的菜单。

例如,“选项”菜单可以有一个用于编辑现有项目的删除选项,这在添加新项目时应该隐藏。

© www.soinside.com 2019 - 2024. All rights reserved.