Imposta l'elemento selezionato in Android BottomNavigationView


123

Sto usando il nuovo android.support.design.widget.BottomNavigationViewdalla libreria di supporto. Come posso impostare la selezione corrente dal codice? Mi sono reso conto che la selezione è tornata al primo elemento, dopo aver ruotato lo schermo. Naturalmente contribuirebbe anche, se qualcuno mi potrebbe dire, come di "salvare" lo stato attuale della BottomNavigationViewnella onPausefunzione di e come ripristinare inonResume .

Grazie!


1
Ho appena dato un'occhiata alla fonte e ho la stessa domanda .. Non sembra che esista un metodo per impostare l'elemento selezionato e BottomNavigationViewnon esegue alcun salvataggio interno dello stato. Probabilmente si aspetta che questo venga incluso in un aggiornamento futuro. Duplica (con qualche informazione in più) qui: stackoverflow.com/questions/40236786/…
Tim Malseed

Risposte:


171

Dall'API 25.3.0 è stato introdotto il metodo setSelectedItemId(int id)che permette di contrassegnare un elemento come selezionato come se fosse stato toccato.

Dai documenti:

Imposta l'ID della voce di menu selezionata. Si comporta come quando si tocca un elemento.

Esempio di codice:

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

IMPORTANTE

È NECESSARIO già aggiunto tutti gli elementi al menu (nel caso in cui si esegue questa operazione di programmazione) ed impostare l'ascoltatore prima di chiamare setSelectedItemId (credo che si desidera che il codice nel tuo ascoltatore da eseguire quando si chiama questo metodo). Se chiami setSelectedItemId prima di aggiungere le voci di menu e impostare il listener, non accadrà nulla.


3
NON FUNZIONA, aggiorna solo la scheda stessa, non attiva un evento
setOnNavigationItemSelectedListener

1
Se non hai definito il comportamento nel onTabSelectedovviamente non farà nulla. Leggi i documenti -> Imposta l'ID della voce di menu selezionata. Si comporta come quando si tocca un elemento. Proviene da sviluppatori Android
Ricardo

1
Ti consiglio vivamente di eseguire il debug e rivedere ciò che stai facendo di sbagliato. Ho già implementato tre applicazioni BottomNavigationView e non ho mai avuto problemi con questo metodo.
Ricardo

@Ricardo Quando aggiungo questo al mio frammento, l'applicazione si è bloccata durante l'accesso a quel frammento
Subin Babu

1
Nel mio caso non funzionava, perché stavo creando il menu di navigazione in modo programmatico. Durante la costruzione il clic non ha funzionato. Dopo che tutti gli elementi sono stati aggiunti al menu, la chiamata a setSelectedItemId () ha funzionato.
Pedro Romão,

53

Per fare clic a livello di codice sull'elemento BottomNavigationBar è necessario utilizzare:

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

Questo organizza correttamente tutti gli elementi con le loro etichette.


4
Questo è un trucco per quanto riguarda il problema. Hai metodi dalla libreria stessa che ti permetteranno di eseguire ciò di cui hai bisogno. Se non puoi, è perché lo stai facendo male
Ricardo

47

Per coloro che usano ancora SupportLibrary <25.3.0

Non sono sicuro che questa sia una risposta completa a questa domanda, ma il mio problema era molto simile: ho dovuto elaborare la backpressione del pulsante e portare l'utente alla scheda precedente in cui si trovava. Quindi, forse la mia soluzione sarà utile per qualcuno:

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

Tieni presente che se l'utente preme un'altra scheda di navigazione BottomNavigationViewnon cancella l'elemento attualmente selezionato, quindi devi chiamare questo metodo onNavigationItemSelecteddopo l'elaborazione dell'azione di navigazione:

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

Per quanto riguarda il salvataggio dello stato dell'istanza, penso che potresti giocare con lo stesso action id vista di navigazione e trovare la soluzione adeguata.


Non Menu menu = bottomNavigationView.getMenu();restituisce una copia del menu, quindi l' bottomNavigationViewoggetto non viene influenzato?
最 白 目

