Comment gérer onContextItemSelected dans une activité multi-fragment?

J’essaie actuellement d’adapter mon application pour utiliser les “Bibliothèques de compatibilité pour Android v4” afin de bénéficier des avantages de l’utilisation des fragments, même pour les utilisateurs d’Android 1.6.

L’implémentation d’un menu contextuel semble être délicate:

  • L’activité principale de l’application est l’extension de la classe FragmentActivity .
  • Les fragments sont tous basés sur une classe qui étend la classe Fragment.
  • La classe de fragment appelle registerForContextMenu () dans sa méthode onCreateView () et remplace les méthodes onCreateContextMenu () et onContextItemSelected () .

Pour onCreateContextMenu (), cela fonctionne plutôt bien. Le menu contextuel est gonflé à partir d’un fichier de ressources et légèrement modifié en fonction de l’élément sélectionné (qui est basé sur un listView … même si le fragment n’est pas un ListFragment).

Le problème se produit lorsqu’une entrée de menu contextuel est sélectionnée. onContextItemSelected () est appelé pour tous les fragments existants à partir du premier ajouté.

Dans mon cas, les fragments sont utilisés pour afficher le contenu d’une structure de dossiers. Lorsque le menu contextuel d’un fragment de sous-dossier est ouvert et qu’un élément de menu est sélectionné, onContextItemSelected () est d’abord appelé sur les niveaux supérieurs (en fonction du nombre de fragments autorisés / visibles à ce moment).

En ce moment, j’utilise une solution de contournement par un champ au niveau de l’activité qui contient la balise du dernier fragment appelant son onCreateContextMenu () . De cette façon, je peux appeler “return super.onContextItemSelected (item)” au début de onContextItemSelected () lorsque la balise stockée n’est pas la même que getTag (). Mais cette approche me semble un peu sale.

Pourquoi onContextItemSelected () est-il appelé sur tous les fragments? et pas seulement celui qui appelait onCreateContextMenu () ?

Quelle est la manière la plus élégante de gérer cela?

Je posterai une réponse même si vous avez trouvé une solution parce que je viens de traiter un problème similaire. Lorsque vous gonflez le menu contextuel pour un fragment spécifique, atsortingbuez à chaque élément de menu un groupeId unique pour le fragment. Ensuite, testez le groupId dans ‘onContextItemSelected’. Par exemple:

public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) { menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_1, 0, R.ssortingng.src1); menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_2, 0, R.ssortingng.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 cette façon, tous vos fragments recevront toujours des appels à ‘onContextItemSelected’, mais seul le bon répondra, évitant ainsi d’avoir à écrire du code de niveau d’activité. Je suppose qu’une version modifiée de cette technique pourrait fonctionner même si vous n’utilisez pas “menu.add (…)”

Une autre solution:

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

Basé sur ce patch de Jake Wharton.

J’ai aimé la solution simple de Sergei G (basée sur le correctif de Jake Wharton), mais inversée car il est plus facile d’append plusieurs fragments:

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

Après cela, le code était le même qu’avant.

J’ai trouvé une solution très simple. Comme onCreateContextMenu () est appelé chaque fois que le ContextMenu est créé, je mets une variable booléenne à 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; } 

La seule autre chose à faire est de demander cette 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); } } 

C’est tout.

J’ai trouvé une alternative Cela ne change rien à mon problème ci-dessus, mais cela le rend inutile.

J’ai complètement supprimé le menu contextuel de mon application. Au lieu de cela, je capture le longclick sur un élément de la liste et modifie les boutons visibles de la barre d’action en ce moment. Du sharepoint vue de l’utilisateur, il s’agit bien plus d’une tablette que d’un menu contextuel.

Dans les applications rétro-compatibles, la barre d’action n’existe pas. J’ai donc décidé de construire mon propre type de barre d’outils pour les périphériques pré-Honeycomb.

Si vous souhaitez restr avec le menu contextuel, je n’ai pas trouvé de meilleure solution que la solution de contournement que j’ai mentionnée ci-dessus.

Dans mon premier fragment, j’ai défini tout mon identifiant de menu> 5000, en tant que première ligne de code de onContextItemSelected du premier fragment que j’ai

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

et le second fragment sera invoqué.

Si vous utilisez des adaptateurs avec des listes de lecture dans votre fragment, cela peut aider.

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

Juste changer

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

à

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

et travaillera très bien !!!

À mon humble avis, nous pouvons simplement vérifier si la vue cible est enfant de la liste de fragments. C’est très simple et fonctionne bien pour moi. Je viens d’append à tous mes fragments: if (getListView.getPositionForView(info.targetView) == -1) return false lors de la migration depuis une ancienne API

Ceci est l’exemple d’un de mes fragments parents. C’est Scala, mais j’espère que vous avez une idée.

 @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 

PS Si vous tracez les appels de onContextItemSelected (…), vous pouvez indiquer que super.onContextItemSelected(item) renvoie toujours false . Valid onContextItemSelected invoqué AFTER , pas WITHIN . Donc, super.onContextItemSelected(item) est inutile et je l’ai remplacé par false .

J’ai trouvé une solution plus facile que les exposés:

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

Dans la méthode a changé le retour vrai; pour retourner super.onContextItemSelected (item); dans mon onContextItemSelected () substitue et tout a commencé à fonctionner.