运行时更改选项菜单 - invalidateOptionsMenu()

24

我正在创建一个菜单,在其中一个项目中用于锁定对象。当点击此项时,应重新创建菜单,并添加一个按钮以解锁该项目。我为此创建了两个菜单。这很好地工作着。我读到Android版本>=11的情况下,显示菜单时将不再调用onPrepareOptionsMenu方法,而我必须调用invalidateOptionsMenu()方法。所以我更改了构建目标(在清单文件和属性中都更改了)到11,并在4.0.3的AVD上运行了应用程序。程序仍然正常工作,但我认为它不应该,所以我需要检查。

if (Build.VERSION.SDK_INT >= 11)
{
  invalidateOptionsMenu();
}

这是我的代码:

public class MainActivity3 extends Activity{

    boolean locked;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        locked = false;
    }

      @Override
      public boolean onCreateOptionsMenu(Menu menu){
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.changing_menu1, menu);

            return true;
        }

        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {

            menu.clear();
            MenuInflater inflater = getMenuInflater();

            if (locked) {
                inflater.inflate(R.menu.changing_menu2, menu);
            }
            else {
                inflater.inflate(R.menu.changing_menu1, menu);
            }

        return super.onPrepareOptionsMenu(menu);
        }

        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {

          case R.id.Menu1:
          break;

          case R.id.Menu2 :
          break;

          case R.id.Menu3 :
          locked = !locked;
          break;

           }
        return true;
        }
}

所以,在4.0中,菜单仍然会被刷新/重新创建。 我是否对invalidateOptionsMenu()的使用有误解?


我不知道该如何报告,但即使是安卓开发者对此也不够清楚。请在此处查看:https://developer.android.com/guide/topics/ui/menus?hl=en#ChangingTheMenu 如果你只读这个,你会明白invalidateOptionsMenu()不会调用onCreateOptionsMenu(),而在Android >= 11中OnPrepareOptionsMenu()并不总是在每次打开菜单时都有效...但它确实有效。令人困惑?erdomester提出了一个很好的问题,@justinmorris给出了很好的答案。 - Juan Ignacio Avendaño Huergo
1个回答

36

invalidateOptionsMenu()被添加是为了让我们有能力强制调用onCreateOptionsMenu()。每次调用菜单时,onPrepareOptionsMenu()仍然会被调用。

你上面尝试实现的内容是使用invalidateOptionsMenu()的一个很好的例子,但由于向后兼容性,你需要同时做两件事:

if (Build.VERSION.SDK_INT >= 11) {
  invalidateOptionsMenu();
}


@Override
public boolean onCreateOptionsMenu(Menu menu){
    if (Build.VERSION.SDK_INT >= 11) {
        selectMenu(menu);
    }
    return true;
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    if (Build.VERSION.SDK_INT < 11) {
        selectMenu(menu);
    }
    return true;
}

private void selectMenu(Menu menu) {
    menu.clear();
    MenuInflater inflater = getMenuInflater();

    if (locked) {
        inflater.inflate(R.menu.changing_menu2, menu);
    }
    else {
        inflater.inflate(R.menu.changing_menu1, menu);
    }
}

嗨!这看起来不错!但我不明白invalidateOptionsMenu()在这里的作用。首先,会调用onCreateOptionsMenu。如果API >= 11,则调用selectMenu(),但如果API < 11会发生什么?我还没有运行代码(目前无法运行),但我不知道为什么选项菜单会在API < 11时打开。 另一件事:如果在项目点击时调用invalidateOptionsMenu()(当API >= 11时),系统会自动调用onPrepareOptionsMenu,但如果API < 11,则只会填充菜单,所以它什么也不做,对吗? - erdomester
在您的用例中使用invalidateOptionsMenu()只是微小的性能改进。最终,在新设备上仅在“锁定”更改时调用invalidateOptionsMenu()更有效率。然后,11+设备仅在有原因时才会填充新的选项菜单,但旧设备将在用户每次按下菜单按钮时重新填充选项菜单。通过使用布尔开关并在onPrepareOptionsMenu()中检查它以查看是否需要重新填充菜单,您可以获得相同的性能增强,但向后兼容。 - JustinMorris
所以基本上我甚至不需要调用invalidateOptionsMenu()。它只是一个为API > 11的设备提供保护的方法,对吧?而且在您的代码中,selectMenu仅在API < 11时被调用,因此如果您调用invalidateOptionsMenu(),菜单将不会刷新,即锁将不会更改为解锁。 - erdomester
没错,你不需要调用invalidateOptionsMenu()。我只是向你展示了你可以这样做。在我的代码中,如果API < 11,我只从onPrepareOptionsMenu()调用selectMenu,因为如果API更高,我会调用invalidateOptionsMenu(),这将导致从onCreateOptionsMenu()调用selectMenu。 - JustinMorris
1
另一个选项是使用一个菜单XML并拥有组。可以在onPrepareOptionsMenu()中更改组的可见性。 - Patrick Jackson

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