68 votes

Comment gérer onContextItemSelected dans une activité à fragments multiples ?

J'essaie actuellement d'adapter mon application pour utiliser les "Compatibility Libraries for Android v4" afin d'offrir les avantages de l'utilisation des fragments même aux utilisateurs d'Android 1.6.

Jusqu'à présent, cela a été possible sans grands problèmes, mais l'implémentation d'un menu contextuel semble être plus délicate...

  • L'activité principale de l'application est d'étendre le FragmentActivity classe.
  • Les fragments sont tous basés sur une classe qui étend la classe Fragment.
  • La classe de fragment appelle registerForContextMenu() dans son onCreateView() et remplace les méthodes onCreateContextMenu() y onContextItemSelected() .

Para onCreateContextMenu() ça marche 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 du menu contextuel est sélectionnée. onContextItemSelected() est appelé pour tous les fragments existants en commençant par le 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é aux niveaux supérieurs (en fonction du nombre de fragments autorisés/visibles à ce moment-là).

Pour l'instant, j'utilise une solution de contournement en utilisant un champ sur le niveau d'activité qui contient la balise du dernier fragment appelant son onCreateContextMenu() . De cette façon, je peux appeler "return super.onContextItemSelected(item)" dans le début de l'instruction onContextItemSelected() lorsque la balise stockée n'est pas la même que celle de getTag(). Mais cette approche me semble un peu sale.

Pourquoi onContextItemSelected() est-il appelé sur tous les fragments ? et pas seulement sur celui qui appelait onCreateContextMenu() ? Quelle est la manière la plus élégante de traiter cette question ?

Merci à l'avance.

68voto

Waterbear Points 918

Je vais poster une réponse même si vous avez trouvé une solution de contournement car je viens de traiter un problème similaire. Lorsque vous gonflez le menu contextuel pour un fragment spécifique, attribuez à chaque élément de menu un groupId qui est unique au fragment. Ensuite, tester pour 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.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 cette façon, tous vos fragments recevront toujours des appels à "onContextItemSelected", mais seul le bon fragment répondra, ce qui évitera de devoir écrire du code au niveau de l'activité. Je suppose qu'une version modifiée de cette technique pourrait fonctionner même si vous n'utilisez pas 'menu.add(...)'.

55voto

Sergey Glotov Points 8390

Une autre solution :

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

Sur la base de ce patch de Jake Wharton.

8voto

azelez Points 1081

J'ai aimé la solution simple de Sergei G (basée sur la solution de Jake Wharton), mais inversée parce qu'elle est plus facile à ajouter à 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 est le même qu'avant.

4voto

Rawpal Points 11

J'ai trouvé une solution très simple. Comme onCreateContextMenu() est appelé chaque fois que le ContextMenu est créé, j'ai mis 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 que je dois 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.

2voto

Lars K. Points 624

J'ai trouvé une alternative. Elle ne change rien à mon problème ci-dessus, mais elle le rend inutile.

J'ai supprimé complètement le menu contextuel de mon application. À la place, je capture le clic long sur un élément de la liste et je modifie les boutons visibles de la barre d'action à ce moment-là. Du point de vue de l'utilisateur, cela ressemble beaucoup plus à une tablette qu'un menu contextuel.

Dans les applications rétrocompatibles, la barre d'action n'existe pas. J'ai donc décidé de construire la mienne (une sorte de barre d'outils par dessus) pour les appareils pré-Honeycomb.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X