Android BottomNavigationView Una pestaña con diferentes colores no seleccionados / seleccionados

8

Estoy intentando hacer coincidir un diseño como este ...

ingrese la descripción de la imagen aquí

Observe que el "tinte de color de la pestaña seleccionada" es azul, pero el icono de la pestaña central siempre debe ser el círculo verde con el reloj blanco en el medio.

He intentado varias cosas. Primero intente hacerlo mediante programación usando un recurso XML de lista de capas que tenga el círculo verde y el recurso PNG del reloj, que no funcionó en absoluto. Luego acabo de pedirle al diseñador que me dé el ícono completo (reloj y círculo verde), pero ahora me encuentro con este problema.

(Sin seleccionar)

ingrese la descripción de la imagen aquí

(Seleccionado)

ingrese la descripción de la imagen aquí

No logro encontrar los términos correctos para buscar en Google para solucionar esto.

Al final, necesito que el color de la pestaña seleccionada sea azul, pero necesito que el ícono de la pestaña central sea siempre el ícono real sin coloración adicional (esencialmente necesita verse exactamente como el .png).

PD: Estoy usando Xamarin.Forms, FreshMvvm y FreshTabbedFONavigationContainer. Sin embargo, a través del Renderer, tengo acceso directo a BottomNavigationView y a todos los demás componentes nativos de Android. Por lo tanto, la solución no tiene que ser una solución de Xamarin. Una solución java / kotlin también funcionaría y puedo convertirla a Xamarin.

======================

EDITADO:

======================

Así que he llegado mucho más lejos usando el código de Andrés Castro a continuación, pero todavía tengo el mismo problema que antes. Usando el código de Andrés a continuación, volví a usar FontAwesome para los íconos (que funciona muy bien), pero al hacerlo significa que necesitaba usar unLayerDrawable para crear el ícono de la pestaña central del círculo / ícono.

Así que esto es lo que tengo hasta ahora ...

Icono de centro no seleccionado

ingrese la descripción de la imagen aquí

Icono del centro seleccionado

ingrese la descripción de la imagen aquí

Como puede ver, el ícono central sigue siendo gris cuando no está seleccionado y azul cuando está seleccionado (los colores apropiados seleccionados / no seleccionados de los otros 4 íconos).

Aquí está el código que tengo hasta ahora relacionado con el icono del centro.

UpdateTabbedIcons

private void UpdateTabbedIcons()
{
    for (var i = 0; i < Element.Children.Count; i++) {
        var tab = _bottomNavigationView.Menu.GetItem(i);

        var element = Element.Children[i];
        if (element is NavigationPage navigationPage) {
            //if the child page is a navigation page get its root page
            element = navigationPage.RootPage;
        }

        UpdateTabIcon(tab, element);
    }
}

UpdateTabIcon

public void UpdateTabIcon(IMenuItem menuItem, Page page)
{
    var icon = page?.Icon;
    if (icon == null) return;

    var drawable = new IconDrawable(Context, icon, "fa-regular-pro-400.ttf");

    var element = Element.CurrentPage;
    if (element is NavigationPage navigationPage) {
        //if the child page is a navigation page get its root page
        element = navigationPage.RootPage;
    }

    if (page is DoNowTabPage) { //Page for center icon
        drawable.Color(Helpers.Resources.White.ToAndroid());
        var finalDrawable = GetCombinedDrawable(drawable);
        menuItem.SetIcon(finalDrawable);
        return;
    } else if (element == page) {
        drawable.Color(BarSelectedItemColor.ToAndroid());
    } else {
        drawable.Color(BarItemColor.ToAndroid());
    }

    menuItem.SetIcon(drawable);
}

GetCombinedDrawable

private Drawable GetCombinedDrawable(IconDrawable iconDrawable)
{
    var displayMetrics = Resources.DisplayMetrics;

    GradientDrawable circleDrawable = new GradientDrawable();
    circleDrawable.SetColor(Helpers.Resources.Green.ToAndroid());
    circleDrawable.SetShape(ShapeType.Oval);
    circleDrawable.SetSize((int)TypedValue.ApplyDimension(ComplexUnitType.Dip, 500, displayMetrics), (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, 500, displayMetrics));
    circleDrawable.Alpha = 1;

    var inset = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, 140, displayMetrics);
    var bottomInset = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip, 40, displayMetrics);
    LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] { circleDrawable, iconDrawable });
    finalDrawable.SetLayerHeight(1, iconDrawable.IntrinsicHeight);
    finalDrawable.SetLayerWidth(1, iconDrawable.IntrinsicWidth);
    finalDrawable.SetLayerInset(1, inset, inset, inset, inset + bottomInset);
    finalDrawable.SetLayerInsetBottom(0, bottomInset);
    finalDrawable.ClearColorFilter();

    return finalDrawable;
}

