natario
natario

Reputation: 25204

Toolbar icon tinting on Android

I've noticed that using AppCompat themes, default toolbar icons get tinted by the attribute colorControlNormal in my style.

<style name="MyTheme" parent="Theme.AppCompat">
    <item name="colorControlNormal">@color/yellow</item>
</style>

enter image description here

As you can see above, however, it does not happen with all icons. I provided the "plus" sign, which I got from the official icons, and it does not get tinted (I used the "white" version of the png). From what I have understood from this question, system tints only icons with just an alpha channel. Is this true?

If so: Is there a place where I can find alpha-defined, official material icons? If not - and if Toolbar icons need to be alpha-only to be tinted - how is Google expecting us to use provided icons in a Toolbar?

Somewhere in the SDK I found some icons ending in _alpha.png, and they actually get tinted well. However I need the full set of material icons, and from the official sources I could only find white, grey600 and black ones.

Applying a ColorFilter at runtime would be slightly painful, and my actual Toolbar - with some icons tinted, some others not - looks quite bad.

Upvotes: 49

Views: 55433

Answers (12)

AnAIDE
AnAIDE

Reputation: 21

try this ... 😊

  menu.getItem(0).getIcon().setTint(Color.parseColor("#22CC34"));

Upvotes: 1

VeeyaaR
VeeyaaR

Reputation: 311

Basically, when you set menu, the three-dot icon takes up the color of android:textColorSecondary from the AppTheme, which in default is set to Black.

So if you are not using, textColorSecondary anywhere in your project, then you can simply add the following line

<item name="android:textColorSecondary">@color/White</item>

After adding it may look like this.

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
    <!-- Customise your theme here. -->

    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:textColorSecondary">@color/White</item>
</style>

Upvotes: 0

Dan Bray
Dan Bray

Reputation: 7832

This can be done in Kotlin with:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    menu.getItem(0)?.icon?.setTint(Color.WHITE)
}
else {
    @Suppress("DEPRECATION")
    menu.getItem(0)?.icon?.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)
}

It should work on all modern versions of Android, and will fail without a crash if getItem or icon returns null.

Upvotes: 1

Ali mohammadi
Ali mohammadi

Reputation: 1899

That's the final and true answer First create style for toolbar like this:

  <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" >
     <item name="iconTint">@color/primaryTextColor</item>
    <!--choice your favorite color-->
  </style>

Then in your main app or activity theme add this line

  <item name="actionBarPopupTheme">@style/AppTheme.PopupOverlay</item>

And finally in you'r layout file add this line to toolbar

 android:theme="?attr/actionBarPopupTheme"

And Then you will see your toolbar icons colored in your favorite color

Upvotes: 16

Nicola Gallazzi
Nicola Gallazzi

Reputation: 8723

With androidX you can define your Toolbar like this

<androidx.appcompat.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:theme="@style/Toolbar" />

Then, extend an AppCompat theme and set colorControlNormal property as you like:

<style name="Toolbar" parent="Theme.AppCompat.Light">
    <item name="colorControlNormal">@color/colorBaseWhite</item>
    <item name="android:background">@color/colorPrimary</item>
</style>

Upvotes: 1

mlunap
mlunap

Reputation: 61

For sdk 23 or higher:

<style name="AppThemeToolbar" parent="MyAppTheme">
        ....
        <item name="android:drawableTint">@color/secondaryLightColor</item>
    </style>
My toolbar

    <com.google.android.material.appbar.AppBarLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content">
      <androidx.appcompat.widget.Toolbar
          android:theme="@style/AppThemeToolbar"
          android:layout_width="match_parent"
          android:layout_height="attr/actionBarSize">
      </androidx.appcompat.widget.Toolbar>
    </com.google.android.material.appbar.AppBarLayout>

Upvotes: 6

chry
chry

Reputation: 434

@NonNull
public static Drawable setTintDrawable(@NonNull Drawable drawable, @ColorInt int color) {
   drawable.clearColorFilter();
   drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
   drawable.invalidateSelf();
   Drawable wrapDrawable = DrawableCompat.wrap(drawable).mutate();
   DrawableCompat.setTint(wrapDrawable, color);
   return wrapDrawable;
 }

and call in this manner:

MenuItem location = menu.findItem(R.id.action_location);
DrawableUtils.setTintDrawable(location.getIcon(), Color.WHITE);

enter image description here

Upvotes: 0

Yusuph wickama
Yusuph wickama

Reputation: 450

I actually was able to do this on API 10 (Gingerbread) and it worked very well.

Edit: It worked on API 22 also...

Here's the final result.

enter image description here

Note: The icon is a drawable resource in the drawable folder(s).

