如何以编程方式创建 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 的标准 Menu
和 MenuItem
类一起使用 即使您的活动扩展了 SherlockActivity,因为它会无论您使用android.view.MenuItem
或com.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 之外膨胀一个菜单的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Java 中以编程方式启动和停止 Amazon EC2 实例
如何以编程方式在 XPathExpression 实例中使用 XPath 函数?