tipos studio registerforcontextmenu oncreatemenu inflar flotante extendido example contextual bar android contextmenu android-fragments android-support-library

studio - registerforcontextmenu android



¿Cómo se maneja onContextItemSelected en una actividad de múltiples fragmentos? (11)

Actualmente estoy tratando de adaptar mi aplicación para usar las "Bibliotecas de compatibilidad para Android v4" para proporcionar los beneficios del uso de fragmentos incluso para usuarios de Android 1.6.

La implementación de un menú contextual parece ser difícil:

  • La actividad principal de la aplicación es extender la clase FragmentActivity .
  • Los fragmentos están basados ​​en una clase que extiende la clase Fragment.
  • La clase de fragmento llama a registerForContextMenu () en su método onCreateView () y anula los métodos onCreateContextMenu () y onContextItemSelected () .

Para onCreateContextMenu () esto funciona bastante bien. El menú de contexto está inflado de un archivo de recursos y ligeramente modificado en función del elemento seleccionado (que se basa en un ListView ... incluso si el fragmento no es un ListFragment).

El problema ocurre cuando se selecciona una entrada del menú de contexto. onContextItemSelected () se llama para todos los fragmentos existentes comenzando por el primero añadido.

En mi caso, los fragmentos se utilizan para mostrar el contenido de una estructura de carpetas. Cuando se abre el menú contextual de un fragmento de subcarpeta y se selecciona un elemento de menú, onContextItemSelected () se llama primero en los niveles superiores (dependiendo de cuántos fragmentos estén permitidos / visibles en este momento).

En este momento, uso una solución por un campo en el nivel de actividad que contiene la etiqueta del último fragmento llamando a onCreateContextMenu () . De esta forma puedo llamar "return super.onContextItemSelected (item)" en el comienzo de onContextItemSelected () cuando la etiqueta almacenada no es la misma que getTag (). Pero este enfoque me parece un poco sucio.

¿Por qué onContextItemSelected () invoca todos los fragmentos? y no solo uno que llamaba aCreateContextMenu () ?

¿Cuál es la forma más elegante de manejar esto?


Descubrí una solución muy fácil. Como se llama onCreateContextMenu () cada vez que se crea el ContextMenu, establezco una variable booleana en verdadero.

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

La única otra cosa que tengo que hacer es pedir esa variable 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); } }

Eso es.


En el método cambiado return true; para devolver super.onContextItemSelected (item); en mi onContextItemSelected () anula y todo comenzó a funcionar.


En mi humilde opinión, podemos simplemente verificar si la vista objetivo es hija de la vista de lista de fragmentos. Es muy simple y funciona bien para mí. Acabo de agregar a todos mis fragmentos: if (getListView.getPositionForView(info.targetView) == -1) return false cuando migra desde API anterior

Este es un ejemplo de uno de mis fragmentos principales. Esto es Scala, pero espero que tengas una idea.

@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

PD Si rastrea llamadas de onContextItemSelected (...) puede notificar que super.onContextItemSelected(item) return siempre es false . Válido onContextItemSelected invocado DESPUÉS, no DENTRO . Así que super.onContextItemSelected(item) es inútil y lo reemplacé con false .


En mi primer fragmento, configuré todo mi ID de menú> 5000, así que, como primera línea de código de onContextItemSelected del primer fragmento, tengo

if (item.getItemId() < 5000) return false;

y el segundo fragmento será invocado.


Encontré una alternativa. No cambia nada en mi problema anterior, pero lo hace inútil.

He eliminado completamente el menú contextual de mi aplicación. En su lugar, capturo el clic largo en un elemento de la lista y cambio los botones visibles de la barra de acción en este momento. Desde el punto de vista del usuario, esto es mucho más que una tableta como un menú contextual.

En aplicaciones compatibles hacia atrás, la barra de acciones no existe. Así que decidí construir mi propio (tipo de barra de herramientas en la parte superior) para los dispositivos pre Honeycomb.

Si desea permanecer con el menú contextual, no encontré una solución mejor como la solución que he mencionado anteriormente.


Encontré una solución más fácil que la expuesta:

public boolean onContextItemSelected(MenuItem item) { ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList); if (!yourList.hasFocus()) return false; switch(item.getItemId()) { ... } }


Me gustó la solución simple de Sergei G (basada en el arreglo de Jake Wharton), pero invertida porque es más fácil agregarla a varios fragmentos:

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

Después de eso, el código es el mismo que antes.


Otra solución única:

@Override public boolean onContextItemSelected(MenuItem item) { if (getUserVisibleHint()) { // context menu logic return true; } return false; }

Basado en este parche de Jake Wharton.


Publicaré una respuesta a pesar de que encontraste una solución porque acabo de resolver un problema similar. Cuando infle el menú contextual para un fragmento específico, asigne a cada elemento del menú un ID de grupo que sea único para el fragmento. Luego, prueba para groupId en ''onContextItemSelected''. Por ejemplo:

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

De esta forma, todos sus fragmentos seguirán recibiendo llamadas a ''onContextItemSelected'', pero solo responderá el correcto, evitando así la necesidad de escribir código de nivel de actividad. Supongo que una versión modificada de esta técnica podría funcionar incluso si no está utilizando ''menu.add (...)''


Si está utilizando adaptadores con listviews en su fragmento, esto podría ayudar.

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()) { ... } }


Solo cambia

@Override public boolean onContextItemSelected(MenuItem item) { return true; }

a

@Override public boolean onContextItemSelected(MenuItem item) { return super.onContextItemSelected(item); }

y funcionará genial !!!