Definir o item selecionado no Android BottomNavigationView


123

Estou usando o novo android.support.design.widget.BottomNavigationViewda biblioteca de suporte. Como posso definir a seleção atual do código? Percebi que a seleção é alterada de volta para o primeiro item, após girar a tela. É claro que também ajudaria, se alguém pudesse me dizer, como "salvar" o estado atual do BottomNavigationViewna onPausefunção e como restaurá-lo onResume.

Obrigado!


1
Acabei de dar uma olhada na fonte e tenho a mesma pergunta ... Parece que não há um método para definir o item selecionado e BottomNavigationViewnão faz nenhum salvamento interno de estado. Provavelmente espera que isso seja incluído em uma atualização futura. Duplique (com mais algumas informações) aqui: stackoverflow.com/questions/40236786/…
Tim Malseed

Respostas:


171

A partir da API 25.3.0, foi introduzido o método setSelectedItemId(int id)que permite marcar um item como selecionado como se tivesse sido tocado.

Dos documentos:

Defina o ID do item de menu selecionado. Isso se comporta da mesma forma que tocar em um item.

Exemplo de código:

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

IMPORTANTE

Você DEVE já ter adicionado todos os itens ao menu (caso faça isso programaticamente) e definir o Listener antes de chamar setSelectedItemId (acredito que você deseja que o código em seu listener seja executado quando você chamar este método). Se você chamar setSelectedItemId antes de adicionar os itens de menu e definir o ouvinte, nada acontecerá.


3
Isso NÃO FUNCIONA, apenas atualiza a própria guia, não aciona um evento
setOnNavigationItemSelectedListener

1
Se você não definiu o comportamento no onTabSelectedobviamente, ele não fará nada. Leia os documentos -> Definir o ID do item de menu selecionado. Isso se comporta da mesma forma que tocar em um item. É de desenvolvedores Android
Ricardo

1
Aconselho você a depurar e revisar o que está fazendo de errado. Já implementei três aplicativos BottomNavigationView e nunca tive problemas com esse método.
Ricardo

@Ricardo Quando adicionei isso ao meu fragmento, o aplicativo travou ao entrar naquele fragmento
Subin Babu

1
No meu caso não estava funcionando, pois estava criando o NavigationMenu programaticamente. Durante a construção, o clique não funcionou. Depois que todos os itens adicionados ao menu, chamar setSelectedItemId () funcionou.
Pedro Romão

53

Para clicar programaticamente no item BottomNavigationBar que você precisa usar:

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

Isso organiza todos os itens com seus rótulos corretamente.


4
Este é um hack relacionado ao problema. Você tem métodos da própria biblioteca que permitem executar o que você precisa. Se você não pode, é porque você está fazendo errado
Ricardo

47

Para aqueles que ainda usam SupportLibrary <25.3.0

Não tenho certeza se esta é uma resposta completa para esta pergunta, mas meu problema era muito semelhante - eu tive que processar o backpressionamento do botão e trazer o usuário para a guia anterior onde ele estava. Então, talvez minha solução seja útil para alguém:

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

Por favor, tenha em mente que se o usuário pressionar outra guia de navegação BottomNavigationViewnão limpará o item selecionado atualmente, então você precisa chamar este método onNavigationItemSelectedapós o processamento da ação de navegação:

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

Em relação ao salvamento do estado da instância, acho que você poderia brincar com a mesma action idvisualização de navegação e encontrar uma solução adequada.


Não Menu menu = bottomNavigationView.getMenu();retorna uma cópia do menu, portanto o bottomNavigationViewobjeto não é afetado?
最 白 目

1
@dan De acordo com a fonte, ele retorna o atributo de classe mMenu, não a cópia android.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
Você não precisa desmarcar outros itens, pelo menos em 26.1.0 / 27.1.0
Jemshit Iskenderov

não desmarque, ele irá destacá-los
djdance

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

onde action_item1está o ID do item de 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

Agora é possível desde a versão 25.3.0 chamar setSelectedItemId()\ o /


5

Adicione android:enabled="true"aos itens BottomNavigationMenu.

E então defina bottomNavigationView.setOnNavigationItemSelectedListener(mListener)e defina como selecionado fazendobottomNavigationView.selectedItemId = R.id.your_menu_id


3

Isso provavelmente será adicionado nas próximas atualizações. Mas, enquanto isso, para conseguir isso, você pode usar a reflexão.

Crie uma visualização personalizada estendendo-se de BottomNavigationView e acesse alguns de seus campos.

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 então use-o em seu arquivo de 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
A reflexão é uma má ideia!
Filipe Brito

performClick é melhor do que reflexão, imho, neste caso.
Lassi Kinnunen

3

Apenas adicionando outra maneira de realizar uma seleção programaticamente - esta é provavelmente a intenção inicial ou talvez tenha sido adicionada posteriormente.

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

O performIdentifierActionleva um Menuid de item e uma bandeira.

Veja a documentação para mais informações.


Isso parece executar a ação pertencente ao item de menu, mas não faz com que o item de menu apareça selecionado. Achei que o último era o que o OP queria, mas não tenho certeza.
LarsH

3

Parece estar corrigido no SupportLibrary 25.1.0 :) Edit: Parece estar corrigido, que o estado da seleção é salvo, ao girar a tela.


1
Acabei de atualizar para 25.1.1. O problema ainda está lá.
Xi Wei

3

Acima da API 25 você pode usar setSelectedItemId (menu_item_id), mas na API 25 você deve fazer diferente, Menu do usuário para obter o manipulador e então setChecked para item específico verificado


3

Use isto para definir o item de menu de navegação inferior selecionado por id de menu

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

2

I você não deseja modificar seu código. Nesse caso, recomendo que você experimente BottomNavigationViewEx

Você só precisa substituir, chamar um método setCurrentItem(index);e getCurrentItem()

Clique aqui para ver a imagem


2

usar

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


1

Você pode tentar o método 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);
    }
}

O único 'menos' da solução é usar MenuItemImpl, que é 'interno' à biblioteca (embora público).


0

SE VOCÊ PRECISA PASSAR DINAMICAMENTE ARGUMENTOS DE FRAGMENTO FAÇA ISTO

Há muitas respostas (a maioria repetidas ou desatualizadas) aqui, mas nenhuma delas lida com uma necessidade muito comum: passar dinamicamente diferentes argumentos para o Fragment carregado em uma guia .

Você não pode passar dinamicamente argumentos diferentes para o Fragment carregado usando setSelectedItemId(R.id.second_tab), o que acaba chamando o estático OnNavigationItemSelectedListener. Para superar essa limitação, acabei fazendo isso no meu MainActivityque contém as guias:

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

O ArticleFragment.newInstance()método é implementado como de costume:

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

Eu espero que isso ajude

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

É mais para determinar o fragmento padrão carregado ao mesmo tempo com o item de menu de navegação inferior correspondente. Você pode incluir o mesmo em seus retornos de chamada OnResume


0

Este método funciona para mim.

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

Exemplo

 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

A reflexão é má ideia.

Vá para esta essência . Existe um método que realiza a seleção, mas também invoca o retorno de chamada:

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

    }

Eu uso em callOnClick()vez de performClick(), desta forma, não preciso desativar o som, que não funcionou de performClick()qualquer maneira.
arekolek
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.