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


69

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

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

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

对于onCreateContextMenu()来说,它工作得很好。上下文菜单从资源文件中放大,并根据所选项目(即使该片段不是ListFragment,也基于listView ...)进行了少许修改。

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

在我的情况下,这些片段用于显示文件夹结构的内容。当打开子文件夹片段的上下文菜单并选择菜单项时,首先在较高级别上调用onContextItemSelected()(取决于此刻允许/可见的片段数量)。

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

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

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


2
感谢您的配合。今晚我遇到了这个确切的问题,不幸的是找不到其他解决方案。但是,至少要感谢您的信息,我才能继续前进。:)
克里斯·斯图尔特

昨天我遇到了完全相同的问题。当我找到这篇文章时,只是想出了问题的范围。我想不出另一种解决方法,但考虑到我会发布它。
Android Dev Dude

得到它了。 github.com/JakeWharton/ActionBarSherlock/issues/56 将尝试对所选项目使用唯一的ID,并通过该ID进行过滤。
Android Dev Dude

1
实际上,比这更简单。更改返回true;返回super.onContextItemSelected(item); 在我的onContextItemSelected()覆盖中,一切开始正常工作。
Android Dev Dude

尝试下一个答案:stackoverflow.com/a/41595222/1223728
Borzh

Answers:


70

即使您找到了解决方法,我也会发布答案,因为我刚刚处理过类似的问题。当您为特定片段增加上下文菜单时,请为每个菜单项分配该片段唯一的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(...)',也可以使用此技术的修改版本


1
这是处理此问题的正确方法...其他都是解决方法。
Alécio卡瓦略

1
这个答案只是帮助我解决了我的问题!谢谢!
Igal 2014年

2
您可以使用return语句更新代码吗?因为这也很重要
Shreyash Mahajan

与使用visibleHint()方法相比,这是一种更可靠的解决方案。实际上,这是我找到的唯一可靠的解决方案。谢谢!请参阅我对Sergey帖子的评论。
Nerdy Bunz

55

另一个解决方案:

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

基于杰克·沃顿的补丁


2
不要忘记在if语句内返回true,否则可能会遇到麻烦...
user219882 2012年

2
这是最好的解决方案恕我直言。
Tobias

3
我使用了倒置解决方案,它在程序中更容易阅读,并且添加了很多片段:
azelez

似乎可以正常工作,但我可以验证getUserVisibleHint()方法不可靠。我有日志证明有时会调用错误的片段,但并非总是如此。我正在使用一个ViewPager,3个相同类的片段,viewPager.setOffscreenPageLimit(2)和My FragmentPagerAdapter扩展FragmentStatePagerAdapter。
Nerdy Bunz

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

之后,代码与之前相同。


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

而已。


我认为,同步检查很有用,因为虽然我实现了您的建议,但有时我都有两个片段来处理事件。
Kirill Rakhman 2012年

4
而且,如果取消上下文菜单而没有选择任何项目怎么办?bMenu将会保留true
马尔科姆

2

我找到了另一种选择。它不会改变我上面的问题,但是却毫无意义。

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

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

如果您希望使用上下文菜单,那么我没有找到更好的解决方案,因为上面已经提到了解决方法。


3
对于较早版本的操作栏,请使用actionbarsherlock
RaphMclee

1
对于较早版本的ActionBar,请使用Android支持库修订版18
Kuitsi 2013年

1

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

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

然后将调用第二个片段。


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()) {
        ...
    }
}

0

只是改变

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

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

并会很好用!!!


0

恕我直言,我们可能只是检查目标视图是否为片段listview的子级。这很简单,对我来说很好。我刚刚添加了所有片段: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)始终通知该返回false。有效的onContextItemSelected在AFTER之后调用,不是WITHIN。所以super.onContextItemSelected(item)是没有用的,我用代替false


0

我发现了一个比暴露的方法更简单的解决方案:

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

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

    switch(item.getItemId()) {
        ...
    }
}

0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.