Définir l'élément sélectionné dans Android BottomNavigationView


123

J'utilise le nouveau android.support.design.widget.BottomNavigationViewde la bibliothèque de support. Comment puis-je définir la sélection actuelle à partir du code? Je me suis rendu compte que la sélection est revenue au premier élément, après avoir fait pivoter l'écran. Bien sûr, cela aiderait également, si quelqu'un pouvait me dire, comment "enregistrer" l'état actuel de BottomNavigationViewla onPausefonction dans la fonction et comment le restaurer dans onResume.

Merci!


1
Je viens de jeter un œil à la source et j'ai la même question ... Il ne semble pas qu'il y ait une méthode pour définir l'élément sélectionné, et BottomNavigationViewne fait aucune sauvegarde interne de l'état. Attendez-vous probablement à ce que cela soit inclus dans une future mise à jour. Dupliquer (avec quelques informations supplémentaires) ici: stackoverflow.com/questions/40236786/…
Tim Malseed

Réponses:


171

À partir de l'API 25.3.0, il a été introduit la méthode setSelectedItemId(int id)qui vous permet de marquer un élément comme sélectionné comme s'il avait été tapé.

À partir de la documentation:

Définissez l'ID de l'élément de menu sélectionné. Cela se comporte comme un appui sur un élément.

Exemple de code:

BottomNavigationView bottomNavigationView;
bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(myNavigationItemListener);
bottomNavigationView.setSelectedItemId(R.id.my_menu_item_id);

IMPORTANT

Vous DEVEZ avoir déjà ajouté tous les éléments au menu (au cas où vous le feriez par programme) et définissez l'écouteur avant d'appeler setSelectedItemId (je crois que vous voulez que le code de votre écouteur s'exécute lorsque vous appelez cette méthode). Si vous appelez setSelectedItemId avant d'ajouter les éléments de menu et de définir l'écouteur, rien ne se passera.


3
Cela ne fonctionne pas, cela ne met à jour que l'onglet lui-même, cela ne déclenche pas un événement
setOnNavigationItemSelectedListener

1
Si vous n'avez pas défini le comportement dans onTabSelectedévidemment, cela ne fera rien. Lisez la documentation -> Définissez l'ID de l'élément de menu sélectionné. Cela se comporte comme un appui sur un élément. C'est de développeurs Android
Ricardo

1
Je vous conseille vivement de déboguer et de revoir ce que vous faites de mal. J'ai déjà implémenté trois applications BottomNavigationView et je n'ai jamais eu de problème avec cette méthode.
Ricardo le

@Ricardo Quand j'ajoute ceci à mon fragment, l'application s'est bloquée en entrant dans ce fragment
Subin Babu

1
Dans mon cas, cela ne fonctionnait pas, car je créais navigationMenu par programme. Pendant la construction, le clic n'a pas fonctionné. Une fois tous les éléments ajoutés au menu, l'appel de setSelectedItemId () fonctionnait.
Pedro Romão

53

Pour cliquer par programme sur l'élément BottomNavigationBar, vous devez utiliser:

View view = bottomNavigationView.findViewById(R.id.menu_action_item);
view.performClick();

Cela organise correctement tous les éléments avec leurs étiquettes.


4
Ceci est un hack concernant le problème. Vous disposez de méthodes de la bibliothèque elle-même qui vous permettront d'effectuer ce dont vous avez besoin. Si vous ne pouvez pas, c'est parce que vous le faites mal
Ricardo

47

Pour ceux qui utilisent encore SupportLibrary <25.3.0

Je ne suis pas sûr que ce soit une réponse complète à cette question, mais mon problème était très similaire - j'ai dû traiter la backpression sur un bouton et amener l'utilisateur à l'onglet précédent où il se trouvait. Alors, peut-être que ma solution sera utile à quelqu'un:

private void updateNavigationBarState(int actionId){
    Menu menu = bottomNavigationView.getMenu();

    for (int i = 0, size = menu.size(); i < size; i++) {
        MenuItem item = menu.getItem(i);
        item.setChecked(item.getItemId() == actionId);
    }
}

Veuillez garder à l'esprit que si l'utilisateur appuie sur un autre onglet de navigation n'effacera BottomNavigationViewpas l'élément actuellement sélectionné, vous devez donc appeler cette méthode dans votre onNavigationItemSelectedaprès le traitement de l'action de navigation:

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    switch (item.getItemId()) {
        case R.id.some_id_1:
            // process action
            break;
        case R.id.some_id_2:
            // process action
            break;
        ...
        default:
            return false;
    }

    updateNavigationBarState(item.getItemId());

    return true;
}