1
@dan Secondo la fonte restituisce l'attributo di classe mMenu, non la copia android.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
Non è necessario deselezionare altri elementi, almeno il 26.1.0 / 27.1.0
Jemshit Iskenderov

non deselezionarli, li metterà in evidenza
djdance

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

dove si action_item1trova l'ID della voce di menu.


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

È ora possibile dalla versione 25.3.0 chiamare setSelectedItemId()\ o /


5

Aggiungi android:enabled="true"a elementi BottomNavigationMenu.

Quindi impostalo bottomNavigationView.setOnNavigationItemSelectedListener(mListener)e impostalo come selezionato facendobottomNavigationView.selectedItemId = R.id.your_menu_id


3

Questo sarà probabilmente aggiunto nei prossimi aggiornamenti. Ma nel frattempo, per ottenere ciò puoi usare la riflessione.

Crea una visualizzazione personalizzata che si estende da BottomNavigationView e accedi ad alcuni dei suoi campi.

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

}

E poi usalo nel tuo file di layout 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 riflessione è una cattiva idea!
Filipe Brito

performClick è meglio della riflessione, imho, in questo caso.
Lassi Kinnunen

3

Basta aggiungere un altro modo per eseguire una selezione in modo programmatico: questa è probabilmente l'intenzione in primo luogo o forse è stata aggiunta in seguito.

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

Il performIdentifierActionprende un MenuID oggetto e una bandiera.

Consulta la documentazione per maggiori informazioni.


Questo sembra eseguire l'azione che appartiene alla voce di menu, ma non fa apparire la voce di menu selezionata. Pensavo che quest'ultimo fosse ciò che voleva l'OP, ma non sono sicuro.
LarsH

3

Sembra essere corretto in SupportLibrary 25.1.0 :) Modifica: Sembra che lo stato della selezione venga salvato quando si ruota lo schermo.


1
Ho appena aggiornato alla 25.1.1. Il problema è ancora lì.
Xi Wei

3

Sopra l'API 25 puoi usare setSelectedItemId (menu_item_id) ma sotto l'API 25 devi fare in modo diverso, menu utente per ottenere l'handle e quindi setChecked su Checked elemento specifico


3

Utilizzare questo per impostare la voce di menu di navigazione inferiore selezionata per ID menu

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

2

Non vuoi modificare il tuo codice. In tal caso, ti ho consigliato di provare BottomNavigationViewEx

Hai solo bisogno di sostituire chiama un metodo setCurrentItem(index);e getCurrentItem()

Clicca qui per visualizzare l'immagine


2

uso

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


1

Puoi provare il metodo performClick:

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

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

L'unico "meno" della soluzione è l'utilizzo MenuItemImpl, che è "interno" alla libreria (sebbene pubblico).


0

SE HAI BISOGNO DI PASSARE DINAMICAMENTE GLI ARGOMENTI DEI FRAMMENTI, FARE QUESTO

Ci sono molte risposte (per lo più ripetute o obsolete) qui ma nessuna di esse gestisce un'esigenza molto comune: passare dinamicamente diversi argomenti al frammento caricato in una scheda .

Non è possibile passare dinamicamente argomenti diversi al frammento caricato utilizzando setSelectedItemId(R.id.second_tab), che finisce per chiamare statico OnNavigationItemSelectedListener. Per superare questa limitazione ho finito per fare questo nel mio MainActivityche contiene le schede:

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

Il ArticleFragment.newInstance()metodo è implementato come al solito:

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

Spero che aiuti

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

Si tratta più che altro di determinare il frammento predefinito caricato allo stesso tempo con la corrispondente voce del menu di navigazione in basso. Puoi includere lo stesso nei tuoi callback OnResume


0

Questo metodo funziona per me.

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

Esempio

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

-1

La riflessione è una cattiva idea.

Vai a questo succo . C'è un metodo che esegue la selezione ma richiama anche la richiamata:

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

    }

Uso callOnClick()invece di performClick(), in questo modo non ho bisogno di disabilitare il suono, che comunque non funzionava performClick().
arekolek
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.