Legen Sie das ausgewählte Element in Android BottomNavigationView fest


121

Ich verwende das neue android.support.design.widget.BottomNavigationViewaus der Support-Bibliothek. Wie kann ich die aktuelle Auswahl aus dem Code einstellen? Mir wurde klar, dass die Auswahl nach dem Drehen des Bildschirms wieder auf das erste Element geändert wird. Natürlich würde es auch helfen, wenn mir jemand sagen könnte, wie man den aktuellen Status BottomNavigationViewder onPauseFunktion "speichert" und wie man sie wiederherstellt onResume.

Vielen Dank!


1
Ich habe mir gerade die Quelle angesehen und habe die gleiche Frage. Es sieht nicht so aus, als gäbe es eine Methode zum Festlegen des ausgewählten Elements, und BottomNavigationViewes wird kein interner Status gespeichert. Erwarten Sie wahrscheinlich, dass dies in einem zukünftigen Update enthalten sein wird. Duplizieren Sie (mit einigen weiteren Informationen) hier: stackoverflow.com/questions/40236786/…
Tim Malseed

Antworten:


170

Ab API 25.3.0 wurde die Methode eingeführt, mit der setSelectedItemId(int id)Sie ein Element als ausgewählt markieren können, als ob es getippt worden wäre.

Aus Dokumenten:

Stellen Sie die ausgewählte Menüelement-ID ein. Dies verhält sich genauso wie das Tippen auf ein Element.

Codebeispiel:

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

WICHTIG

Sie MÜSSEN bereits alle Elemente zum Menü hinzugefügt haben (falls Sie dies programmgesteuert tun) und den Listener festlegen, bevor Sie setSelectedItemId aufrufen (ich glaube, Sie möchten, dass der Code in Ihrem Listener ausgeführt wird, wenn Sie diese Methode aufrufen). Wenn Sie setSelectedItemId aufrufen, bevor Sie die Menüelemente hinzufügen und den Listener einstellen, geschieht nichts.


3
Dies funktioniert NICHT, es aktualisiert nur die Registerkarte selbst, es löst kein setOnNavigationItemSelectedListener-Ereignis aus
TjerkW

1
Wenn Sie das Verhalten in der onTabSelectedoffensichtlich nicht definiert haben , wird es nichts tun. Lesen Sie die Dokumente -> Stellen Sie die ausgewählte Menüelement-ID ein. Dies verhält sich genauso wie das Tippen auf ein Element. Es ist von Android-Entwicklern
Ricardo

1
Ich rate Ihnen dringend, zu debuggen und zu überprüfen, was Sie falsch machen. Ich habe bereits drei BottomNavigationView-Anwendungen implementiert und hatte nie ein Problem mit dieser Methode.
Ricardo

@ Ricardo Als ich dies zu meinem Fragment hinzufüge, wurde die Anwendung beim Betreten dieses Fragments hängen gelassen
Subin Babu

1
In meinem Fall hat es nicht funktioniert, weil ich das Navigationsmenü programmgesteuert erstellt habe. Während des Baus hat der Klick nicht funktioniert. Nachdem alle Elemente zum Menü hinzugefügt wurden, funktionierte der Aufruf von setSelectedItemId ().
Pedro Romão

53

Um programmgesteuert auf das Element BottomNavigationBar zu klicken, müssen Sie Folgendes verwenden:

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

Dadurch werden alle Elemente mit ihren Beschriftungen korrekt angeordnet.


4
Dies ist ein Hack in Bezug auf das Problem. Sie haben Methoden aus der Bibliothek selbst, mit denen Sie das ausführen können, was Sie benötigen. Wenn Sie nicht können, dann ist es, weil Sie es falsch machen
Ricardo

47

Für diejenigen, die noch SupportLibrary <25.3.0 verwenden

Ich bin nicht sicher, ob dies eine vollständige Antwort auf diese Frage ist, aber mein Problem war sehr ähnlich - ich musste den backTastendruck verarbeiten und den Benutzer zum vorherigen Tab bringen, auf dem er sich befand. Vielleicht ist meine Lösung für jemanden nützlich:

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

Beachten Sie bitte, dass, wenn der Benutzer auf eine andere Navigationsregisterkarte drückt BottomNavigationView, das aktuell ausgewählte Element nicht gelöscht wird. Sie müssen diese Methode daher onNavigationItemSelectednach der Verarbeitung der Navigationsaktion aufrufen :

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

In Bezug auf das Speichern des Instanzstatus denke ich, dass Sie mit derselben action idNavigationsansicht spielen und eine geeignete Lösung finden könnten .


Gibt Menu menu = bottomNavigationView.getMenu();keine Kopie des Menüs zurück, sodass das bottomNavigationViewObjekt nicht betroffen ist?
白 目