En ce qui concerne la sauvegarde de l'état de l'instance, je pense que vous pouvez jouer avec la même action idvue de navigation et trouver une solution appropriée.


Ne Menu menu = bottomNavigationView.getMenu();renvoie pas une copie du menu, donc l' bottomNavigationViewobjet n'est pas affecté?
最 白 目

1
@dan Selon la source, il renvoie l'attribut de classe mMenu, pas la copie android.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
Vous n'avez pas besoin de décocher d'autres éléments, au moins sur 26.1.0 / 27.1.0
Jemshit Iskenderov

ne pas décocher, cela les mettra en évidence
djdance

19
bottomNavigationView.setSelectedItemId(R.id.action_item1);

action_item1est l'ID de l'élément de menu.


Ça ne marche pas pour moi.
Kristy Welsh

8
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

   bottomNavigationView.setOnNavigationItemSelectedListener(this);
   Menu menu = bottomNavigationView.getMenu();
   this.onNavigationItemSelected(menu.findItem(R.id.action_favorites));
}

7

Il est désormais possible depuis la version 25.3.0 d'appeler setSelectedItemId()\ o /


5

Ajouter android:enabled="true"aux éléments de BottomNavigationMenu.

Et puis définissez bottomNavigationView.setOnNavigationItemSelectedListener(mListener)et définissez-le comme sélectionné en faisantbottomNavigationView.selectedItemId = R.id.your_menu_id


3

Cela sera probablement ajouté dans les prochaines mises à jour. Mais en attendant, pour ce faire, vous pouvez utiliser la réflexion.

Créez une vue personnalisée à partir de BottomNavigationView et accédez à certains de ses champs.

public class SelectableBottomNavigationView extends BottomNavigationView {

    public SelectableBottomNavigationView(Context context) {
        super(context);
    }

