Establecer el elemento seleccionado en Android BottomNavigationView


123

Estoy usando el nuevo android.support.design.widget.BottomNavigationViewde la biblioteca de soporte. ¿Cómo puedo configurar la selección actual del código? Me di cuenta de que la selección se cambia de nuevo al primer elemento, después de girar la pantalla. Por supuesto, también ayudaría, si alguien pudiera decirme, cómo "guardar" el estado actual del BottomNavigationViewen la onPausefunción y cómo restaurarlo onResume.

¡Gracias!


1
Acabo de echar un vistazo a la fuente y tengo la misma pregunta. No parece que haya un método para configurar el elemento seleccionado, y BottomNavigationViewno guarda ningún estado interno. Probablemente espere que esto se incluya en una actualización futura. Duplicar (con más información) aquí: stackoverflow.com/questions/40236786/…
Tim Malseed

Respuestas:


171

Desde la API 25.3.0 se introdujo el método setSelectedItemId(int id)que le permite marcar un elemento como seleccionado como si se hubiera tocado.

De los documentos:

Configure el ID del elemento de menú seleccionado. Esto se comporta de la misma manera que tocar un elemento.

Ejemplo de código:

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

IMPORTANTE

Usted DEBE ya ha añadido todos los artículos al menú (en caso de que hace esto mediante programación) y establecer el oyente antes de llamar setSelectedItemId (creo que desea que el código en su oyente a ejecutar cuando se llama a este método). Si llama a setSelectedItemId antes de agregar los elementos del menú y configurar el oyente, no sucederá nada.


3
Esto NO FUNCIONA, solo actualiza la pestaña en sí, no activa un evento
setOnNavigationItemSelectedListener

1
Si no ha definido el comportamiento en el, onTabSelectedobviamente, no hará nada. Lea los documentos -> Establezca el ID del elemento de menú seleccionado. Esto se comporta de la misma manera que tocar un elemento. Es de desarrolladores de Android
Ricardo

1
Le recomiendo que depure y revise lo que está haciendo mal. Ya he implementado tres aplicaciones BottomNavigationView y nunca he tenido problemas con este método.
Ricardo

@Ricardo Cuando agregué esto a mi fragmento, la aplicación se
bloqueó

1
En mi caso no estaba funcionando, porque estaba creando navigationMenu programáticamente. Durante la construcción, el clic no funcionó. Después de agregar todos los elementos al menú, la llamada a setSelectedItemId () funcionó.
Pedro Romão

53

Para hacer clic mediante programación en el elemento BottomNavigationBar, necesita usar:

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

Esto organiza todos los elementos con sus etiquetas correctamente.


4
Este es un truco sobre el problema. Tiene métodos de la propia biblioteca que le permitirán realizar lo que necesita. Si no puede, es porque lo está haciendo mal
Ricardo

47

Para aquellos que todavía usan SupportLibrary <25.3.0

No estoy seguro de si esta es una respuesta completa a esta pregunta, pero mi problema era muy similar: tuve que procesar la backpresión del botón y llevar al usuario a la pestaña anterior donde estaba. Entonces, tal vez mi solución sea útil para alguien:

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, tenga en cuenta que si el usuario presiona otra pestaña de navegación BottomNavigationView, no se borrará el elemento seleccionado actualmente, por lo que debe llamar a este método onNavigationItemSelecteddespués del procesamiento de la acción de navegación:

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

Con respecto al ahorro del estado de la instancia, creo que podría jugar con la misma action idvista de navegación y encontrar una solución adecuada.


¿No Menu menu = bottomNavigationView.getMenu();devuelve una copia del menú, por lo que el bottomNavigationViewobjeto no se ve afectado?
最 白 目

1
@dan Según la fuente, devuelve el atributo de clase mMenu, no la copia android.googlesource.com/platform/frameworks/support/+/master/…
Viacheslav

6
No es necesario que desmarque otros elementos, al menos el 26.1.0 / 27.1.0
Jemshit Iskenderov

no desmarque, los resaltará
djdance

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

donde action_item1es el ID del elemento del menú.


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

Ahora es posible desde la versión 25.3.0 llamar a setSelectedItemId()\ o /


5

Agregar android:enabled="true"a elementos de BottomNavigationMenu.

Y luego configúrelo bottomNavigationView.setOnNavigationItemSelectedListener(mListener)y configúrelo como seleccionado haciendobottomNavigationView.selectedItemId = R.id.your_menu_id


3

Esto probablemente se agregará en próximas actualizaciones. Pero mientras tanto, para lograr esto puedes usar la reflexión.

Cree una vista personalizada que se extienda desde BottomNavigationView y acceda a algunos de sus 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();
        }
    }

}

Y luego úselo en su archivo de diseño 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 reflexión es una mala idea!
Filipe Brito

performClick es mejor que la reflexión, en mi humilde opinión, en este caso.
Lassi Kinnunen

3

Simplemente agregando otra forma de realizar una selección programáticamente: esta es probablemente la intención en primer lugar o tal vez se agregó más adelante.

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

La performIdentifierActiontoma un MenuID de artículo y una bandera.

Consulte la documentación para obtener más información.


Esto parece realizar la acción que pertenece al elemento del menú, pero no hace que el elemento del menú aparezca seleccionado. Pensé que esto último era lo que quería el OP, pero no estoy seguro.
LarsH

3

Parece estar arreglado en SupportLibrary 25.1.0 :) Editar: Parece arreglado, que el estado de la selección se guarda, al rotar la pantalla.


1
Acabo de actualizar a 25.1.1. El problema sigue ahí.
Xi Wei

3

Por encima de API 25 puede usar setSelectedItemId (menu_item_id) pero bajo API 25 debe hacerlo de manera diferente, el menú de usuario para obtener el control y luego establecerChecked en un elemento específico marcado


3

Use esto para configurar el elemento de menú de navegación inferior seleccionado por ID de menú

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


2

utilizar

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


1

Puede probar el 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);
    }
}

El único "inconveniente" de la solución es el uso MenuItemImpl, que es "interno" a la biblioteca (aunque público).


0

SI NECESITA APROBAR DINÁMICAMENTE ARGUMENTOS DE FRAGMENTO HAGA ESTO

Hay muchas respuestas (en su mayoría repetidas o desactualizadas) aquí, pero ninguna de ellas maneja una necesidad muy común: pasar dinámicamente diferentes argumentos al Fragmento cargado en una pestaña .

No puede pasar dinámicamente diferentes argumentos al Fragmento cargado usando setSelectedItemId(R.id.second_tab), que termina llamando a static OnNavigationItemSelectedListener. Para superar esta limitación, terminé haciendo esto en mi MainActivityque contiene las pestañas:

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

El ArticleFragment.newInstance()método se implementa como de costumbre:

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

espero que esto ayude

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

Se trata más de determinar el fragmento predeterminado cargado al mismo tiempo con el elemento del menú de navegación inferior correspondiente. Puede incluir lo mismo en sus devoluciones de llamada OnResume


0

Este método funciona para mí.

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

Ejemplo

 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 reflexión es mala idea.

Dirígete a esta esencia . Hay un método que realiza la selección pero también invoca la devolución de llamada:

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

    }

Utilizo en callOnClick()lugar de performClick(), de esta manera no necesito deshabilitar el sonido, que de performClick()todos modos no funcionó .
arekolek
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.