Встановіть вибраний елемент у Android BottomNavigationView


123

Я використовую нове android.support.design.widget.BottomNavigationViewз бібліотеки підтримки. Як я можу встановити поточний вибір з коду? Я зрозумів, що після обертання екрана вибір змінюється на перший елемент. Звичайно , це також допомогло б, якби хто - то може сказати мені, як «врятувати» нинішнього стану BottomNavigationViewв onPauseфункції і як відновити його в onResume.

Дякую!


1
Я щойно подивився на джерело, і у мене те саме питання. Це не схоже на те, що існує метод встановлення вибраного елемента і BottomNavigationViewне робить внутрішнього збереження стану. Можливо, очікуємо, що це буде включено у майбутнє оновлення. Дублікат (з великою кількістю деякою інформацією) тут: stackoverflow.com/questions/40236786 / ...
Tim Malseed

Відповіді:


171

З API 25.3.0 було введено метод, setSelectedItemId(int id)який дозволяє позначити елемент як обраний, як ніби він був натисканий.

З документів:

Встановіть вибраний ідентифікатор елемента меню. Це поводиться так само, як натискання на предмет.

Приклад коду:

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

ВАЖЛИВО

Ви ОБОВ'ЯЗКОВО вже додали всі елементи до меню (у випадку, якщо ви це зробите програмно) та встановите Слухач перед тим, як викликати setSelectedItemId (я вважаю, що ви хочете, щоб код у вашому слухачі запускався, коли ви закликаєте цей метод). Якщо ви зателефонуєте на setSelectedItemId перед тим, як додати елементи меню та встановити слухача, нічого не відбудеться.


3
Це НЕ ПРАЦЮЄ, він лише оновлює вкладку, вона не викликає події
setOnNavigationItemSelectedListener

1
Якщо ви не визначилися з поведінкою, onTabSelectedочевидно, вона нічого не зробить. Прочитайте документи -> Встановіть вибраний ідентифікатор елемента меню. Це поводиться так само, як натискання на предмет. Це від розробників Android
Рікардо

1
Я настійно раджу налагодити та переглянути те, що ви робите неправильно. Я вже реалізував три програми BottomNavigationView і жодного разу не мав проблем із цим методом.
Рікардо

@Ricardo Коли я додаю це до свого фрагмента, додаток зависло під час вступу до цього фрагменту
Subin Babu

1
У моєму випадку не працювало, тому що я створював навігаційне меню програмно. Під час будівництва клацання не працювало. Після того, як всі елементи додані в меню, виклик setSelectedItemId () спрацював.
Педро Ромао

53

Щоб програматично натиснути на пункт BottomNavigationBar, вам потрібно скористатися:

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

Це правильно розташовує всі елементи з їх мітками.


4
Це злом щодо проблеми. У вас є методи з самої бібліотеки, які дозволять вам виконувати те, що вам потрібно. Якщо ви не можете, то це тому, що ви робите неправильно
Рікардо

47

Для тих, хто все ще використовує SupportLibrary <25.3.0

Я не впевнений, чи є повною відповіддю на це питання, але моя проблема була дуже схожа - мені довелося обробити backнатискання кнопок і привести користувача до попередньої вкладки, де він був. Тож, можливо, моє рішення буде комусь корисним:

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

Будь ласка, майте на увазі, що якщо користувач натисне іншу вкладку навігації BottomNavigationView, не очистить поточно вибраний елемент, тож вам потрібно зателефонувати цей метод onNavigationItemSelectedпісля обробки дії навігації:

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

Щодо збереження стану екземпляра, я думаю, ви могли б пограти з таким самим action idвиглядом навігації і знайти відповідне рішення.


Не Menu menu = bottomNavigationView.getMenu();повертає копію меню, тому bottomNavigationViewоб'єкт не впливає?
最 白 目

