Ustaw wybrany element w widoku Android BottomNavigationView


123

Używam nowego android.support.design.widget.BottomNavigationViewz biblioteki wsparcia. Jak mogę ustawić aktualny wybór z kodu? Zdałem sobie sprawę, że po obróceniu ekranu następuje zmiana wyboru z powrotem na pierwszą pozycję. Oczywiście, że może przyczynić się również, czy ktoś może mi powiedzieć, jak do „Zapisz” aktualnego stanu BottomNavigationVieww onPausefunkcji i jak przywrócić go onResume.

Dzięki!


1
Właśnie spojrzałem na źródło i mam to samo pytanie .. Wygląda na to, że nie ma metody ustawiania wybranej pozycji i BottomNavigationViewnie wykonuje żadnego wewnętrznego zapisywania stanu. Prawdopodobnie spodziewaj się, że zostanie to uwzględnione w przyszłej aktualizacji. Duplikat (z dodatkowymi informacjami) tutaj: stackoverflow.com/questions/40236786/…
Tim Malseed

Odpowiedzi:


171

Od API 25.3.0 została wprowadzona metoda, setSelectedItemId(int id)która pozwala oznaczyć element jako wybrany tak, jakby był dotknięty.

Z dokumentów:

Ustaw ID wybranej pozycji menu. Działa to tak samo, jak stuknięcie w element.

Przykład kodu:

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

WAŻNY

Państwo musi już dodane wszystkie elementy do menu (w przypadku, gdy to zrobić programowo) i ustaw Listener przed wywołaniem setSelectedItemId (wierzę chcesz kod w swoim słuchacza do uruchomienia po wywołaniu tej metody). Jeśli wywołasz setSelectedItemId przed dodaniem elementów menu i ustawieniem słuchacza, nic się nie stanie.


3
To NIE DZIAŁA, tylko aktualizuje samą kartę, nie wywołuje zdarzenia
setOnNavigationItemSelectedListener

1
Jeśli nie zdefiniowałeś zachowania, to onTabSelectedoczywiście nic nie da. Przeczytaj dokumentację -> Ustaw identyfikator wybranego elementu menu. Działa to tak samo, jak stuknięcie w element. Pochodzi od programistów Androida
Ricardo,

1
Gorąco radzę debugować i sprawdzić, co robisz źle. Zaimplementowałem już trzy aplikacje BottomNavigationView i nigdy nie miałem problemu z tą metodą.
Ricardo

@Ricardo Kiedy dodałem to do mojego fragmentu, aplikacja zawiesiła się przy wchodzeniu do tego fragmentu
Subin Babu

1
W moim przypadku nie działało, ponieważ tworzyłem navigationMenu programowo. Podczas budowy klik nie działał. Po dodaniu wszystkich elementów do menu, zadziałało wywołanie setSelectedItemId ().
Pedro Romão

53

Aby programowo kliknąć element BottomNavigationBar, którego potrzebujesz:

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

Spowoduje to prawidłowe ułożenie wszystkich elementów wraz z ich etykietami.


4
To jest hack dotyczący problemu. Masz metody z samej biblioteki, które pozwolą Ci wykonać to, czego potrzebujesz. Jeśli nie możesz, to dlatego, że robisz to źle
Ricardo

47

Dla tych, którzy nadal używają SupportLibrary <25.3.0

Nie jestem pewien, czy to pełna odpowiedź na to pytanie, ale mój problem był bardzo podobny - musiałem przetworzyć backnaciśnięcie przycisku i przenieść użytkownika do poprzedniej zakładki, w której się znajdował. Więc może moje rozwiązanie przyda się komuś:

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

Pamiętaj, że jeśli użytkownik naciśnie inną zakładkę nawigacji BottomNavigationViewnie wyczyści aktualnie wybranej pozycji, musisz wywołać tę metodę w swojej onNavigationItemSelectedakcji nawigacyjnej po przetworzeniu:

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

Jeśli chodzi o zapisywanie stanu instancji, myślę, że możesz pobawić się tym samym action idwidokiem nawigacji i znaleźć odpowiednie rozwiązanie.


Nie Menu menu = bottomNavigationView.getMenu();zwraca kopii menu, więc bottomNavigationViewobiekt nie jest dotknięty?
最 白 目