Como puedes ver en el GradientDrawable que estoy creando para el círculo, estoy configurando su color a mi color Verde (tengo una clase personalizada llamada Recursos ... esa no es la Resourcesclase de Android ).

Entonces ahí es donde estoy atrapado. Estoy configurando el círculo dibujable para que tenga un color verde, pero una vez en la Vista de navegación inferior, su color siempre coincide con los colores no seleccionados / seleccionados de los otros iconos.

Con la esperanza de superar este último problema. Gracias por cualquier ayuda.

Ryan Alford
fuente
Una solución simple es usar un elemento en blanco en el medio y colocar una vista de imagen separada sobre BotViewNavigationView con la imagen verde.
Shivam Yadav
1
¿Por qué no cambia programáticamente el tinte / color seleccionado según el índice de la vista de navegación inferior?
Saamer
1
@Saamer Tu comentario parecía tan simple jaja. ¿Puedes publicarlo como respuesta para que pueda otorgar la recompensa? Este es el código que usé ..._bottomNavigationView.ItemIconTintList = null;
Ryan Alford
¿Funcionó para ti? BottomNavigationView es dolorosamente más difícil que su implementación de iOS. Investigué un poco para ver si lo que preguntabas era posible, y luego, cuando lo vi en Android, comencé a pensar en formas de hacerlo realidad.
Saamer
1
Sí, funcionó. No estoy seguro, pero parece que mi cambio restablecería TODOS los tonos de íconos de elementos, pero no lo hace. Todas las otras pestañas siguen funcionando porque las configuro manualmente cada vez, y el centro es verde cuando está seleccionado y no seleccionado.
Ryan Alford

Respuestas:

1

BottomNavigationView es dolorosamente más difícil que su implementación de iOS. Investigué un poco para ver si lo que preguntabas era posible, y luego, cuando lo vi en Android, comencé a pensar en formas de hacerlo realidad.

Para implementar su último desafío, deberá cambiar mediante programación el tono / color seleccionado cada vez, según el índice de la vista de navegación inferior.

Saamer
fuente
2

Como tiene acceso a la vista de navegación inferior, puede "volver a dibujar" la barra de herramientas inferior cada vez que cambie de página. Hicimos algo similar y no notamos ningún problema de rendimiento.

Primero querrá monitorear los cambios de página suscribiéndose al cambio de página dentro OnElementChanged

Element.CurrentPageChanged += ElementOnCurrentPageChanged;

Luego, dentro ElementOnCurrentPageChangedpuede atravesar cada página y obtener el elemento de menú para esa página

private void UpdateTabbedIcons()
{
    for (var i = 0; i < Element.Children.Count; i++)
    {
        var tab = _bottomNavigationView.Menu.GetItem(i);

        var element = Element.Children[i];
        if (element is NavigationPage navigationPage)
        {
            //if the child page is a navigation page get its root page
            element = navigationPage.RootPage;
        }

        UpdateTabIcon(tab, element);
    }
}

En nuestro caso, utilizamos iconos de fuente, así que dibujamos los iconos y configuramos los colores cada vez.

public void UpdateTabIcon(IMenuItem menuItem, Page page)
{
    var icon = page?.Icon?.File;
    if (icon == null) return;

    var drawable = new IconDrawable(Context, "FontAwesome.ttf", icon).SizeDp(20);

    var element = Element.CurrentPage;
    if (element is NavigationPage navigationPage)
    {
        //if the child page is a navigation page get its root page
        element = navigationPage.RootPage;
    }

    if (element == page)
    {
        drawable.Color(BarSelectedItemColor.ToAndroid());
    }
    else
    {
        drawable.Color(BarItemColor.ToAndroid());
    }

    menuItem.SetIcon(drawable);
}

También tuvimos que anular OnAttachedToWindow para asegurarnos de que los iconos se volvieran a dibujar al volver a la aplicación desde diferentes estados.

