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

发布于 2024-10-22 12:58:09 字数 1072 浏览 2 评论 0原文

我目前正在尝试调整我的应用程序以使用“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() 的那个?

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

I'm currently trying to adapt my application to use the "Compatibility Libraries for Android v4" to provide the benefits of the usage of fragments even to Android 1.6 users.

The implementation of a context menu seems to be tricky:

  • The main activity of the application
    is extending the FragmentActivity
    class.
  • The fragments are all based on one
    class which extends the Fragment class.
  • The fragment class is calling
    registerForContextMenu() in its onCreateView() method and overrides the methods
    onCreateContextMenu() and onContextItemSelected().

For onCreateContextMenu() this works pretty well. The context menu is inflated from a resource file and slightly modified based on the selected item (which is based on a listView... even if the fragment is not an ListFragment).

The problem occurs when a context menu entry is selected.
onContextItemSelected() is called for all currently existing fragments starting with the first added one.

In my case the fragments are used to show the content of a folder structure. When the context menu of a subfolder fragment is opened and a menu item is selected, onContextItemSelected() is first called on the upper levels (depending on how many fragments are allowed/visible in this moment).

Right now, I use a workaround by a field on activity level which holds the tag of last fragment calling its onCreateContextMenu(). This way I can call "return super.onContextItemSelected(item)" in the begin of onContextItemSelected() when the stored tag is not the same as getTag().
But this approach looks a bit dirty to me.

Why is onContextItemSelected() called on all fragments? and not just one the one that was calling onCreateContextMenu()?

What is the most elegant way to handle this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

穿越时光隧道 2024-10-29 12:58:09

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

I'll post an answer even though you found a workaround because I just dealt with a similar issue. When you inflate the context menu for a specific fragment, assign each menu item a groupId that is unique to the fragment. Then test for the groupId in 'onContextItemSelected.' For Example:

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

This way all of your fragments will still receive calls to 'onContextItemSelected,' but only the correct one will respond, thus avoiding the need to write activity-level code. I assume a modified version of this technique could work even though you aren't using 'menu.add(...)'

谈情不如逗狗 2024-10-29 12:58:09

另一种解决方案:

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

基于 Jake Wharton 的此补丁

Another one solution:

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

Based upon this patch from Jake Wharton.

忆依然 2024-10-29 12:58:09

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

之后,代码与之前相同。

I liked the simple solution by Sergei G (based on Jake Wharton fix), but inverted because it is easier to add to several fragments:

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

After that, the code same as it was before.

悲凉≈ 2024-10-29 12:58:09

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

就是这样。

I found out a very easy solution. As onCreateContextMenu() is called every time the ContextMenu is created I set a boolean variable to 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;
}

The only other thing I have to do is ask for that variable 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);
    }
}

That's it.

风柔一江水 2024-10-29 12:58:09

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

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

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

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

I found an alternative. It does not change anything on my problem above, but it makes it pointless.

I have remove the context menu completely from my application. Instead I capture the longclick on a list item and change the visible buttons of the action bar in this moment.
From the user point of view this is much more tablet like as a context menu.

In backward compatible applications the actionbar does not exist. So I've decided to build my own (kind of toolbar on top) for the devices pre Honeycomb.

If you would like to stay with the context menu, I did not find a better solution as the workaround I've mentioned above.

赢得她心 2024-10-29 12:58:09

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

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

If you are using adapters with listviews in your fragment this might help.

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()) {
        ...
    }
}
淡写薰衣草的香 2024-10-29 12:58:09

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

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

,将调用第二个片段。

In my first fragment, i have set all my menu id > 5000 so, as first line of code of onContextItemSelected of first fragment i have

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

and the second fragment will be invoked.

尐籹人 2024-10-29 12:58:09

只需更改

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

为即可

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

,效果会很好!

Just change

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

to

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

and will work great!!!

夜清冷一曲。 2024-10-29 12:58:09

恕我直言,我们可能只是检查目标视图是否是片段列表视图的子视图。它非常简单并且对我来说效果很好。我刚刚添加到所有片段:if (getListView.getPositionForView(info.targetView) == -1)
从旧 API 迁移时返回 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

PS 如果您跟踪 onContextItemSelected(...) 的调用,您可能会注意到 super.onContextItemSelected(item) 始终返回 false。有效的 onContextItemSelected 在之后调用,而不是在之内调用。所以 super.onContextItemSelected(item) 没有用,我用 false 替换它。

IMHO we may just check if target view is child of fragment listview. It is very simple and work for me well. I just added to all my fragments:if (getListView.getPositionForView(info.targetView) == -1)
return false
when migrate from older API

This is example from one of my parent fragments. This is Scala, but I hope you got an idea.

@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

P.S. If you trace calls of onContextItemSelected(...) you may notify that super.onContextItemSelected(item) return always false. Valid onContextItemSelected invoked AFTER, not WITHIN. So super.onContextItemSelected(item) is useless and I replaced it with false.

顾忌 2024-10-29 12:58:09

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

In the method changed return true; to return super.onContextItemSelected(item); in my onContextItemSelected() override and everything started working.

云醉月微眠 2024-10-29 12:58:09

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

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

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

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

I found an easier solution than the exposed:

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

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

    switch(item.getItemId()) {
        ...
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文