1
@dan Laut Quelle gibt es das Klassenattribut mMenu zurück, nicht die Kopie android.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
Sie müssen andere Elemente nicht deaktivieren, zumindest am 26.1.0 / 27.1.0
Jemshit Iskenderov

Nicht deaktivieren, es wird sie hervorheben
djdance

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

Wo action_item1ist die Menüpunkt-ID?


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

Seit der Version 25.3.0 ist es jetzt möglich, setSelectedItemId()\ o / aufzurufen .


5

In android:enabled="true"zu BottomNavigationMenu Artikel.

Und dann setzen bottomNavigationView.setOnNavigationItemSelectedListener(mListener)und setzen Sie es wie ausgewählt durch AusführenbottomNavigationView.selectedItemId = R.id.your_menu_id


3

Dies wird wahrscheinlich in den kommenden Updates hinzugefügt. In der Zwischenzeit können Sie jedoch Reflexion verwenden, um dies zu erreichen.

Erstellen Sie eine benutzerdefinierte Ansicht, die sich von BottomNavigationView aus erstreckt, und greifen Sie auf einige ihrer Felder zu.

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

}

Und dann verwenden Sie es in Ihrer XML-Layoutdatei.

<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
Reflexion ist eine schlechte Idee!
Filipe Brito

performClick ist in diesem Fall besser als Reflektion, imho.
Lassi Kinnunen

3

Fügen Sie einfach eine andere Möglichkeit hinzu, um eine Auswahl programmgesteuert durchzuführen - dies war wahrscheinlich die Absicht an erster Stelle, oder vielleicht wurde dies später hinzugefügt.

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

Das performIdentifierActionnimmt eine MenuArtikel-ID und eine Flagge.

Weitere Informationen finden Sie in der Dokumentation .


Dies scheint die zum Menüpunkt gehörende Aktion auszuführen, bewirkt jedoch nicht, dass der Menüpunkt ausgewählt erscheint. Ich dachte, Letzteres war das, was das OP wollte, aber ich bin nicht positiv.
LarsH

3

Scheint in SupportLibrary 25.1.0 behoben zu sein :) Bearbeiten: Es scheint behoben zu sein, dass der Status der Auswahl beim Drehen des Bildschirms gespeichert wird.


1
Ich habe gerade auf 25.1.1 aktualisiert. Das Problem besteht immer noch.
Xi Wei

3

Oberhalb von API 25 können Sie setSelectedItemId (menu_item_id) verwenden, aber unter API 25 müssen Sie das Benutzermenü anders ausführen, um das Handle zu erhalten, und dann setChecked to Checked spezifisches Element


2

Ich möchte Ihren Code nicht ändern. Wenn ja, empfahl ich Ihnen zu versuchen BottomNavigationViewEx .

Sie müssen nur ersetzen, eine Methode aufrufen setCurrentItem(index);und getCurrentItem()

Klicken Sie hier, um das Bild anzuzeigen


2

Verwenden Sie diese Option, um den ausgewählten unteren Navigationsmenüpunkt anhand der Menü-ID festzulegen

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


1

Sie können die performClick-Methode ausprobieren:

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

1

verwenden

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

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

Das einzige "Minus" der Lösung ist die Verwendung MenuItemImpl, die für die Bibliothek "intern" ist (obwohl öffentlich).


0

WENN SIE FRAGMENTARGUMENTE DYNAMISCH PASSIEREN MÜSSEN, TUN SIE DAS

Es gibt hier viele (meist wiederholte oder veraltete) Antworten, aber keine von ihnen erfüllt ein sehr häufiges Bedürfnis: Dynamische Übergabe verschiedener Argumente an das in eine Registerkarte geladene Fragment .

Sie können verschiedene Argumente nicht dynamisch an das geladene Fragment übergeben setSelectedItemId(R.id.second_tab), indem Sie das statische Element aufrufen OnNavigationItemSelectedListener. Um diese Einschränkung zu überwinden, habe ich dies in meinem MainActivityOrdner getan , der die folgenden Registerkarten enthält:

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

Die ArticleFragment.newInstance()Methode wird wie gewohnt implementiert:

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

ich hoffe das hilft

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

Es geht eher darum, das Standardfragment zu bestimmen, das gleichzeitig mit dem entsprechenden unteren Navigationsmenüpunkt geladen wird. Sie können dasselbe in Ihre OnResume-Rückrufe aufnehmen


0

Diese Methode funktioniert bei mir.

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

Beispiel

 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

Reflexion ist eine schlechte Idee.

Gehen Sie zu diesem Kern . Es gibt eine Methode, die die Auswahl durchführt, aber auch den Rückruf aufruft:

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

    }

Ich benutze callOnClick()stattdessen performClick(), auf diese Weise muss ich den Sound nicht deaktivieren, was performClick()sowieso nicht funktioniert hat.
Arekolek
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.