El fragmento incorrecto en ViewPager recibe la llamada onContextItemSelected

Tengo una aplicación que muestra algunos fragmentos (del mismo tipo) en un ViewPager y estoy teniendo problemas con los elementos del menú contextual. (Estoy usando la biblioteca de soporte).

Cuando se selecciona un elemento del menú contextual en el menú contextual en uno de los fragmentos, el fragmento incorrecto recibe la onContextItemSelected evento onContextItemSelected .

Por ejemplo, si estoy en el fragmento n. ° 3 en el localizador, el fragmento en la posición n. ° 2 lo recibe en su lugar. Si vuelvo al fragmento n. ° 2, el fragmento n. ° 3 recibe la llamada.

Tengo una muestra aquí .

(Actualmente estoy trabajando en esto en mi propia aplicación al tener una variable mHandleContext en cada fragmento y habilitarla / deshabilitarla cuando se cambie la página. De esta forma, la llamada onContextItemSelected enviará a todos los fragmentos hasta que se llame a la derecha. )

¿Estoy haciendo algo mal o es un error con la biblioteca de soporte? Como nota al margen, esto no sucedió cuando estaba usando ActionBarSherlock 3.5.1, que tenía su propio tenedor de la biblioteca de soporte.

Entonces, este es un tipo de decisión estúpida de diseño de Google o algo que simplemente ha pasado totalmente desapercibido. La forma más sencilla de evitar esto es ajustar la llamada onContextItemSelected con una instrucción if como esta:

 if (getUserVisibleHint()) { // Handle menu events and return true } else return false; // Pass the event to the next fragment 

La biblioteca de compatibilidad en ActionBarSherlock 3.5 tenía un truco como este.

Sucede debido a esto:

 public boolean dispatchContextItemSelected(MenuItem item) { if (mActive != null) { for (int i=0; i 

Como puede ver, FragmentManager llama a Fragment.onContextItemSelected para todos sus propios fragmentos hasta que devuelve verdadero. En su ejemplo, puedo ofrecer dicha corrección:

  public static class TestListFragment extends ListFragment { private int mNumber = 0; private ArrayList 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(); mItems.add("I am list #" + mNumber); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setListAdapter(new ArrayAdapter(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; } } 

Oh Google, me refiero a WTF?

El problema es que onContextItemSelected es muy genérico, y se llama para cada elemento de menú de cada fragmento.

Puede usar MenuItem.OnMenuItemClickListener para forzar que el menú de fragmentos no use todo onContextItemSelected , pero solo la misma función de este fragmento.

Use la siguiente implementación:

 @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: // ... } } 

Usar intenciones para cada uno de los elementos del menú funcionó bien para mí.

  @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); } 

La solución getUserVisibleHint() no funciona para mí; siempre devuelve true incluso cuando el fragmento no está en la pantalla. La solución getGroupId() tampoco funciona al inflar el menú desde un recurso XML, que es mi situación.

Parece que a menos que Android cambie, cualquier solución siempre será un poco hacky. onCreateView una variable global para almacenar una referencia de ID para el fragmento actual en onCreateView . Luego lo paso a cada MenuItem ContextMenu en onCreateContextMenu . Cuando se selecciona un elemento, valido que estos dos ID son los mismos.

 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 } }