protected override void OnAttachedToWindow()
{
    UpdateTabbedIcons();

    base.OnAttachedToWindow();
}

Tendrá que modificar esto para que se ajuste a su caso de uso, pero creo que este método debería lograr lo que está buscando.

Andres Castro
fuente
Increíble. Lo intentaré esta noche. Si tiene un código de trabajo que utiliza FontAwesome (u otras fuentes de gráficos vectoriales) para BottomNavigationView, realmente debería hacer una publicación de blog en alguna parte. No pude encontrar ningún ejemplo y mis intentos nunca mostrarían el ícono.
Ryan Alford el
1

puede usar imágenes SVG y hacer su propio atributo de color y hacer un selector dibujable para el icono central, así como otro icono de vista de navegación inferior, como se muestra a continuación:

en el archivo colors.xml

    <color name="color_bottom_selected">#44C8F5</color>
 <color name="color_bottom_unselected">#c0c0c0</color>

en el archivo attrs.xml

   <attr name="color_bottom_unselected" format="color" />
    <attr name="color_bottom_selected" format="color" />

en archivo de imagen SVG

reemplace el valor de color codificado con su atributo

android:fillColor="#fff" to android:fillColor="?attr/color_bottom_unselected"

y no olvides hacer que el selector sea dibujable

asad mahmood
fuente
1

Intente utilizar el modo de tinte DST , que simplemente ignorará el tinte.

Agregue esta línea a su método GetCombinedDrawable()

circleDrawable.setTintMode(PorterDuff.Mode.DST);

Si esto no funciona, intente esto:

finalDrawable.ClearColorFilter();
finalDrawable.setTintMode(PorterDuff.Mode.DST);
Oleksii K.
fuente
En realidad probé todas las opciones de PorterDuff.Mode, pero ninguna funcionó.
Ryan Alford el
0

Te puedo ayudar con esto:

public class HomeMenuTabLayout extends TabLayout {

public static final int HOME_MENU_TABLAYOUT_COUNT = 5;

public static final int HOME_MENU_LIVE_INDEX = 0;
public static final int HOME_MENU_TEAM_INDEX = 1;
public static final int HOME_MENU_ADS_INDEX = 2;
public static final int HOME_MENU_WALLET_INDEX = 3;
public static final int HOME_MENU_POST_INDEX = 4;

public int selectedIndex = 0;
private TextView unread;

public HomeMenuTabLayout(Context context) {
    super(context);
    initItems(context);
}

public HomeMenuTabLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    initItems(context);
}

public HomeMenuTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initItems(context);
}

public void initItems(Context context) {
    for (int i = 0; i < HOME_MENU_TABLAYOUT_COUNT; i++) {
        addTab(newTab());
    }

    for (int i = 0; i < HOME_MENU_TABLAYOUT_COUNT; i++) {
        TabLayout.Tab tab = this.getTabAt(i);
        tab.setCustomView(getTabView(context, i, false));
    }
}

public void setTagIndex(Context context, int index) {
    getTabView(context, selectedIndex, false);
    selectedIndex = index;
    getTabView(context, selectedIndex, true);
}

private View getTabView(Context context, int index, boolean selected) {
    View v = null;
    TabLayout.Tab tab = this.getTabAt(index);
    if (tab != null) {
        v = tab.getCustomView();
        if (v == null) {
            v = LayoutInflater.from(context).inflate(R.layout.activity_main_tab_layout, null);
        }
    }

    if (v == null) {
        return null;
    }

    ImageView img = v.findViewById(R.id.tablayout_image);
    int color = 0;
    int color2 = 0;
    if (selected) {
        color = getResources().getColor(R.color.corn_flower_blue);
        color2 = getResources().getColor(R.color.dark_sky_blue_three);
        TmlyUtils.displayViewWithZoom(img);
    } else {
        color = getResources().getColor(R.color.battleship_grey_dark);
        color2 = getResources().getColor(R.color.battleship_grey_dark);
    }

    switch (index) {
        case HOME_MENU_ADS_INDEX:
            Bitmap offers = StyleKit.imageOfTabbarSearchActive(color, color2);
            img.setImageBitmap(offers);
            break;
        case HOME_MENU_LIVE_INDEX:

            Bitmap live = StyleKit.imageOfTabbarHomeActive(color, color2);
            img.setImageBitmap(live);
            unread = v.findViewById(R.id.tablayout_unread);
            break;
        case HOME_MENU_TEAM_INDEX:
            Bitmap team = StyleKit.imageOfTabbarSocialActive(color, color2);
            img.setImageBitmap(team);
            break;
        case HOME_MENU_WALLET_INDEX:
            Bitmap wallet = StyleKit.imageOfTabbarCaddyActive(color, color2);
            img.setImageBitmap(wallet);
            break;
        case HOME_MENU_POST_INDEX:
            Bitmap chat = StyleKit.imageOfTabbarPlusActive(getResources().getColor(R.color.white), getResources().getColor(R.color.white));
            img.setImageBitmap(chat);
            View cirle = v.findViewById(R.id.tablayout_circle);
            cirle.setVisibility(View.VISIBLE);
            break;
        default:
            break;
    }
    return v;
}
}

