如何以编程方式创建 Menu 实例?即在 onCreateOptionsMenu 之外膨胀一个菜单

Posted

技术标签:

【中文标题】如何以编程方式创建 Menu 实例?即在 onCreateOptionsMenu 之外膨胀一个菜单【英文标题】:How to create a Menu instance programmatically? i.e. inflate a Menu outside onCreateOptionsMenu 【发布时间】:2012-12-16 15:06:30 【问题描述】:

我想给一个菜单对象充气 outside onCreateOptionsMenu 方法(意思是在用户不按下按钮时创建/显示菜单),所以我需要创建一个菜单实例将其传递给 inflate 方法。

这是我想要实现的一个示例:

Menu menu = // How to create an instance !? 
new MenuInflater(context).inflate(R.menu.my_menu, menu)

Menu 是一个接口,所以我需要知道哪个类在实现它。我确实浏览了 android 代码以获取有关如何创建 Menu 对象的任何提示,但仍然找不到我要查找的内容。

编辑 1

我的目标是从自定义视图中触发 onOptionsItemSelected(MenuItem item) 事件,该事件将由 Activity 处理,因此我需要一个具有特定 itemId 和标题的 MenuItem 对象以将其与事件一起传递。

如果我能成功地创建一个 Menu 对象,就很容易得到它的子 MenuItems。

编辑 2

根本没有尝试显示菜单,我想要的是使用在菜单 XML 中定义的元素填充 ListView具有标题、图标和 itemId 并且每当单击 ListViewItem 时,我想触发在我的活动中处理的 onOptionsItemSelected(MenuItem item) 事件

我知道我可以解析菜单 XML 以提取项目信息,但是如果不创建 标准 MenuItem 对象将其作为参数传递,我将无法触发 onOptionsItemSelected(MenuItem item)

任何帮助将不胜感激。谢谢!

【问题讨论】:

看这里 - developer.android.com/reference/android/view/Menu.html - 我没有看到任何构造函数,所以你最好按照 Waqas 所说的去做 Menu 是一个接口,所以我试图找出哪个类正在实现它。文档显示Menu只有两个间接子类ContextMenu和SubMenu,不知道能不能用。 【参考方案1】:

我不确定为什么这不是一个答案,而且我知道这是一个老问题,但对于未来的读者..

如果你只是这样做:

val menu = MenuBuilder(context)
MenuInflater(context).inflate(R.menu.menu_XXXX, menu)

有效!

androidx.appcompat.view.menu.MenuBuilder 实现android.view.Menu。经检查,PopupMenu 仅此而已。

注意@iTech 提到的PopupMenu 使用的com.android.internal.view.menu.MenuBuilder 不是公开的,不应使用。

这里有两个辅助函数和一个用法示例:

fun Context.inflateMenu(@MenuRes menuRes: Int): Lazy<MenuBuilder> = lazy 
    MenuBuilder(this)
        .also  MenuInflater(this).inflate(menuRes, it) 


fun Fragment.inflateMenu(@MenuRes menuRes: Int): Lazy<MenuBuilder> = lazy 
    MenuBuilder(context)
        .also  MenuInflater(context).inflate(menuRes, it) 

用法:

活动

class MyActivity : AppCompatActivity(R.layout.activity_my) 

    val menu by inflateMenu(R.menu.menu_my)

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)

        ...
    

片段:

class MyFragment : Fragment(R.layout.fragment_my) 

    val menu by inflateMenu(R.menu.menu_my)

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)

        ...
    

【讨论】:

【参考方案2】:

这里有一个获取 Menu 实例的技巧:

PopupMenu p  = new PopupMenu(getContext(), null);
Menu menu = p.getMenu();

【讨论】:

这似乎比在接受的答案中使用的反射更可取...... 这是一个比使用反射更好的实现。这个答案应该被接受 @Musma 谢谢。我在 Xamarin.Android 中用作 IMenu imenu=p.Menu;【参考方案3】:

我找到了两种以编程方式创建 Menu 实例并对其进行扩展的解决方案:

