70

我目前正在尝试调整我的应用程序以使用“Android v4 兼容性库”,以便为 Android 1.6 用户提供使用片段的好处。

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

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

对于onCreateContextMenu()这很有效。上下文菜单是从资源文件扩展而来的,并根据所选项目(基于 listView...即使片段不是 ListFragment)稍作修改。

选择上下文菜单项时会出现此问题。 从第一个添加的片段开始,对所有当前存在的片段调用onContextItemSelected() 。

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

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

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

处理这个问题的最优雅的方法是什么?

4

11 回答 11

71

即使您找到了解决方法,我也会发布答案,因为我刚刚处理了类似的问题。当您为特定片段扩展上下文菜单时,为每个菜单项分配一个对片段唯一的 groupId。然后测试“onContextItemSelected”中的 groupId。例如:

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(...)”,这种技术的修改版本也可以工作

于 2011-11-18T20:58:20.203 回答
55

另一种解决方案:

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

基于来自 Jake Wharton的这个补丁。

于 2012-04-12T14:32:31.720 回答
8

我喜欢 Sergei G 的简单解决方案(基于 Jake Wharton 修复),但倒置是因为它更容易添加到多个片段中:

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();
 }

之后,代码与以前相同。

于 2013-05-29T10:06:54.370 回答
5

我找到了一个非常简单的解决方案。由于每次创建 ContextMenu 时都会调用 onCreateContextMenu(),因此我将布尔变量设置为 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;
}

我唯一要做的另一件事是要求该变量 OnContextItemSelected()

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);
    }
}

就是这样。

于 2011-12-28T14:56:45.173 回答
3

我找到了一个替代方案。它不会改变我上面的问题的任何东西,但它使它毫无意义。

我已经从我的应用程序中完全删除了上下文菜单。相反,我捕获了对列表项的长按并在此时更改操作栏的可见按钮。从用户的角度来看,这更像是一个上下文菜单。

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

如果您想继续使用上下文菜单,我没有找到比上面提到的解决方法更好的解决方案。

于 2011-05-30T11:15:03.553 回答
1

在我的第一个片段中,我已将所有菜单 ID 设置为 > 5000,因此,作为我拥有的第一个片段的 onContextItemSelected 的第一行代码

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

第二个片段将被调用。

于 2011-11-12T16:08:25.493 回答
1

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

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()) {
        ...
    }
}
于 2014-07-22T14:53:33.473 回答
0

只是改变

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

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

并且会很好用!!!

于 2012-06-08T11:38:04.590 回答
0

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

这是来自我的一个父片段的示例。这是 Scala,但我希望你有一个想法。

@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)return always false。之后调用的有效 onContextItemSelected ,而不是 WITHIN。所以super.onContextItemSelected(item)没用,我用false.

于 2012-08-01T11:03:53.367 回答
0

我找到了一个比暴露更简单的解决方案:

public boolean onContextItemSelected(MenuItem item) {
    ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList);

    if (!yourList.hasFocus())
        return false;

    switch(item.getItemId()) {
        ...
    }
}
于 2015-05-29T17:29:08.860 回答
0

在方法改变返回true;返回 super.onContextItemSelected(item); 在我的 onContextItemSelected() 覆盖中,一切都开始工作了。

于 2015-05-30T15:27:44.193 回答