Este es un TabLayout personalizado que puede ver que estoy extiende la clase TabLayout, cuando se crea el TabLayout, llamo al método initItems que agregaTab y establece una vista personalizada para él.

GetTabView devuelve la vista inflada como puede ver con esto

 LayoutInflater.from(context).inflate(R.layout.activity_main_tab_layout, null);

Si lo necesitas

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="@dimen/tab_bar_height">

    <ImageView
        android:id="@+id/tablayout_circle"
        android:layout_width="@dimen/tab_bar_height"
        android:layout_height="@dimen/tab_bar_height"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="@drawable/circle_blue_gradient"
        android:visibility="gone"
        tools:visibility="visible" />

    <ImageView
        android:id="@+id/tablayout_image"
        android:layout_width="@dimen/tab_bar_icon_favorite_height"
        android:layout_height="@dimen/tab_bar_icon_favorite_height"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />

</RelativeLayout>

después de inflar la vista, puede obtener su elemento de vista con

  ImageView img = v.findViewById(R.id.tablayout_image);

Puede verificar si la vista está seleccionada con el valor booleano seleccionado; para su caso, debe ignorar el cambio de color cuando el índice es el central.

Solo una cosa cuando hace clic en un icono de TabLayout no olvide llamar al

setTagIndex();

Si no lo hace, la imagen no se volverá a dibujar

Vodet
fuente
0

Solución

Gradle:

implementation 'com.github.armcha:SpaceNavigationView:1.6.0'

Diseño:

<com.luseen.spacenavigation.SpaceNavigationView
        android:id="@+id/space"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        app:active_item_color="@color/color_trans"
        app:centre_button_color="@color/black"
        app:inactive_item_color="@color/color_trans"
        app:space_background_color="@color/white"
        app:space_item_icon_only_size="24dp"
        app:space_item_icon_size="@dimen/space_item_icon_default_size"
        app:space_item_text_size="@dimen/space_item_text_default_size" />

Añadir elementos de navegación espacial.

 spaceNavigationView = (SpaceNavigationView) findViewById(R.id.space);
 spaceNavigationView.initWithSaveInstanceState(savedInstanceState);

       /*Space navigation View*/
        spaceNavigationView.initWithSaveInstanceState(savedInstanceState);

        spaceNavigationView.addSpaceItem(new SpaceItem(0, "Templates", R.drawable.ic_color_template_tab, getResources().getColor(R.color.color_yellow)));
        spaceNavigationView.addSpaceItem(new SpaceItem(1, "Explore", R.drawable.ic_category_tab, getResources().getColor(R.color.color_bg)));
        spaceNavigationView.addSpaceItem(new SpaceItem(2, "Tools", R.drawable.ic_tools_tab, getResources().getColor(R.color.color_bg)));
        spaceNavigationView.addSpaceItem(new SpaceItem(3, "My Work", R.drawable.ic_my_work_tab, getResources().getColor(R.color.color_bg)));


        spaceNavigationView.setCentreButtonIconColorFilterEnabled(true);
        spaceNavigationView.setCentreButtonIcon(R.drawable.ic_create2_tab);
        spaceNavigationView.setInActiveCentreButtonIconColor(ContextCompat.getColor(this, R.color.white));
        spaceNavigationView.setActiveCentreButtonBackgroundColor(ContextCompat.getColor(this, R.color.color_yellow));
        spaceNavigationView.setCentreButtonColor(ContextCompat.getColor(this, R.color.black));
        spaceNavigationView.setCentreButtonRippleColor(ContextCompat.getColor(this, R.color.color_yellow));
        spaceNavigationView.setCentreButtonSelectable(true);

        spaceNavigationView.setSpaceBackgroundColor(ContextCompat.getColor(this, R.color.obaudiopicker_white));
    //  spaceNavigationView.setCentreButtonSelected(2, R.drawable.ic_color_template_tab, getResources().getColor(R.color.color_yellow));
        spaceNavigationView.setInActiveSpaceItemColor(ContextCompat.getColor(this, R.color.color_bg));

