【问题标题】:How to handle onContextItemSelected in a multi fragment activity?如何在多片段活动中处理 onContextItemSelected?
【发布时间】:2011-07-14 22:43:22
【问题描述】:

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

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

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

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

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

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

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

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

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

【问题讨论】:

  • 感谢您的工作。我今晚遇到了这个确切的问题,不幸的是找不到任何其他解决方案。但是,由于您的信息,我至少向前迈进了。 :)
  • 我昨天遇到了完全相同的问题。当我发现这篇文章时,我刚刚弄清楚了问题的范围。我想不出另一种临时解决方法,但如果我想到它,我会发布一个。
  • 知道了。 github.com/JakeWharton/ActionBarSherlock/issues/56 将尝试对所选项目使用唯一 ID 并通过它进行过滤。
  • 其实比这更简单。改变返回真;返回 super.onContextItemSelected(item);在我的 onContextItemSelected() 覆盖中,一切都开始工作了。
  • 尝试下一个答案:stackoverflow.com/a/41595222/1223728

标签: android contextmenu android-fragments android-support-library


【解决方案1】:

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

【讨论】:

  • 这是处理问题的正确方法...其他都是解决方法。
  • 这个答案只是帮助我解决了我的问题!谢谢!
  • 你能用return语句更新代码吗?因为它也很重要。
  • 这是一个比使用 visibleHint() 方法更可靠的解决方案。事实上,这是我找到的唯一可靠的解决方案。谢谢!请参阅我对 Sergey 帖子的评论。
【解决方案2】:

另一种解决方案:

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

基于来自 Jake Wharton 的 this patch

【讨论】:

  • 不要忘记在 if 语句中 return true 否则你会遇到麻烦...
  • 这是最好的解决方案恕我直言。
  • 我用的是倒排的解决方案,只是在程序中更容易阅读,并且添加到很多片段中:
  • 它可能似乎工作,但我可以验证 getUserVisibleHint() 方法不可靠。我有日志证明有时会调用错误的片段,但并非总是如此。我正在使用一个 ViewPager,同一个类的 3 个片段,viewPager.setOffscreenPageLimit(2),我的 FragmentPagerAdapter 扩展了 FragmentStatePagerAdapter。
【解决方案3】:

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

之后,代码与之前相同。

【讨论】:

    【解决方案4】:

    我找到了一个非常简单的解决方案。由于每次创建 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);
        }
    }
    

    就是这样。

    【讨论】:

    • 我认为,同步检查很有用,因为我有时让两个片段都处理事件,尽管我实施了你的建议。
    • 如果上下文菜单被取消而不选择任何项目怎么办?然后bMenu 将保持true
    【解决方案5】:

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

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

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

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

    【讨论】:

    【解决方案6】:

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

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

    第二个片段将被调用。

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

        只是改变

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

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

        而且会很好用!!!

        【讨论】:

          【解决方案9】:

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

          这是来自我的父片段之一的示例。这是 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
          

          附:如果您跟踪 onContextItemSelected(...) 的调用,您可能会通知 super.onContextItemSelected(item) 始终返回 false。有效的 onContextItemSelected 在之后调用,而不是在其内。所以super.onContextItemSelected(item)没用,我用false代替了。

          【讨论】:

            【解决方案10】:

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

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

            【讨论】:

              【解决方案11】:

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

              【讨论】:

                猜你喜欢
                • 2012-01-30
                • 2015-04-12
                • 2016-11-25
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-12-23
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多