Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Helper class to colorize all Android Toolbar Icons
/*
Copyright 2015 Michal Pawlowski
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package pl.snowdog.material.ui;
import android.app.Activity;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.os.Build;
import android.support.v7.internal.view.menu.ActionMenuItemView;
import android.support.v7.internal.widget.TintImageView;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageButton;
import java.util.ArrayList;
import pl.snowdog.material.R;
/**
* Helper class that iterates through Toolbar views, and sets dynamically icons and texts color
* Created by chomi3 on 2015-01-19.
*/
public class ToolbarColorizeHelper {
/**
* Use this method to colorize toolbar icons to the desired target color
* @param toolbarView toolbar view being colored
* @param toolbarIconsColor the target color of toolbar icons
* @param activity reference to activity needed to register observers
*/
public static void colorizeToolbar(Toolbar toolbarView, int toolbarIconsColor, Activity activity) {
final PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(toolbarIconsColor, PorterDuff.Mode.MULTIPLY);
for(int i = 0; i < toolbarView.getChildCount(); i++) {
final View v = toolbarView.getChildAt(i);
//Step 1 : Changing the color of back button (or open drawer button).
if(v instanceof ImageButton) {
//Action Bar back button
((ImageButton)v).getDrawable().setColorFilter(colorFilter);
}
if(v instanceof ActionMenuView) {
for(int j = 0; j < ((ActionMenuView)v).getChildCount(); j++) {
//Step 2: Changing the color of any ActionMenuViews - icons that are not back button, nor text, nor overflow menu icon.
//Colorize the ActionViews -> all icons that are NOT: back button | overflow menu
final View innerView = ((ActionMenuView)v).getChildAt(j);
if(innerView instanceof ActionMenuItemView) {
for(int k = 0; k < ((ActionMenuItemView)innerView).getCompoundDrawables().length; k++) {
if(((ActionMenuItemView)innerView).getCompoundDrawables()[k] != null) {
final int finalK = k;
//Important to set the color filter in seperate thread, by adding it to the message queue
//Won't work otherwise.
innerView.post(new Runnable() {
@Override
public void run() {
((ActionMenuItemView) innerView).getCompoundDrawables()[finalK].setColorFilter(colorFilter);
}
});
}
}
}
}
}
//Step 3: Changing the color of title and subtitle.
toolbarView.setTitleTextColor(toolbarIconsColor);
toolbarView.setSubtitleTextColor(toolbarIconsColor);
//Step 4: Changing the color of the Overflow Menu icon.
setOverflowButtonColor(activity, colorFilter);
}
}
/**
* It's important to set overflowDescription atribute in styles, so we can grab the reference
* to the overflow icon. Check: res/values/styles.xml
* @param activity
* @param colorFilter
*/
private static void setOverflowButtonColor(final Activity activity, final PorterDuffColorFilter colorFilter) {
final String overflowDescription = activity.getString(R.string.abc_action_menu_overflow_description);
final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
final ArrayList<View> outViews = new ArrayList<View>();
decorView.findViewsWithText(outViews, overflowDescription,
View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
if (outViews.isEmpty()) {
return;
}
TintImageView overflow=(TintImageView) outViews.get(0);
overflow.setColorFilter(colorFilter);
removeOnGlobalLayoutListener(decorView,this);
}
});
}
private static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
}
else {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
}
@olkunmustafa

This comment has been minimized.

Copy link

@olkunmustafa olkunmustafa commented Sep 13, 2015

It didn't exactly work for me.
It can change the toolbar title and menudrawer color but, menu items colors are not changed.

@ksza

This comment has been minimized.

Copy link

@ksza ksza commented Oct 21, 2015

Just update to support v7 - 23. In the latest version, the overflow has been changed a bit and the TintImageView is gone. To make the code work with the laster v7 lib, simply replace:

            TintImageView overflow=(TintImageView) outViews.get(0);
            overflow.setColorFilter(colorFilter);

with

            final ActionMenuView overflowViewParent = (ActionMenuView) outViews.get(0).getParent();
            overflowViewParent.getOverflowIcon().setColorFilter(color, PorterDuff.Mode.SRC_ATOP);

I hope this help. Cheers!

@danielwilson1702

This comment has been minimized.

Copy link

@danielwilson1702 danielwilson1702 commented Jan 8, 2016

I don't think you need the content description to identify the overflow icon, the getOverflowIcon() function is probably new but I modified your setOverflowButtonColor() like so:

    private static void setOverflowButtonColor(final Activity activity, final Toolbar toolbar, final int toolbarIconsColor) {
        final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
        final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
        viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                if(toolbar != null && toolbar.getOverflowIcon() != null){
                    Drawable bg = DrawableCompat.wrap(toolbar.getOverflowIcon());
                    DrawableCompat.setTint(bg, toolbarIconsColor);
                }
                removeOnGlobalLayoutListener(decorView,this);
            }
        });
    }

Thanks for this though it still seems to be kinda needlessly tricky to color tool bar icons!

@auval

This comment has been minimized.

Copy link

@auval auval commented Nov 15, 2016

Did you mean to put steps 3 and 4 inside the for loop?
I think it should go outside the loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment