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