Utilizar initWithSaveInstanceState(savedInstanceState) y override onSaveInstanceStatesi desea mantener la posición del elemento seleccionado y la insignia en la rotación del dispositivo

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        spaceNavigationView.onSaveInstanceState(outState);
        Log.i(TAG, "onSaveInstanceState: ");
    }

Establecer onClick oyente

spaceNavigationView.setSpaceOnClickListener(new SpaceOnClickListener() {
            @Override
            public void onCentreButtonClick() {
                setDefaultIcon();
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        spaceNavigationView.setActiveCentreButtonIconColor(ContextCompat.getColor(NEWBusinessCardMainActivity.this, R.color.black));

                    }
                });
                changeCurrentFragmentTo(Constants.ITEM_CREATE);
            }

            @Override
            public void onItemClick(final int itemIndex, String itemName) {
                Log.d("onItemClick ", "" + itemIndex + " " + itemName);
                switch (itemIndex) {
                    case 0:
                        setDefaultIcon();
                        new Handler().post(new Runnable() {
                            @Override
                            public void run() {
                                spaceNavigationView.changeItemIconAtPosition(itemIndex, R.drawable.ic_color_template_tab);
                                spaceNavigationView.changeItemTextColorAtPosition(itemIndex, getResources().getColor(R.color.color_yellow));
                            }
                        });
                        changeCurrentFragmentTo(Constants.ITEM_TEMPLATE);
                        break;
                    case 1:
                        setDefaultIcon();
                        new Handler().post(new Runnable() {
                            @Override
                            public void run() {
                                spaceNavigationView.changeItemIconAtPosition(itemIndex, R.drawable.ic_color_category_tab);
                                spaceNavigationView.changeItemTextColorAtPosition(itemIndex, getResources().getColor(R.color.color_category));

                            }
                        });
                        changeCurrentFragmentTo(Constants.ITEM_CATEGORY);
                        break;
                    case 2:
                        setDefaultIcon();
                        new Handler().post(new Runnable() {
                            @Override
                            public void run() {
                                spaceNavigationView.changeItemIconAtPosition(itemIndex, R.drawable.ic_color_tool_tab);
                                spaceNavigationView.changeItemTextColorAtPosition(itemIndex, getResources().getColor(R.color.color_tools));

                            }
                        });
                        changeCurrentFragmentTo(Constants.ITEM_TOOLS);
                        break;
                    case 3:
                        setDefaultIcon();
                        new Handler().post(new Runnable() {
                            @Override
                            public void run() {
                                spaceNavigationView.changeItemIconAtPosition(itemIndex, R.drawable.ic_color_my_work_tab);
                                spaceNavigationView.changeItemTextColorAtPosition(itemIndex, getResources().getColor(R.color.color_mywork));

                            }
                        });
                        changeCurrentFragmentTo(Constants.ITEM_MY_WORK);
                        break;
                }
            }

            @Override
            public void onItemReselected(int itemIndex, String itemName) {
   //        Toast.makeText(MainActivity.this, itemIndex + " " + itemName, Toast.LENGTH_SHORT).show();

            }
        });

setDefaultIcon ()

private void setDefaultIcon() {
        spaceNavigationView.changeItemIconAtPosition(0, R.drawable.ic_template_tab, getResources().getColor(R.color.color_bg));
        spaceNavigationView.changeItemIconAtPosition(1, R.drawable.ic_category_tab, getResources().getColor(R.color.color_bg));
        spaceNavigationView.changeItemIconAtPosition(2, R.drawable.ic_tools_tab, getResources().getColor(R.color.color_bg));
        spaceNavigationView.changeItemIconAtPosition(3, R.drawable.ic_my_work_tab, getResources().getColor(R.color.color_bg));


    }

Ejemplo:

ingrese la descripción de la imagen aquí

R.Desai
fuente