【问题标题】:Android - Correct use of invalidateOptionsMenu()Android - 正确使用 invalidateOptionsMenu()
【发布时间】:2015-03-15 01:30:17
【问题描述】:

我在invalidateOptionsMenu() 上搜索了很多,我知道它的作用。但我想不出任何现实生活中这种方法有用的例子。

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

现在我真正的问题是,是否遵循 only 使用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# 语法。

【问题讨论】:

  • 你的 minSdkVersion 是多少?
  • 它适用于 11+。我的是 15 岁。
  • 所以它应该调用OnCreateOptionsMenu(),你可以尝试运行调试来通过代码
  • @Xingchen 先生,我知道这个功能是做什么的。它正在做应该做的事情。我只是不明白这个功能的意义。它有什么用处?
  • 此功能旨在更新您的操作栏菜单。就我而言,每次我的菜单项文本值发生更改时,我都会调用 invalidateOptionsMenu(),然后调用 onPrepareOptionsMenu() 来更改项值

标签: android android-actionbar xamarin android-menu android-optionsmenu


【解决方案1】:

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

都可以在documentation找到:

系统调用onCreateOptionsMenu()后,保留一个实例 您填充的菜单,不会再次调用 onCreateOptionsMenu() 除非菜单因某种原因无效。然而,你应该 仅使用 onCreateOptionsMenu() 来创建初始菜单状态和 不要在活动生命周期中进行更改。

如果您想根据发生的事件修改选项菜单 在活动生命周期中,您可以在 onPrepareOptionsMenu() 方法。此方法向您传递 Menu 对象 因为它当前存在,所以您可以修改它,例如添加、删除或 禁用项目。 (片段还提供了一个 onPrepareOptionsMenu() 回调。)

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

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

【讨论】:

  • 但我不必调用 invalidateOptionsMenu()。我只是做 menu.add() 并显示了
  • 动态菜单需要它。你怎么能从外面onCreateOptionsMenu() 打电话给menu.add()?例如,当用户向下滚动列表视图时,如何更改菜单项?通过使滚动菜单无效,强制活动重新创建它,并检查onCreateOptionsMenu() 中的某个条件。如果您检查onCreateOptionsMenu() 中的条件而不使无效,这将不起作用,因为活动会认为菜单仍然可以。
  • 我不知道如何在这里编写代码,但让我们看看: OnCreateOptionsMenu(IMenu menu){ _Menu = menu;并且你有对 Menu 的引用。现在做任何你想做的事
【解决方案2】:

在应用生命周期中使用它来重新加载新菜单:

新:

getActivity().invalidateOptionsMenu();

ActivityCompat.invalidateOptionsMenu(getActivity());

【讨论】:

  • 我们应该在 Fragment 的哪个生命周期中调用这个?
  • ActivityCompat .invalidateOptionsMenu() 现已弃用
  • 已弃用,现在正确的调用只是 invalidateOptionsMenu()(来自 Activity)
  • 已弃用,来自片段调用 getActivity().invalidateOptionsMenu();
【解决方案3】:

您需要覆盖方法onPrepareOptionsMenu(),以相同的方法编写操作菜单的更新代码,如果您正在使用片段,则在onCreateView()中添加setHasOptionsMenu(true);

希望对你有帮助

【讨论】:

  • 好的,但是 onPrepareOptionsMenu() 只是一个固定的方法。固定我的意思是,它不是一次 onPrepareOptionsMenu() 做某事而另一次做其他事情的情况。
  • 为什么不呢?你知道,有条件(if-else)之类的东西。在你想象的任何情况下,你都可以让它做任何你想做的事
  • @AlexanderZhak 那么,答案是什么?这是唯一的方法还是可能有其他情况?
  • 我还没有收到任何具体的答复:(
【解决方案4】:

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

onResume() 中调用invalidateOptionsMenu(),您将保证onPrepareOptionsMenu 将在onResume 之后被调用(它也可能在之前被调用)。例如,这将允许根据在onResume 中检索到的数据启用菜单项。

【讨论】:

    【解决方案5】:
    /**
     * 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);
    }
    

    【讨论】:

    • 你的代码展示了我对自己问题的一个很好的解决方案。感谢 sn-p
    【解决方案6】:

    下面展示了对我有用的最佳方法

    1. 将菜单的初始状态放在onCreateOptionsMenu(...)中

    2. 使用 invalidateOptionsMenu() 强制 onCreateOptionsMenu(...) 和 onPrepareOptionsMenu(...)

    3. 在 onPrepareOptionsMenu(...) 中,调用 menu.clear() 从菜单中删除所有项目。

    4. 还是在 onPrepareOptionsMenu(...) 把你的动态菜单改变后清除

    希望这会有所帮助...

    【讨论】:

      【解决方案7】:

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

      invalidateOptionsMenu() 的一个很好的用途是当我们有一个 ListView 和 Delete All MenuItem 所以当 ListView 为空时,我们应该使用 invalidateOptionsMenu() 来删除 Delete All MenuItem

      这是一个与此答案相关的问题:Question

      【讨论】:

      • 如果您有新问题,请点击 按钮提出问题。如果有助于提供上下文,请包含指向此问题的链接。 - From Review
      • 实际上这个链接为答案提供了上下文@sushildlh
      【解决方案8】:

      它很旧,但希望这对将来的某些人有所帮助。

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

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

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

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

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

      为了避免重复代码,您决定将 EditActivity 用于这两个目的。

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

      例如,选项菜单可以有一个删除选项来编辑现有项目,并且在添加新项目时应该隐藏它。

      【讨论】:

        【解决方案9】:

        来自片段调用getActivity().invalidateOptionsMenu();

        【讨论】:

          猜你喜欢
          • 2012-12-10
          • 1970-01-01
          • 2019-11-08
          • 1970-01-01
          • 1970-01-01
          • 2013-04-13
          • 1970-01-01
          • 1970-01-01
          • 2013-01-31
          相关资源
          最近更新 更多