Неправильный фрагмент в ViewPager получает вызов onContextItemSelected
У меня есть приложение, которое показывает несколько фрагментов (того же типа) в ViewPager
и у меня возникли проблемы с пунктами контекстного меню. (Я использую библиотеку поддержки).
Когда в контекстном меню в одном из фрагментов выбран пункт контекстного меню, неправильный фрагмент получает onContextItemSelected
Событие вызова.
Например, если я нахожусь на фрагменте № 3 в пейджере, фрагмент в позиции № 2 получает его вместо этого. Если я проведу назад к фрагменту № 2, фрагмент № 3 получит вызов вместо этого.
У меня есть образец здесь.
(В настоящее время я работаю над этим в моем собственном приложении, имея mHandleContext
переменная в каждом фрагменте и включение / отключение его при изменении страницы. Таким образом, onContextItemSelected
вызов будет идти на все фрагменты, пока не будет вызван правильный.)
Я делаю что-то не так или это ошибка в библиотеке поддержки? Как примечание, этого не произошло, когда я использовал ActionBarSherlock 3.5.1, который имел свой собственный форк библиотеки поддержки.
5 ответов
Так что это какое-то идиотское дизайнерское решение от Google или что-то, что просто осталось без внимания. Самый простой способ обойти это - обернуть onContextItemSelected
вызовите с оператором if, как это:
if (getUserVisibleHint()) {
// Handle menu events and return true
} else
return false; // Pass the event to the next fragment
В библиотеке совместимости в ActionBarSherlock 3.5 был такой взлом.
Это происходит из-за этого:
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;
}
}
О, Google, я имею в виду WTF?
Проблема в том, что onContextItemSelected является очень общим и вызывается для каждого пункта меню каждого фрагмента.
Вы можете использовать MenuItem.OnMenuItemClickListener, чтобы заставить фрагмент меню не использовать все 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:
// ...
}
}
getUserVisibleHint()
решение не работает для меня - оно всегда возвращается true
даже когда фрагмент не на экране. getGroupId()
Решение также не работает при накачивании меню из ресурса XML, что является моей ситуацией.
Кажется, что если Android не изменится, любое решение всегда будет немного хакерским. Я создаю глобальную переменную для хранения ссылки на идентификатор для текущего фрагмента в onCreateView
, Затем я передаю его каждому ContextMenu
MenuItem
в onCreateContextMenu
, Когда элемент выбран, я подтверждаю, что эти два идентификатора одинаковы.
private int myFragmentReference;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Initialisation stuff
myFragmentReference = 12345;
}
@Override
public void onCreateContextMenu(ContextMenu contextMenu, View v, ContextMenu.ContextMenuInfo contextMenuInfo) {
// Usual stuff
int size = contextMenu.size();
for (int i = 0; i < size; i++) {
MenuItem menuItem = contextMenu.getItem(i);
Intent intent = new Intent();
intent.putExtra("id", myFragmentReference);
menuItem.setIntent(intent);
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
int id = 0;
Intent intent = menuItem.getIntent();
if (intent != null) {
id = intent.getIntExtra("id", 0);
}
if (id == myFragmentReference) {
// This is the currently displayed fragment
}
}
Использование намерений для каждого из пунктов меню работало хорошо для меня.
@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);
}