49

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.

Community
  • 1
  • 1
natario
  • 24,954
  • 17
  • 88
  • 158
  • 2
    AppCompat only tints its own icons. For now, you will need to manually tint any icons that you're providing separately from AppCompat. – alanv Jan 29 '15 at 19:16
  • Also, the linked question only applies to notifications, not general tinting. – alanv Jan 29 '15 at 19:18
  • 1
    See also [this StackO-Post](http://stackoverflow.com/questions/26780046/menuitem-tinting-on-appcompat-toolbar) for a thorough explanation. – reVerse Jan 29 '15 at 19:19
  • Looks like I should have searched a bit longer. I will gladly accept your answer @alanv , or let's flag this as a duplicate. Out of curiosity, you do say "for now" because you know about a fix coming, or you just imagine so? – natario Jan 30 '15 at 09:37
  • It's something that we'd like to do, but I can't make any promises. – alanv Jan 30 '15 at 17:58

12 Answers12

40

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>
Joe Bowbeer
  • 3,574
  • 3
  • 36
  • 47
  • What's the difference between using `?attr/colorControlNormal` at `android:tint` vs `android:fillColor` attributes? Thanks! – Thomas Vos Jun 20 '17 at 15:46
  • @SuperThomasLab The example is from Chris Banes's blog where it illustrates replacing a tinted image with a tinted vector. Note that the fillColor was effectively hard-coded in the source image. I do not know if other color representations are now supported in AppCompat. – Joe Bowbeer Jun 22 '17 at 20:06
  • I was looking a long time for this answer. Thanks a lot. – S. Gissel Jan 09 '23 at 12:17
23

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.

Learn OpenGL ES
  • 4,759
  • 1
  • 36
  • 38
17

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!

Yusuph wickama
  • 450
  • 4
  • 15
16

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

Ali mohammadi
  • 1,839
  • 26
  • 27
6

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.

Community
  • 1
  • 1
natario
  • 24,954
  • 17
  • 88
  • 158
6

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>

mlunap
  • 61
  • 1
  • 3
5

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

Drew
  • 3,307
  • 22
  • 33
1

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>
Nicola Gallazzi
  • 7,897
  • 6
  • 45
  • 64
1

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.

Dan Bray
  • 7,242
  • 3
  • 52
  • 70
1

try this ...

  menu.getItem(0).getIcon().setTint(Color.parseColor("#22CC34"));
AnAIDE
  • 21
  • 2
0
@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

chry
  • 434
  • 7
  • 5
0

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>
VeeyaaR
  • 291
  • 4
  • 7