Как обрабатывать onContextItemSelected в мульти фрагмент деятельности?


в настоящее время я пытаюсь адаптировать свое приложение для использования "библиотек совместимости для Android v4", чтобы обеспечить преимущества использования фрагментов даже для пользователей Android 1.6.

реализация контекстного меню кажется сложной:

  • основная деятельность приложения расширяет FragmentActivity класс.
  • все фрагменты основаны на одном класс, который расширяет класс фрагмента.
  • в класс фрагмента вызывает registerForContextMenu() в своем onCreateView() метод и переопределяет методы onCreateContextMenu() и onContextItemSelected().

на onCreateContextMenu() это работает очень хорошо. Контекстное меню раздувается из файла ресурсов и слегка изменяется на основе выбранного элемента (который основан на listView... даже если фрагмент не является ListFragment).

проблема возникает при выборе пункта контекстного меню. onContextItemSelected() вызывается для всех существующих в настоящее время фрагментов, начиная с первого добавленного.

в моем случае фрагменты используются для показа содержимого папки. При открытии контекстного меню фрагмента вложенной папки и выборе пункта меню onContextItemSelected() сначала вызывают на верхних уровнях (в зависимости от того, сколько фрагменты разрешены / видны в этот момент).

прямо сейчас я использую обходной путь полем на уровне активности, которое содержит тег последнего фрагмента, вызывающего его onCreateContextMenu(). Таким образом я могу назвать "возвращение супер.onContextItemSelected (item)" в начале onContextItemSelected() если сохраненный тег не совпадает с getTag (). Но этот подход выглядит немного грязным для меня.

Почему onContextItemSelected () вызывается для всех фрагментов? и не только тот, который звонил onCreateContextMenu()?

каков самый элегантный способ справиться с этим?

11 67

11 ответов:

Я отправлю ответ, даже если вы нашли обходной путь, потому что я только что имел дело с подобной проблемой. При раздувании контекстного меню для конкретного фрагмента назначьте каждому элементу меню уникальный для данного фрагмента идентификатор группы. Затем тест для группы в onContextItemSelected. Например:

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", но только правильный ответит, что позволит избежать необходимости писать уровень активности код. Я предполагаю, что модифицированная версия этого метода может работать, даже если вы не используете меню.добавлять.(..)'

еще одно решение:

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

на основе этот патч от Джейка Уортон.

Мне понравилось простое решение Сергея г (на основе Джейка Уортона fix), но перевернутое, потому что его легче добавить к нескольким фрагментам:

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

после этого код такой же, как и раньше.

Я нашел очень простое решение. Поскольку onCreateContextMenu () вызывается каждый раз, когда создается ContextMenu, я устанавливаю логическую переменную в 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);
    }
}

вот именно.

Я нашел альтернативу. Это ничего не меняет в моей проблеме выше, но делает ее бессмысленной.

Я полностью удалил контекстное меню из своего приложения. Вместо этого я захватываю longclick на элемент списка и изменить видимые кнопки панели действий в этот момент. С точки зрения пользователя это гораздо больше планшет, как в качестве контекстного меню.

в приложениях с обратной совместимостью панель действий не существует. Поэтому я решил построить свой собственный (вид панели инструментов сверху) для устройств pre Honeycomb.

Если вы хотите остаться с контекстным меню, я не нашел лучшего решения, как обходной путь, о котором я упоминал выше.

в моем первом фрагменте, я установил все мое меню id > 5000 так, как первая строка кода onContextItemSelected первого фрагмента у меня

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

и будет вызван второй фрагмент.

Если вы используете адаптеры с listviews в вашем фрагменте, это может помочь.

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

и будет отлично работать!!!

IMHO мы можем просто проверить, является ли целевое представление дочерним для фрагмента listview. Это очень просто и хорошо работает для меня. Я просто добавил ко всем своим фрагментам:if (getListView.getPositionForView(info.targetView) == -1) return false при миграции из старого API

это пример из одного из моих родителей фрагментов. Это скала, но я надеюсь, что у тебя есть идея.

@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. Если вы отслеживать звонки onContextItemSelected(...) вы можете уведомить об этом super.onContextItemSelected(item) всегда возвращает false. Допустимый onContextItemSelected вызывается после, не Внутри. Так что super.onContextItemSelected(item) бесполезно и Я заменил его с false.

Я нашел более простое решение, чем открытые:

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 () переопределить и все начало работать.