Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

Ответ 3

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

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

Основываясь на этом патче от Jake Wharton.

Ответ 4

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

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

Я нашел очень легкое решение. Поскольку 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);
    }
}

Что это.

Ответ 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

IMHO мы можем просто проверить, является ли целевой вид дочерним по отношению к списку фрагментов. Это очень просто и хорошо работает для меня. Я просто добавил ко всем моим фрагментам: 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

P.S. Если вы отслеживаете вызовы onContextItemSelected (...), вы можете уведомить, что super.onContextItemSelected(item) всегда возвращает false. Valid onContextItemSelected вызывается ПОСЛЕ, а не WITHIN. Поэтому 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

В методе changed return true; для возврата super.onContextItemSelected(item); в моем onContextItemSelected() переопределении и все началось.