1
@dan За джерелом, він повертає атрибут класу mMenu, а не копію android.googlesource.com/platform/frameworks/support/+/master/…
В'ячеслав

6
Вам не потрібно знімати прапорці, принаймні 26.1.0 / 27.1.0
Jemshit Iskenderov

не зніміть прапорець, це виділить їх
djdance

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

де action_item1ідентифікатор елемента меню.


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

Додати android:enabled="true"до BottomNavigationMenu items.

А потім встановіть bottomNavigationView.setOnNavigationItemSelectedListener(mListener)і встановіть його, як вибраноbottomNavigationView.selectedItemId = R.id.your_menu_id


3

Це, ймовірно, буде додано в наступних оновленнях. Але тим часом для цього можна скористатися рефлексією.

Створіть спеціальний вигляд, що поширюється на BottomNavigationView, і відкрийте доступ до деяких його полів.

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

}

А потім використовуйте його у своєму файлі макета 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
Рефлексія - погана ідея!
Філіпе Бріто

виконуватиClick краще, ніж рефлексія, імхо, в цьому випадку.
Лассі Кіннунен

3

Просто додавання ще одного способу проведення вибірки програмно - це, мабуть, те, що було наміром в першу чергу, або, можливо, це було додано пізніше.

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

performIdentifierActionПриймає Menuідентифікатор товару і прапор.

Докладнішу інформацію див. У документації .


Здається, це виконує дію, що належить до пункту меню, але не викликає вибору пункту меню. Я подумав, що останнє - те, чого хотів ОП, але я не позитивний.
LarsH

3

Здається, виправлено в SupportLibrary 25.1.0 :) Редагувати: Здається, виправлено, що стан вибору зберігається під час обертання екрана.


1
Я щойно оновив до 25.1.1. Проблема все ще існує.
Xi Wei

3

Вище API 25 ви можете використовувати setSelectedItemId (menu_item_id), але під API 25 потрібно зробити інакше, Меню користувача, щоб отримати обробку, а потім встановити Перевірений на Перевірений конкретний елемент


3

Використовуйте це, щоб встановити вибраний пункт меню навігації за ідентифікатором меню

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

2

Я не хочу змінювати свій код. Якщо так, я рекомендую вам спробувати BottomNavigationViewEx

Вам просто потрібно замінити метод виклику setCurrentItem(index);і getCurrentItem()

Натисніть тут, щоб переглянути зображення




1

Ви можете спробувати метод 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);
    }
}

Єдиний «мінус» рішення - MenuItemImplце «внутрішній» для бібліотеки (хоча і загальнодоступної).


0

ЯКЩО МОЖЕТЕ ДИНАМІКАЛЬНО ПАРАЖИТИ АРГУМЕНТИ ФРАГМЕНТУ, Зробіть це

Тут є безліч (в основному повторних або застарілих) відповідей, але жоден з них не справляється з дуже загальною потребою: динамічно передаючи різні аргументи до фрагменту, завантаженого на вкладку .

Ви не можете динамічно передавати різні аргументи до завантаженого фрагмента, використовуючи setSelectedItemId(R.id.second_tab), що в кінцевому підсумку викликає статику OnNavigationItemSelectedListener. Для подолання цього обмеження я закінчила це робити у своєму, MainActivityщо містить вкладки:

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()Метод реалізований як зазвичай:

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

Я сподіваюся, що це допомагає

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

Це більше визначення фрагмента за замовчуванням, завантаженого одночасно з відповідним пунктом меню нижнього навігаційного меню. Ви можете включити те саме у свої зворотні дзвінки OnResume


0

Цей метод працює для мене.

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

Приклад

 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

Рефлексія - це погана ідея.

Перейдіть до цієї суті . Існує метод, який виконує вибір, але також викликає зворотний виклик:

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

    }

Я використовую callOnClick()замість performClick()цього, мені не потрібно відключати звук, який все performClick()одно не працював .
arekolek
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.