Now here's how its done:

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);

    MenuItem item = menu.findItem(R.id.action_refresh);
    Drawable icon = getResources().getDrawable(R.drawable.ic_refresh_white_24dp);
    icon.setColorFilter(getResources().getColor(R.color.colorAccent), PorterDuff.Mode.SRC_IN);

    item.setIcon(icon);
}

At this point you can change it to any color you want!

Upvotes: 17

Joe Bowbeer
Joe Bowbeer

Reputation: 3861

Another option is to use the new support for vector drawables in the support library.

See res/xml/ic_search.xml in blog post AppCompat — Age of the vectors

Notice the reference to ?attr/colorControlNormal

<vector xmlns:android="..."
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0"
    android:tint="?attr/colorControlNormal">
    <path
        android:pathData="..."
        android:fillColor="@android:color/white"/>
</vector>

Upvotes: 40

Drew
Drew

Reputation: 3334

You could just create a custom Toolbar that uses your tint color when inflating the menu.

public class MyToolbar extends Toolbar {
    ... some constructors, extracting mAccentColor from AttrSet, etc

    @Override
    public void inflateMenu(@MenuRes int resId) {
        super.inflateMenu(resId);
        Menu menu = getMenu();
        for (int i = 0; i < menu.size(); i++) {
            MenuItem item = menu.getItem(i);
            Drawable icon = item.getIcon();
            if (icon != null) {
                item.setIcon(applyTint(icon));
            }
        }
    }
    void applyTint(Drawable icon){
        icon.setColorFilter(
           new PorterDuffColorFilter(mAccentColor, PorterDuff.Mode.SRC_IN)
        );
    }

}

Just make sure you call in your Activity/Fragment code:

toolbar.inflateMenu(R.menu.some_menu);
toolbar.setOnMenuItemClickListener(someListener);

No reflection, no view lookup, and not so much code, huh?

And don't use onCreateOptionsMenu/onOptionsItemSelected, if you use this approach

Upvotes: 5

Learn OpenGL ES
Learn OpenGL ES

Reputation: 4950

Here is the solution that I use. Call tintAllIcons after onPrepareOptionsMenu or the equivalent location. The reason for mutate() is if you happen to use the icons in more than one location; without the mutate, they will all take on the same tint.

public class MenuTintUtils {
    public static void tintAllIcons(Menu menu, final int color) {
        for (int i = 0; i < menu.size(); ++i) {
            final MenuItem item = menu.getItem(i);
            tintMenuItemIcon(color, item);
            tintShareIconIfPresent(color, item);
        }
    }

    private static void tintMenuItemIcon(int color, MenuItem item) {
        final Drawable drawable = item.getIcon();
        if (drawable != null) {
            final Drawable wrapped = DrawableCompat.wrap(drawable);
            drawable.mutate();
            DrawableCompat.setTint(wrapped, color);
            item.setIcon(drawable);
        }
    }

    private static void tintShareIconIfPresent(int color, MenuItem item) {
        if (item.getActionView() != null) {
            final View actionView = item.getActionView();
            final View expandActivitiesButton = actionView.findViewById(R.id.expand_activities_button);
            if (expandActivitiesButton != null) {
                final ImageView image = (ImageView) expandActivitiesButton.findViewById(R.id.image);
                if (image != null) {
                    final Drawable drawable = image.getDrawable();
                    final Drawable wrapped = DrawableCompat.wrap(drawable);
                    drawable.mutate();
                    DrawableCompat.setTint(wrapped, color);
                    image.setImageDrawable(drawable);
                }
            }
        }
    }
}

This won't take care of the overflow, but for that, you can do this:

Layout:

<android.support.v7.widget.Toolbar
    ...
    android:theme="@style/myToolbarTheme" />

Styles:

<style name="myToolbarTheme">
        <item name="colorControlNormal">#FF0000</item>
</style>

This works as of appcompat v23.1.0.

Upvotes: 23

natario
natario

Reputation: 25204

I see this question is getting some views so I'm going to post an answer for those who don't read the comments.

My conjectures in the question were all wrong and it is not a matter of alpha channels, at least not externally. The fact is simply that, quoting @alanv ,

AppCompat only tints its own icons. For now, you will need to manually tint any icons that you're providing separately from AppCompat.

This might change in the future but also might not. From this answer you can also see the list of icons (they all belong to the internal resource folder of appcompat, so you can't change them) that are automatically tinted and with which color.

Personally I use a colorControlNormal which is black or white (or similar shades), and import the icons with that particular color. Colored icons on a colored background look a little bad. However, another solution I found pleasant is this class on github. You just call MenuColorizer.colorMenu() when you create the menu.

Upvotes: 6

Related Questions