Android BottomNavigationView One guia com cores diferentes não selecionadas / selecionadas


8

Estou tentando combinar um design como este ..

insira a descrição da imagem aqui

Observe que a "tonalidade da cor da guia selecionada" é azul, mas o ícone da guia central deve sempre ser o círculo verde com o relógio branco no meio.

Eu tentei várias coisas. Primeiro, tente fazê-lo programaticamente usando um recurso XML de lista de camadas que tinha o recurso PNG de círculo e relógio verde, que não funcionou. Depois, pedi ao designer que me desse o ícone completo (relógio e círculo verde), mas agora estou enfrentando esse problema.

(Não selecionado)

insira a descrição da imagem aqui

(Selecionado)

insira a descrição da imagem aqui

Não consigo encontrar os termos corretos para pesquisar no Google para corrigir isso.

No final, preciso que a cor da guia selecionada seja azul, mas preciso que o ícone da guia central seja sempre o ícone real, sem cores adicionais (essencialmente, ele precisa se parecer exatamente com o .png).

PS: Estou usando o Xamarin.Forms, FreshMvvm e o FreshTabbedFONavigationContainer. No entanto, através do Renderer, tenho acesso direto ao BottomNavigationView e a todos os outros componentes nativos do Android. Portanto, a solução não precisa ser uma solução Xamarin. Uma solução java / kotlin também funcionaria e eu posso convertê-la para o Xamarin.

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

EDITADO:

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

Então, eu fui muito além usando o código de Andrés Castro abaixo, mas ainda estou tendo o mesmo problema de antes. Usando o código de Andres abaixo, voltei a usar o FontAwesome para os ícones (o que funciona muito bem), mas ao fazer isso, eu precisava usar a LayerDrawablepara criar o ícone da guia central do círculo / ícone.

Então é isso que eu tenho até agora ..

Ícone central não selecionado

insira a descrição da imagem aqui

Ícone do centro selecionado

insira a descrição da imagem aqui

Como você pode ver, o ícone central ainda fica cinza quando não selecionado e azul quando selecionado (as cores selecionadas / não selecionadas apropriadas dos outros 4 ícones).

Aqui está o código que tenho até agora referente ao ícone central.

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 você pode ver no GradientDrawableque eu estou criando para o círculo, estou configurando a cor para a minha cor verde (eu tenho uma classe personalizada chamada Resources Resources.. que não é a classe Android ).

Então é aí que eu estou preso. Estou definindo o círculo que pode ser desenhado para ter uma cor verde, mas uma vez no BottomNavigationView, a cor sempre corresponde às cores não selecionadas / selecionadas dos outros ícones.

Esperando superar esta edição final. Obrigado por qualquer ajuda.


Uma solução simples é usar um item em branco no meio e colocar uma visualização de imagem separada sobre o BottomNavigationView com a imagem verde.
Shivam Yadav

1
Por que você não altera programaticamente a tonalidade / cor selecionada, dependendo do índice da exibição de navegação inferior?
Saamer 13/12/19

1
@ Saamer Seu comentário parecia tão simples haha. Você pode publicá-lo como resposta para que eu possa conceder a recompensa? Este é o código que usei ..._bottomNavigationView.ItemIconTintList = null;
Ryan Alford

Funcionou para você? O BottomNavigationView é dolorosamente mais difícil do que sua implementação no iOS. Eu fiz algumas pesquisas para ver se o que você estava perguntando era possível e, quando o vi no Android, comecei a pensar em maneiras de fazer isso acontecer.
Saamer 16/12/19

1
Sim, funcionou. Não tenho certeza, mas parece que minha alteração redefiniria TODAS as tonalidades do ícone do item, mas não. Todas as outras guias ainda funcionam porque estou definindo manualmente todas as vezes, e o centro fica verde quando é selecionado e desmarcado.
Ryan Alford

Respostas:


1

O BottomNavigationView é dolorosamente mais difícil do que sua implementação no iOS. Eu fiz algumas pesquisas para ver se o que você estava perguntando era possível e, quando o vi no Android, comecei a pensar em maneiras de fazer isso acontecer.

Para implementar seu último desafio, você precisaria alterar programaticamente a tonalidade / cor selecionada sempre, dependendo do índice da visualização de navegação inferior.


2

Como você tem acesso à visualização de navegação inferior, basta "redesenhar" a barra de ferramentas inferior sempre que alternar as páginas. Fizemos algo semelhante e não percebemos nenhum problema de desempenho.

Você primeiro deseja monitorar as alterações de página assinando a alteração de página dentro OnElementChanged

Element.CurrentPageChanged += ElementOnCurrentPageChanged;

Dentro, ElementOnCurrentPageChangedvocê pode percorrer cada página e obter o item de menu dessa 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);
    }
}

No nosso caso, usamos ícones de fonte para desenhar os ícones e definir as cores todas as vezes.

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

Também tivemos que substituir o OnAttachedToWindow para garantir que os ícones fossem redesenhados ao retornar ao aplicativo de diferentes estados.

protected override void OnAttachedToWindow()
{
    UpdateTabbedIcons();

    base.OnAttachedToWindow();
}

Você precisará modificar isso alguns para se adequar ao seu caso de uso, mas acho que esse método deve realizar o que você está procurando.


Impressionante. Vou tentar hoje à noite. Se você possui um código de trabalho que usa FontAwesome (ou outras fontes de gráficos vetoriais) para o BottomNavigationView, você realmente deve publicar um blog em algum lugar. Não encontrei nenhum exemplo e minhas tentativas nunca mostrariam o ícone.
Ryan Alford

1

você pode usar imagens SVG, criar seu próprio atributo de cor e criar um seletor extraível para o ícone central, bem como outro ícone de exibição de navegação inferior, como abaixo:

no arquivo colors.xml

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

no arquivo attrs.xml

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

no arquivo de imagem SVG

substitua o valor da cor codificada pelo seu atributo

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

e não se esqueça de tornar o seletor extraível


1

Tente usar o DST no modo tonalidade , que simplesmente ignorará a tonalidade.

Adicione esta linha ao seu método GetCombinedDrawable()

circleDrawable.setTintMode(PorterDuff.Mode.DST);

Se isso não funcionar, tente o seguinte:

finalDrawable.ClearColorFilter();
finalDrawable.setTintMode(PorterDuff.Mode.DST);

Na verdade, eu tentei todas as opções de PorterDuff.Mode, mas nenhuma funcionou.
Ryan Alford

0

Eu posso ajudá-lo com isso :

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 é um TabLayout personalizado, você pode ver que eu estende a classe TabLayout, quando o TabLayout é criado, estou chamando o método initItems que addTab e define uma exibição personalizada para ele.

O getTabView retorna a exibição inflada, como você pode ver com este

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

Se você precisar

<?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>

depois de inflar a vista, você pode obter seu elemento de vista com

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

Você pode verificar se a visualização está selecionada com o booleano selecionado. Para o seu caso, você precisa ignorar a opção de cores quando o índice é central.

Apenas uma coisa quando você clica em um ícone do TabLayout, não se esqueça de chamar o

setTagIndex();

Se você não fizer isso, a imagem não será redesenhada


0

Solução

Gradle:

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

Layout:

<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" />

Adicione itens de Navegação 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));

Use initWithSaveInstanceState(savedInstanceState)e override onSaveInstanceStatese desejar manter a posição do item selecionado e o crachá na rotação do dispositivo

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

Definir ouvinte onClick

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


    }

Exemplo:

insira a descrição da imagem aqui

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.