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!

Miguel
fuente
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.

Ricardo
fuente
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.

dianakarenms
fuente
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.

Viacheslav
fuente
¿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ú.

Ashwini
fuente
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));
}
Askar Syzdykov
fuente
7

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

Chico
fuente
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

Rumaan Khalander
fuente
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"/>
cpienovi
fuente
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.

Darwind
fuente
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.

Miguel
fuente
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

mhheydarchi
fuente
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);
vhad01
fuente
2

No quieres modificar tu código. Si es así, te recomendé que probaras BottomNavigationViewEx

Solo necesitas reemplazar llamar a un método setCurrentItem(index);y getCurrentItem()

Haga clic aquí para ver la imagen

ittianyu
fuente
2

utilizar

        bottomNavigationView.getMenu().getItem(0).setChecked(true);
Behrad
fuente
1

Le hice un error a Google sobre el hecho de que no hay una forma confiable de seleccionar la página en un BottomNavigationView: https://code.google.com/p/android/issues/detail?id=233697

NavigationView aparentemente tenía un problema similar, que solucionaron agregando un nuevo método setCheckedItem ().

Johan Paul
fuente
1

Puede probar el método performClick:

View view = bottomNavigationView.findViewById(R.id.YOUR_ACTION);
view.performClick();
ismail alaoui
fuente
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).

Alexey
fuente
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
        }
    }

}
Albert Vila Calvo
fuente
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

Zephania Mwando
fuente
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()
    }
code4rox
fuente
-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;

    }
Nikola Despotoski
fuente
Utilizo en callOnClick()lugar de performClick(), de esta manera no necesito deshabilitar el sonido, que de performClick()todos modos no funcionó .
arekolek