How to generate a Dynamic Menu with NavigationView that contains a notification counter?

I'm developing an Android app that contains a menu that is dynamically generated, so the menu generates it programmatically:

private void cargarAvisosMenu() 
{
    // Menu
    final Menu menu = this.mNavigationView.getMenu();


    // Carga de datos
    CentroSelection selection = new CentroSelection();
    CentroCursor cur = selection.query(getApplicationContext().getContentResolver());

    // Recorro el cursor de centros
    while (cur.moveToNext()) {
        boolean avisosTipoA = false;
        boolean avisosTipoB = false;

        Centro c = Centro.getFromCursor(cur);
        if (c.getIsTipoAVisible() && c.getComponenteTipoA().getControlesTipoAPendientes() > 0) {
            avisosTipoA = true;
        }
        if (c.getIsTipoBVisible() && c.getComponenteTipoB().getControlesTipoBPendientes() > 0){
            avisosTipoB = true;
        }

        if (avisosTipoA || avisosTipoB) {
            // Añado la sección y los items.
            final SubMenu subMenu = menu.addSubMenu(c.getNombre());
            if (avisosTipoA) {
                subMenu.add("TipoA").setIcon(R.drawable.ic_tipoA);
            }
            if (avisosTipoB) {
                subMenu.add("TipoB").setIcon(R.drawable.ic_tipoB);
            }
        }
    }
}

My intention is to add a counter that indicates the number of notifications (gmail-style).

According to the documentation this is possible, either through an xml menu, using the attribute app:actionLayout, or using the function MenuItemCompat.setActionView(). Since the menu I am developing is dynamic, I have opted for the second option, so that the code would look like this:

private void cargarAvisosMenu() 
{
    // Menu
    final Menu menu = this.mNavigationView.getMenu();


    // Carga de datos
    CentroSelection selection = new CentroSelection();
    CentroCursor cur = selection.query(getApplicationContext().getContentResolver());

    // Recorro el cursor de centros
    while (cur.moveToNext()) {
        boolean avisosTipoA = false;
        boolean avisosTipoB = false;

        Centro c = Centro.getFromCursor(cur);
        if (c.getIsTipoAVisible() && c.getComponenteTipoA().getControlesTipoAPendientes() > 0) {
            avisosTipoA = true;
        }
        if (c.getIsTipoBVisible() && c.getComponenteTipoB().getControlesTipoBPendientes() > 0){
            avisosTipoB = true;
        }

        if (avisosTipoA || avisosTipoB) {
            // Añado la sección y los items.
            final SubMenu subMenu = menu.addSubMenu(c.getNombre());
            if (avisosTipoA) {
                subMenu.add("TipoA").setIcon(R.drawable.ic_tipoA);
            }
            if (avisosTipoB) {
                // subMenu.add("TipoB").setIcon(R.drawable.ic_tipoB);
                int itemId = subMenu.add("TipoB").getItemId();
                View menuItem = MenuItemCompat.setActionView(subMenu.findItem(itemId), R.layout.menu_notificaciones).getActionView();
                ((ImageView) menuItem.findViewById(R.id.ivMenuNotificaciones)).setBackground(getDrawable(R.drawable.ic_barcode));
                ((TextView) menuItem.findViewById(R.id.tvMenuNotificaciones)).setText("TipoB");
                ((TextView) menuItem.findViewById(R.id.tvContadorNotificaciones)).setText("100");
            }
        }
    }
}

And the file " menu_notifications.xml":

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:orientation="horizontal">
    <ImageView
        android:id="@+id/ivMenuNotificaciones"
        android:layout_width="64dp"
        android:layout_height="match_parent" />
    <TextView
        android:id="@+id/tvMenuNotificaciones"
        android:layout_width="wrap_content"
        android:layout_height="match_parent" />
    <TextView
        android:id="@+id/tvContadorNotificaciones"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="right"/>
</LinearLayout>

However the menu does not load the "menu_notifications"view.

 16
Author: hecnabae, 2015-12-10

2 answers

I have finally fixed the problem by creating the menu manually, by entering a ListView inside the NavigationView component.

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/toolbar_actionbar"
    android:fitsSystemWindows="true">

    <!-- Contenido principal -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true" />
    </LinearLayout>

    <!-- Menú Deslizable -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nvMenu"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"
        android:fitsSystemWindows="true">

        <!-- Menú Deslizable basado en ListView -->
        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ListView
                android:id="@+id/left_drawer"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/white"
                android:cacheColorHint="@android:color/transparent"
                android:choiceMode="singleChoice"
                android:divider="@android:color/transparent"
                android:dividerHeight="0dp" />
        </FrameLayout>
    </android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>

If I'm not mistaken, this is the mode that was used in the past for menu development. I am aware that it is not the solution I was looking for, however I cannot find the way to add the counter by code directly to the NavigationView control.

 6
Author: hecnabae, 2015-12-14 09:58:59

According to setActionView the action will be replaced when this item is displayed as an action within the parent.

You need to indicate this action. You can do this using setShowAsAction. This describes when this item can be displayed.

 5
Author: raukodraug, 2015-12-12 15:04:20