Неверный фрагмент в ViewPager получает onContextItemВыбранный вызов
У меня есть приложение, которое показывает несколько фрагментов (одного типа) в ViewPager
, и у меня возникают некоторые проблемы с элементами контекстного меню. (Я использую библиотеку поддержки).
Когда в контекстном меню в одном из фрагментов выбирается элемент контекстного меню, неправильный фрагмент принимает вызов onContextItemSelected
.
Например, если я нахожусь на фрагменте # 3 в пейджере, вместо этого вместо него получает фрагмент в позиции # 2. Если я вернусь назад к фрагменту № 2, вместо этого вместо него будет найден фрагмент # 3.
У меня есть образец здесь.
(В настоящее время я работаю над этим в своем собственном приложении, имея переменную mHandleContext
в каждом фрагменте и включаю/отключая ее при изменении страницы. Таким образом, вызов onContextItemSelected
выйдет во все фрагменты пока не назовет правый.)
Я делаю что-то неправильно или это ошибка в библиотеке поддержки? В качестве побочной заметки этого не произошло, когда я использовал ActionBarSherlock 3.5.1, у которого была собственная вилка библиотеки поддержки.
Ответы
Ответ 1
Итак, это какое-то идиотское дизайнерское решение от Google или что-то, что просто совсем не считалось. Самый простой способ обойти это - обернуть вызов onContextItemSelected
с помощью оператора if следующим образом:
if (getUserVisibleHint()) {
// Handle menu events and return true
} else
return false; // Pass the event to the next fragment
В библиотеке совместимости в ActionBarSherlock 3.5 был взломан такой файл.
Ответ 2
Это происходит из-за этого:
public boolean dispatchContextItemSelected(MenuItem item) {
if (mActive != null) {
for (int i=0; i<mAdded.size(); i++) {
Fragment f = mAdded.get(i);
if (f != null && !f.mHidden) {
if (f.onContextItemSelected(item)) {
return true;
}
}
}
}
return false;
}
Как вы можете видеть, FragmentManager вызывает Fragment.onContextItemSelected для всех своих собственных фрагментов, пока не вернет true. В вашем примере я могу предложить такое исправление:
public static class TestListFragment extends ListFragment {
private int mNumber = 0;
private ArrayList<String> mItems;
public static TestListFragment newInstance(int number) {
Bundle args = new Bundle();
args.putInt("number", number + 1);
TestListFragment fragment = new TestListFragment();
fragment.setArguments(args);
return fragment;
}
public TestListFragment() {}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mNumber = getArguments().getInt("number");
mItems = new ArrayList<String>();
mItems.add("I am list #" + mNumber);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mItems));
registerForContextMenu(getListView());
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(mNumber, 0, 0, "Hello, World!");
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if(item.getGroupId() == mNumber){
Log.d("ViewPagerContextMenuBug", "onContextItemSelected called for number " + mNumber);
Toast.makeText(getActivity(), "onContextItemSelected called for number " + mNumber, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
}
Ответ 3
О, Google, я имею в виду WTF?
Проблема в том, что onContextItemSelected очень общий, и он вызывается для каждого пункта меню каждого фрагмента.
Вы можете использовать MenuItem.OnMenuItemClickListener, чтобы заставить меню фрагмента не использовать all onContextItemSelected, но только ту же функцию этого фрагмента.
Использовать следующую реализацию:
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getActivity().getMenuInflater();
if (v == btnShare) {
inflater.inflate(R.menu.share_menu, menu);
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
onContextItemSelected(item);
return true;
}
});
}
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case R.id.print:
// ...
}
}
Ответ 4
Использование намерений для каждого из пунктов меню работало хорошо для меня.
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextmenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = super.getActivity.getMenuInflater();
inflater.infalte(R.menu.list_item, menu);
for(int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
Intent intent = new Intent();
intent.putExtra(KEY_EXTRA_FRAGMENT_ID, this.fragmentId);
if (item != null) {
item.setIntent(intent);
}
}
}
@Override
public boolean onContextItemSelected(MeniItem item) {
Intent intent = item.getIntent();
if (intent != null) {
if (intent.getIntExtra(KEY_EXTRA_FRAGMENT_ID, -1) == this.fragmentId) {
// Implement code according the item function.
return true;
}
}
return super.onContextItemSelected(item);
}