J'essaie de faire correspondre un design comme celui-ci ..
Notez que la "teinte de couleur de l'onglet sélectionné" est un bleu, mais l'icône de l'onglet central doit toujours être le cercle vert avec l'horloge blanche au milieu.
J'ai essayé un certain nombre de choses. D'abord essayer de le faire par programme en utilisant une ressource XML de liste de couches qui avait la ressource PNG de cercle vert et d'horloge, qui ne fonctionnait pas du tout. Ensuite, je viens de demander au designer de me donner l'icône complète (horloge et cercle vert), mais maintenant je rencontre ce problème ..
(Non sélectionné)
(Choisi)
Je n'arrive pas à trouver les termes corrects à rechercher sur Google pour résoudre ce problème.
En fin de compte, j'ai besoin que la couleur de l'onglet sélectionnée soit bleue, mais j'ai besoin que l'icône de l'onglet central soit toujours l'icône réelle sans coloration supplémentaire (elle doit essentiellement ressembler exactement au .png).
PS: J'utilise Xamarin.Forms, FreshMvvm et FreshTabbedFONavigationContainer. Cependant, via le Renderer, j'ai un accès direct à BottomNavigationView et à tous les autres composants natifs Android. La solution ne doit donc pas nécessairement être une solution Xamarin. Une solution Java/kotlin fonctionnerait aussi et je peux simplement le convertir en Xamarin.
======================
ÉDITÉ:
======================
Je suis donc allé beaucoup plus loin en utilisant Andres Castro code ci-dessous, mais j'ai toujours le même problème qu'auparavant. En utilisant le code d'Andres ci-dessous, je suis retourné à l'utilisation de FontAwesome pour les icônes (ce qui fonctionne très bien), mais ce faisant, je devais utiliser un LayerDrawable
pour créer l'icône de l'onglet central du cercle/icône.
Voilà donc ce que j'ai jusqu'à présent ..
Icône centrale non sélectionnée
Icône centrale sélectionnée
Comme vous pouvez le voir, l'icône centrale est toujours grise lorsqu'elle n'est pas sélectionnée et bleue lorsqu'elle est sélectionnée (les couleurs appropriées sélectionnées/non sélectionnées des 4 autres icônes).
Voici le code que j'ai jusqu'à présent concernant l'icône centrale.
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;
}
Comme vous pouvez le voir dans le GradientDrawable
que je crée pour le cercle, je mets sa couleur à ma couleur verte (j'ai une classe personnalisée appelée Resources..that n'est pas le Android Resources
class).
C'est donc là que je suis coincé. Je configure le cercle dessinable pour avoir une couleur verte, mais une fois dans BottomNavigationView, sa couleur correspond toujours aux couleurs non sélectionnées/sélectionnées des autres icônes.
En espérant dépasser ce dernier numéro. Merci pour toute aide.
BottomNavigationView est douloureusement plus difficile que son implémentation iOS. J'ai fait des recherches pour voir si ce que vous demandiez était possible, puis quand je l'ai vu en Android natif, j'ai commencé à réfléchir à des moyens pour y arriver.
Pour implémenter votre dernier défi, vous devrez modifier par programme la teinte/couleur sélectionnée à chaque fois en fonction de l'index de la vue de navigation inférieure.
Puisque vous avez accès à la vue de navigation inférieure, vous pouvez simplement "redessiner" votre barre d'outils inférieure à chaque fois que vous changez de page. Nous avons fait quelque chose de similaire et n'avons remarqué aucun problème de performance.
Vous voudrez d'abord surveiller les changements de page en vous abonnant au changement de page dans OnElementChanged
Element.CurrentPageChanged += ElementOnCurrentPageChanged;
Ensuite, à l'intérieur de ElementOnCurrentPageChanged
, vous pouvez parcourir chaque page et obtenir l'élément de menu pour cette page
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);
}
}
Dans notre cas, nous avons utilisé des icônes de police, nous avons donc dessiné les icônes et défini les couleurs à chaque fois.
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);
}
Nous avons également dû remplacer OnAttachedToWindow pour nous assurer que les icônes étaient redessinées lors du retour à l'application à partir de différents états.
protected override void OnAttachedToWindow()
{
UpdateTabbedIcons();
base.OnAttachedToWindow();
}
Vous devrez en modifier certains pour l'adapter à votre cas d'utilisation, mais je pense que cette méthode devrait accomplir ce que vous recherchez.
vous pouvez utiliser des images SVG et créer votre propre attribut de couleur et créer un sélecteur dessinable pour l'icône centrale ainsi que d'autres icônes de vue de navigation en bas, comme ci-dessous:
dans le fichier colors.xml
<color name="color_bottom_selected">#44C8F5</color>
<color name="color_bottom_unselected">#c0c0c0</color>
dans le fichier attrs.xml
<attr name="color_bottom_unselected" format="color" />
<attr name="color_bottom_selected" format="color" />
dans le fichier image SVG
remplacez la valeur de couleur codée en dur par votre attribut
Android:fillColor="#fff" to Android:fillColor="?attr/color_bottom_unselected"
et n'oubliez pas de rendre le sélecteur dessinable
Essayez d'utiliser le mode de teinte DST , qui ignorera simplement la teinte.
Ajoutez cette ligne dans votre méthode GetCombinedDrawable()
circleDrawable.setTintMode(PorterDuff.Mode.DST);
Si cela ne fonctionne pas, essayez ceci:
finalDrawable.ClearColorFilter();
finalDrawable.setTintMode(PorterDuff.Mode.DST);
Je peux vous aider avec ceci:
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;
}
}
Il s'agit d'un TabLayout personnalisé, vous pouvez voir que je suis une classe TabLayout étendue, lorsque le TabLayout est créé, j'appelle la méthode initItems qui addTab et définit une vue personnalisée pour cela.
GetTabView renvoie la vue gonflée comme vous pouvez le voir avec cette
LayoutInflater.from(context).inflate(R.layout.activity_main_tab_layout, null);
Si tu en as besoin
<?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>
après avoir gonflé la vue, vous pouvez obtenir votre élément de vue avec
ImageView img = v.findViewById(R.id.tablayout_image);
Vous pouvez vérifier si la vue est sélectionnée avec le booléen sélectionné, pour votre cas, vous devez ignorer le commutateur de couleur lorsque l'index est le central.
Une seule chose lorsque vous cliquez sur une icône TabLayout n'oubliez pas d'appeler le
setTagIndex();
Si vous ne le faites pas, l'image ne sera pas redessinée
Solution
Gradle:
implementation 'com.github.armcha:SpaceNavigationView:1.6.0'
Disposition:
<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" />
Ajoutez des éléments de navigation spatiale.
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));
Utilisez initWithSaveInstanceState(savedInstanceState)
et override onSaveInstanceState
Si vous souhaitez conserver la position et le badge de l'élément sélectionné lors de la rotation de l'appareil
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
spaceNavigationView.onSaveInstanceState(outState);
Log.i(TAG, "onSaveInstanceState: ");
}
Définir l'écouteur 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));
}
Exemple: