如何处理多片段活动中的onContextItemSelected?

我目前正试图使我的应用程序使用“适用于Android v4的兼容性库”,甚至为Android 1.6用户提供片段使用的好处。

上下文菜单的实现似乎很棘手:

  • 应用程序的主要活动是扩展FragmentActivity类。
  • 这些片段都是基于扩展片段类的一个类。
  • 片段类在其onCreateView()方法中调用registerForContextMenu() ,并覆盖onCreateContextMenu()onContextItemSelected()方法

对于onCreateContextMenu(),这工作得很好。 上下文菜单从资源文件中膨胀,并根据所选项目(基于listView …即使片段不是ListFragment)稍作修改。

select上下文菜单项时会发生此问题。 为所有当前存在的片段调用onContextItemSelected() ,从第一个添加的片段开始。

在我的情况下,片段是用来显示文件夹结构的内容。 当打开子文件夹片段的上下文菜单并select菜单项时, onContextItemSelected()首先在上层调用(取决于此时允许/可见片段的数量)。

现在,我通过活动级别的字段使用一个解决方法,该字段包含调用其onCreateContextMenu()的最后一个片段的标记。 这样,当存储的标签与getTag()不相同时,我可以在onContextItemSelected()的开头调用“return super.onContextItemSelected(item)”。 但是这种方法对我来说看起来有点肮脏。

为什么onContextItemSelected()在所有片段上调用? 而不只是一个调用onCreateContextMenu()

什么是最优雅的方式来处理这个?

即使您find解决办法,我也会发布答案,因为我刚刚处理了类似的问题。 当您膨胀特定片段的上下文菜单时,为每个菜单项分配一个片段唯一的groupId。 然后在'onContextItemSelected'中testinggroupId。 例如:

public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) { menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_1, 0, R.string.src1); menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_2, 0, R.string.src2); } public boolean onContextItemSelected(MenuItem item) { //only this fragment's context menus have group ID of -1 if (item.getGroupId() == UNIQUE_FRAGMENT_GROUP_ID) { switch(item.getItemId()) { case MENU_OPTION_1: doSomething(); break; case MENU_OPTION_2: doSomethingElse(); break; } } 

这样,所有的片段仍然会接收到'onContextItemSelected'的调用,但只有正确的才会响应,从而避免了编写活动级代码的需要。 我假设这种技术的修改版本可以工作,即使你没有使用“menu.add(…)”

另一个解决scheme:

 @Override public boolean onContextItemSelected(MenuItem item) { if (getUserVisibleHint()) { // context menu logic return true; } return false; } 

基于杰克·沃顿的这个补丁 。

我喜欢Sergei G(基于Jake Wharton fix)的简单解决scheme,但倒过来是因为它更容易添加到几个片段中:

 public boolean onContextItemSelected(android.view.MenuItem item) { if( getUserVisibleHint() == false ) { return false; } // The rest of your onConextItemSelect code AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); } 

之后,代码和之前一样。

我发现了一个非常简单的解决scheme。 由于onCreateContextMenu()每次创buildContextMenu时都被调用,所以我设置一个布尔variables为true。

 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.film_menu, menu); bMenu=true; } 

我唯一要做的另一件事就是要求这个variablesOnContextItemSelected()

 public boolean onContextItemSelected(MenuItem item) { if (bMenu) { bMenu=false; if (item.getItemId() == R.id.filmProperties) { ///Your code return true; } else { return super.onContextItemSelected(item); } } else { return super.onContextItemSelected(item); } } 

而已。

我find了一个select。 它并没有改变我上面的问题,但它使它毫无意义。

我已经从我的应用程序完全删除上下文菜单。 相反,我捕获列表项上的longclick,并在此刻更改操作栏的可见button。 从用户的angular度来看,这更像是一个上下文菜单的平板电脑。

在向后兼容的应用程序中,操作栏不存在。 所以我决定为Honeycomb之前的设备构build我自己的(在顶部的一种工具栏)。

如果你想留在上下文菜单,我没有find一个更好的解决scheme作为我上面提到的解决方法。

在我的第一个片段中,我已经设置了所有我的菜单id> 5000所以,作为onContextItemSelected的第一个片段的代码的第一行我有

 if (item.getItemId() < 5000) return false; 

第二个片段将被调用。

如果你在片段中使用带有列表视图的适配器,这可能会有所帮助。

 public boolean onContextItemSelected(final MenuItem item) { final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); //Check if the context menu call came from the list in this fragment (needed for support for multiple fragments in one screen) if (info.targetView.getParent() != getView().findViewById(android.R.id.list)) return super.onContextItemSelected(item); //Handle context menu item call switch (item.getItemId()) { ... } } 

只是改变

  @Override public boolean onContextItemSelected(MenuItem item) { return true; } 

 @Override public boolean onContextItemSelected(MenuItem item) { return super.onContextItemSelected(item); } 

并会很好的工作!

恕我直言,我们可能只是检查目标视图是片段listview的孩子。 这很简单,为我工作好。 我刚刚添加到我所有的片段: if (getListView.getPositionForView(info.targetView) == -1) return false从旧API迁移时if (getListView.getPositionForView(info.targetView) == -1) return false

这是我的父母片段之一的例子。 这是斯卡拉,但我希望你有一个想法。

 @Loggable override def onContextItemSelected(menuItem: MenuItem): Boolean = { for { filterBlock <- TabContent.filterBlock optionBlock <- TabContent.optionBlock environmentBlock <- TabContent.environmentBlock componentBlock <- TabContent.componentBlock } yield menuItem.getMenuInfo match { case info: AdapterContextMenuInfo => if (getListView.getPositionForView(info.targetView) == -1) return false TabContent.adapter.getItem(info.position) match { case item: FilterBlock.Item => filterBlock.onContextItemSelected(menuItem, item) case item: OptionBlock.Item => optionBlock.onContextItemSelected(menuItem, item) case item: EnvironmentBlock.Item => environmentBlock.onContextItemSelected(menuItem, item) case item: ComponentBlock.Item => componentBlock.onContextItemSelected(menuItem, item) case item => log.debug("skip unknown context menu item " + info.targetView) false } case info => log.fatal("unsupported menu info " + info) false } } getOrElse false 

PS如果跟踪onContextItemSelected(…)的调用,则可能会通知super.onContextItemSelected(item)返回始终为false 。 有效的onContextItemSelected调用AFTER,而不是WITHIN 。 所以super.onContextItemSelected(item)是无用的,我用false代替它。

我发现比暴露的更简单的解决scheme:

 public boolean onContextItemSelected(MenuItem item) { ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList); if (!yourList.hasFocus()) return false; switch(item.getItemId()) { ... } } 

在方法改变的情况下返回true; 返回super.onContextItemSelected(item); 在我onContextItemSelected()重写和一切开始工作。