Как обрабатывать onContextItemSelected в мульти фрагмент деятельности?
в настоящее время я пытаюсь адаптировать свое приложение для использования "библиотек совместимости для Android v4", чтобы обеспечить преимущества использования фрагментов даже для пользователей Android 1.6.
реализация контекстного меню кажется сложной:
- основная деятельность приложения расширяет FragmentActivity класс.
- все фрагменты основаны на одном класс, который расширяет класс фрагмента.
- в класс фрагмента вызывает registerForContextMenu() в своем onCreateView() метод и переопределяет методы onCreateContextMenu() и onContextItemSelected().
на onCreateContextMenu() это работает очень хорошо. Контекстное меню раздувается из файла ресурсов и слегка изменяется на основе выбранного элемента (который основан на listView... даже если фрагмент не является ListFragment).
проблема возникает при выборе пункта контекстного меню. onContextItemSelected() вызывается для всех существующих в настоящее время фрагментов, начиная с первого добавленного.
в моем случае фрагменты используются для показа содержимого папки. При открытии контекстного меню фрагмента вложенной папки и выборе пункта меню onContextItemSelected() сначала вызывают на верхних уровнях (в зависимости от того, сколько фрагменты разрешены / видны в этот момент).
прямо сейчас я использую обходной путь полем на уровне активности, которое содержит тег последнего фрагмента, вызывающего его onCreateContextMenu(). Таким образом я могу назвать "возвращение супер.onContextItemSelected (item)" в начале onContextItemSelected() если сохраненный тег не совпадает с getTag (). Но этот подход выглядит немного грязным для меня.
Почему onContextItemSelected () вызывается для всех фрагментов? и не только тот, который звонил onCreateContextMenu()?
каков самый элегантный способ справиться с этим?
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
.