    public SelectableBottomNavigationView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SelectableBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public void setSelected(int index) {
        try {
            Field f = BottomNavigationView.class.getDeclaredField("mMenuView");
            f.setAccessible(true);
            BottomNavigationMenuView menuView = (BottomNavigationMenuView) f.get(this);

            try {
                Method method = menuView.getClass().getDeclaredMethod("activateNewButton", Integer.TYPE);
                method.setAccessible(true);
                method.invoke(menuView, index);
            } catch (SecurityException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

Et puis utilisez-le dans votre fichier de mise en page xml.

<com.your.app.SelectableBottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:itemBackground="@color/primary"
        app:itemIconTint="@drawable/nav_item_color_state"
        app:itemTextColor="@drawable/nav_item_color_state"
        app:menu="@menu/bottom_navigation_menu"/>

3
La réflexion est une mauvaise idée!
Filipe Brito

performClick est mieux que la réflexion, à mon humble avis, dans ce cas.
Lassi Kinnunen

3

Il suffit d'ajouter une autre façon d'effectuer une sélection par programme - c'est probablement ce qui était l'intention en premier lieu ou peut-être que cela a été ajouté plus tard.

Menu bottomNavigationMenu = myBottomNavigationMenu.getMenu();
bottomNavigationMenu.performIdentifierAction(selected_menu_item_id, 0);

Le performIdentifierActionprend un Menuidentifiant d'élément et un indicateur.

Consultez la documentation pour plus d'informations.


Cela semble exécuter l'action appartenant à l'élément de menu, mais ne fait pas apparaître l'élément de menu sélectionné. Je pensais que c'était ce que voulait l'OP, mais je ne suis pas sûr.
LarsH

3

Semble être corrigé dans SupportLibrary 25.1.0 :) Edit: Il semble être fixe, que l'état de la sélection est sauvegardé, lors de la rotation de l'écran.


1
Je viens de mettre à jour vers 25.1.1. Le problème est toujours là.
Xi Wei

3

Au-dessus de l'API 25, vous pouvez utiliser setSelectedItemId (menu_item_id) mais sous l'API 25, vous devez faire différemment, le menu utilisateur pour obtenir la poignée, puis définirChecked sur l'élément spécifique vérifié


3

Utilisez ceci pour définir l'élément de menu de navigation inférieur sélectionné par identifiant de menu

MenuItem item = mBottomNavView.getMenu().findItem(menu_id);
item.setChecked(true);

2

Je ne veux pas modifier votre code. Si tel est le cas, je vous ai recommandé d'essayer BottomNavigationViewEx

Vous avez juste besoin de remplacer, d'appeler une méthode setCurrentItem(index);et getCurrentItem()

Cliquez ici pour voir l'image


2

utilisation

        bottomNavigationView.getMenu().getItem(0).setChecked(true);


1

Vous pouvez essayer la méthode performClick:

View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();

0

La réflexion est une mauvaise idée.

Dirigez-vous vers cet essentiel . Il existe une méthode qui effectue la sélection mais invoque également le rappel:

  @CallSuper
    public void setSelectedItem(int position) {
        if (position >= getMenu().size() || position < 0) return;

        View menuItemView = getMenuItemView(position);
        if (menuItemView == null) return;
        MenuItemImpl itemData = ((MenuView.ItemView) menuItemView).getItemData();


        itemData.setChecked(true);

        boolean previousHapticFeedbackEnabled = menuItemView.isHapticFeedbackEnabled();
        menuItemView.setSoundEffectsEnabled(false);
        menuItemView.setHapticFeedbackEnabled(false); //avoid hearing click sounds, disable haptic and restore settings later of that view
        menuItemView.performClick();
        menuItemView.setHapticFeedbackEnabled(previousHapticFeedbackEnabled);
        menuItemView.setSoundEffectsEnabled(true);


        mLastSelection = position;

    }

J'utilise à la callOnClick()place de performClick(), de cette façon je n'ai pas besoin de désactiver le son, qui ne fonctionnait pas de performClick()toute façon.
arekolek

0
private void setSelectedItem(int actionId) {
    Menu menu = viewBottom.getMenu();
    for (int i = 0, size = menu.size(); i < size; i++) {
        MenuItem menuItem = menu.getItem(i);
        ((MenuItemImpl) menuItem).setExclusiveCheckable(false);
        menuItem.setChecked(menuItem.getItemId() == actionId);
        ((MenuItemImpl) menuItem).setExclusiveCheckable(true);
    }
}

Le seul «moins» de la solution est l'utilisation MenuItemImpl, qui est «interne» à la bibliothèque (bien que publique).


0

SI VOUS AVEZ BESOIN DE PASSER DYNAMIQUEMENT DES ARGUMENTS DE FRAGMENT, FAITES CELA

Il y a beaucoup de réponses (pour la plupart répétées ou obsolètes) ici mais aucune d'elles ne répond à un besoin très courant: passer dynamiquement différents arguments au fragment chargé dans un onglet .

Vous ne pouvez pas passer dynamiquement différents arguments au fragment chargé en utilisant setSelectedItemId(R.id.second_tab), ce qui finit par appeler le static OnNavigationItemSelectedListener. Pour surmonter cette limitation, j'ai fini par faire cela dans mon MainActivityqui contient les onglets:

fun loadArticleTab(articleId: String) {
    bottomNavigationView.menu.findItem(R.id.tab_article).isChecked = true // use setChecked() in Java
    supportFragmentManager
        .beginTransaction()
        .replace(R.id.main_fragment_container, ArticleFragment.newInstance(articleId))
        .commit()
}

La ArticleFragment.newInstance()méthode est implémentée comme d'habitude:

private const val ARG_ARTICLE_ID = "ARG_ARTICLE_ID"

class ArticleFragment : Fragment() {

    companion object {
        /**
         * @return An [ArticleFragment] that shows the article with the given ID.
         */
        fun newInstance(articleId: String): ArticleFragment {
            val args = Bundle()
            args.putString(ARG_ARTICLE_ID, day)
            val fragment = ArticleFragment()
            fragment.arguments = args
            return fragment
        }
    }

}

0

J'espère que ça aide

//Setting default selected menu item and fragment
        bottomNavigationView.setSelectedItemId(R.id.action_home);
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.fragment_container, new HomeFragment()).commit();

Il s'agit plus de déterminer le fragment par défaut chargé en même temps avec l'élément de menu de navigation inférieur correspondant. Vous pouvez inclure la même chose dans vos rappels OnResume


0

Cette méthode fonctionne pour moi.

private fun selectBottomNavigationViewMenuItem(bottomNavigationView : BottomNavigationView,@IdRes menuItemId: Int) {
            bottomNavigationView.setOnNavigationItemSelectedListener(null)
            bottomNavigationView.selectedItemId = menuItemId
            bottomNavigationView.setOnNavigationItemSelectedListener(this)
        }

Exemple

 override fun onBackPressed() {
        replaceFragment(HomeFragment())
        selectBottomNavigationViewMenuItem(navView, R.id.navigation_home)
    }



private fun replaceFragment(fragment: Fragment) {
        val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        transaction.replace(R.id.frame_container, fragment)
        transaction.commit()
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.