使用ActionbarSherlock 库或AppCompat v7 library Menu menu = new MenuBuilder(context); 或者您可以编写自己的MenuBuilder

使用标准安卓 SDK:

// 通过反射创建实例

Menu menu = newMenuInstance(context);


protected Menu newMenuInstance(Context context) 
    try 
        Class<?> menuBuilderClass = Class.forName("com.android.internal.view.menu.MenuBuilder");

        Constructor<?> constructor = menuBuilderClass.getDeclaredConstructor(Context.class);

        return (Menu) constructor.newInstance(context);

     catch (Exception e) e.printStackTrace();

    return null;

一旦你有了一个 Menu 实例,你就可以很容易地从你程序中任何地方的菜单 XML 资源扩充它

new MenuInflater(context).inflate(menuId, menu);

我测试了这两种方法,它们都运行良好,我建议将第二种方法与来自 android SDK 的标准 MenuMenuItem 类一起使用 即使您的活动扩展了 SherlockActivity,因为它会无论您使用android.view.MenuItemcom.actionbarsherlock.view.MenuItem 触发它,仍然会收到onOptionsItemSelected(MenuItem item)

【讨论】:

我尝试使用第二个版本,因为我不使用 Sherlock,并且我从 newMenuInstance 返回空对象。有什么想法吗? 我使用 AppCompat v7 库,所以第一个解决方案非常适合我。只需添加 1 行代码。 @iTech,由于构造函数没有公开,如何确定使用这种方法不会给我们带来任何问题? 好吧,反射无法保证,但任何隐藏的 API 都是如此。但是,我认为这种情况不太可能发生变化。【参考方案4】:

您可以拉取存储在 Activity 中使用的 PhoneWindow 中的 MenuBuilder 实例(实现 Menu 接口)。我有一个可行的解决方案,它使用 Java 反射。

我创建了一个项目“InDroid”来公开几个重要的隐藏方法,来自Android平台的变量-http://code.google.com/p/indroid/

BR, 普拉桑塔

【讨论】:

使用反射来实现这个功能有多安全?【参考方案5】:

您无需对充气机进行任何操作即可添加您自己的菜单。只需覆盖onCreateOptionsMenu 并开始在menu 对象中添加您自己的items。例如:

@Override
public boolean onCreateOptionsMenu(Menu menu) 
    super.onCreateOptionsMenu(menu);

    menu.add(0, 1, Menu.NONE, "First");
    menu.add(0, 2, Menu.NONE, "Second");
    // repeat this to add additional menus

    return true;

add 方法中的第二个参数是id。使用唯一的 ids 来识别您选择的菜单项。

【讨论】:

我为我的问题添加了更多解释。我不是试图显示菜单的问题,我只想在自定义视图类中创建一个实例。 你不能那样做。因为onCreateOptionsMenu 会在活动创建时触发一次,或者除非您调用invalidateOptionsMenu()。所以我建议您调用 invalidate 方法,然后根据您的要求更改onCreateOptionsMenu 中的菜单。 我不想显示任何菜单。我想在自定义视图 outside onCreateOptionsMenu 中膨胀菜单对象,以便访问其子菜单项信息(例如标题、itemId 和图标)。从您的评论看来这是不可行的。 “在自定义视图中膨胀菜单对象”是什么意思?我认为View类与Menu无关,除了ContextMenu 你说得对,这就是为什么我提到我试图显示任何菜单。我对我的问题添加了进一步的解释。赞赏!

以上是关于如何以编程方式创建 Menu 实例?即在 onCreateOptionsMenu 之外膨胀一个菜单的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式创建新的本地 SQL Server 实例?

以编程方式识别何时创建或终止微服务的新实例?

如何在 Java 中以编程方式启动和停止 Amazon EC2 实例

如何以编程方式在 XPathExpression 实例中使用 XPath 函数?

SharePoint:如何以编程方式将项目添加到自定义列表实例

如何获取在 Xamarin MacOS 中以编程方式创建的 NSTextField 的值?