1
@dan Według źródła zwraca atrybut klasy mMenu, a nie kopię android.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
Nie musisz odznaczać innych pozycji, przynajmniej 26.1.0 / 27.1.0
Jemshit Iskenderov

nie
odznaczaj

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

gdzie action_item1jest identyfikator pozycji 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));
}


5

Add android:enabled="true"to BottomNavigationMenu Items.

A następnie ustaw bottomNavigationView.setOnNavigationItemSelectedListener(mListener)i ustaw jako wybrany, wykonującbottomNavigationView.selectedItemId = R.id.your_menu_id


3

Prawdopodobnie zostanie to dodane w nadchodzących aktualizacjach. Ale w międzyczasie, aby to osiągnąć, możesz użyć refleksji.

Utwórz widok niestandardowy rozszerzający się z BottomNavigationView i uzyskaj dostęp do niektórych jego pól.

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

}

A następnie użyj go w pliku układu 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
Odbicie to zły pomysł!
Filipe Brito

performClick jest lepsze niż odbicie, imho, w tym przypadku.
Lassi Kinnunen

3

Po prostu dodanie innego sposobu programowego przeprowadzenia selekcji - prawdopodobnie taki był zamiar na początku, a może został dodany później.

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

performIdentifierActionZajmuje Menuidentyfikator elementu i flagę.

Więcej informacji można znaleźć w dokumentacji .


Wydaje się, że wykonuje akcję należącą do elementu menu, ale nie powoduje, że element menu jest wyświetlany jako wybrany. Myślałem, że to drugie było tym, czego chciał OP, ale nie jestem pewien.
LarsH

3

Wydaje się, że zostało to naprawione w SupportLibrary 25.1.0 :) Edycja: Wydaje się, że naprawiono, że stan zaznaczenia jest zapisywany podczas obracania ekranu.


1
Właśnie zaktualizowałem do wersji 25.1.1. Problem nadal istnieje.
Xi Wei,

3

Powyżej API 25 możesz użyć setSelectedItemId (menu_item_id), ale pod API 25 musisz zrobić inaczej, menu użytkownika, aby uzyskać uchwyt, a następnie ustawić Checked na zaznaczony konkretny element


3

Użyj tego, aby ustawić wybraną dolną pozycję menu nawigacji według identyfikatora menu

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

2

Nie chcesz modyfikować swojego kodu. Jeśli tak, polecam wypróbowanie BottomNavigationViewEx

Wystarczy wymienić wywołanie metody setCurrentItem(index);i getCurrentItem()

Kliknij tutaj, aby wyświetlić obraz


2

posługiwać się

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


1

Możesz wypróbować metodę 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);
    }
}

Jedynym „minusem” rozwiązania jest użycie MenuItemImpl, które jest „wewnętrzne” dla biblioteki (choć publicznej).


0

JEŚLI TRZEBA DYNAMICZNIE PRZEJŚĆ ARGUMENTY FRAGUMENTÓW, ZRÓB TO

Jest tu wiele (w większości powtarzających się lub nieaktualnych) odpowiedzi, ale żadna z nich nie spełnia bardzo częstej potrzeby: dynamicznego przekazywania różnych argumentów do fragmentu załadowanego do karty .

Nie można dynamicznie przekazywać różnych argumentów do załadowanego fragmentu przy użyciu setSelectedItemId(R.id.second_tab), co kończy się wywołaniem static OnNavigationItemSelectedListener. Aby przezwyciężyć to ograniczenie, zrobiłem to w moim, MainActivityktóry zawiera karty:

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

ArticleFragment.newInstance()Metoda jest realizowane jak zwykle:

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

mam nadzieję, że to pomoże

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

Chodzi raczej o określenie domyślnego fragmentu załadowanego w tym samym czasie z odpowiednią dolną pozycją menu nawigacji. Możesz dołączyć to samo do swoich wywołań zwrotnych OnResume


0

Ta metoda działa dla mnie.

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

Przykład

 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

Odbicie to zły pomysł.

Przejdź do sedna . Istnieje metoda, która dokonuje wyboru, ale również wywołuje wywołanie zwrotne:

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

    }

Używam callOnClick()zamiast performClick(), w ten sposób nie muszę wyłączać dźwięku, który i tak nie działał performClick().
arekolek
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.