Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Material Toolbar Utilities
import android.animation.LayoutTransition;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.annotation.DrawableRes;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import java.lang.reflect.Field;
import java.util.HashMap;
/**
* Created by pechanecjr on 10. 11. 2014.
*/
public class ToolbarUtils {
public static final boolean API_21 = Build.VERSION.SDK_INT >= 21;
private ToolbarUtils() {
}
private static final HashMap<TintConfig, Drawable> CACHE = new HashMap<>();
/**
* Tint specified drawable with {@code colorControlNormal} (or {@code android:colorControlNormal} on API 21) from style.
* @param drawableId
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Drawable getControlDrawable(@DrawableRes int drawableId, Context context) {
TypedArray ta = context.obtainStyledAttributes(new int[]{API_21 ? android.R.attr.colorControlNormal : R.attr.colorControlNormal});
int c = ta.getColor(0, Color.BLACK);
ta.recycle();
Drawable d = context.getResources().getDrawable(drawableId);
return getTintedDrawableInternal(d, c);
}
/**
* Tint specified drawable with {@code colorControlNormal} (or {@code android:colorControlNormal} on API 21) from style.
* @param d
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Drawable getControlDrawable(Drawable d, Context context) {
TypedArray ta = context.obtainStyledAttributes(new int[]{API_21 ? android.R.attr.colorControlNormal : R.attr.colorControlNormal});
int c = ta.getColor(0, Color.BLACK);
ta.recycle();
return getTintedDrawableInternal(d, c);
}
/**
* Tint specified drawable with {@code colorAccent} (or {@code android:colorAccent} on API 21) from style.
* @param drawableId
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Drawable getAccentDrawable(@DrawableRes int drawableId, Context context) {
TypedArray ta = context.obtainStyledAttributes(new int[]{API_21 ? android.R.attr.colorAccent : R.attr.colorAccent});
int c = ta.getColor(0, Color.BLACK);
ta.recycle();
Drawable d = context.getResources().getDrawable(drawableId);
return getTintedDrawableInternal(d, c);
}
/**
* Tint specified drawable with {@code colorAccent} (or {@code android:colorAccent} on API 21) from style.
* @param d
* @param context
* @return
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static Drawable getAccentDrawable(Drawable d, Context context) {
TypedArray ta = context.obtainStyledAttributes(new int[]{API_21 ? android.R.attr.colorAccent : R.attr.colorAccent});
int c = ta.getColor(0, Color.BLACK);
ta.recycle();
return getTintedDrawableInternal(d, c);
}
/**
* Tint specified drawable with specified color.
* @param context
* @param drawableId
* @param colorId
* @return
*/
public static Drawable getTintedDrawable(Context context, @DrawableRes int drawableId, @ColorRes int colorId) {
Drawable d = context.getResources().getDrawable(drawableId);
int c = context.getResources().getColor(colorId);
return getTintedDrawableInternal(d, c);
}
private static Drawable getTintedDrawableInternal(Drawable d, int c) {
TintConfig tc = new TintConfig(d, c);
if (CACHE.containsKey(tc)) {
return CACHE.get(tc);
}
d = d.mutate();
d.setColorFilter(c, PorterDuff.Mode.SRC_IN);
CACHE.put(tc, d);
return d;
}
/**
* Tint one menu item icon with specified color.
* @param item
* @param color
*/
public static void tintMenuItem(MenuItem item, int color) {
Drawable icon = item.getIcon();
if (icon != null) {
icon = icon.mutate();
icon.setColorFilter(color, PorterDuff.Mode.SRC_IN);
item.setIcon(icon);
}
}
/**
* Tint all menu item icons (except back and overflow) with specified color.
* @param menu
* @param color
*/
public static void tintMenu(Menu menu, int color) {
for (int i = 0; i < menu.size(); i++) {
MenuItem item = menu.getItem(i);
tintMenuItem(item, color);
}
}
/**
* Tint all menu item icons (except back and overflow) with {@code colorControlNormal} color from style.
* @param menu
* @param context
*/
public static void tintMenu(Menu menu, Context context) {
TypedArray ta = context.obtainStyledAttributes(new int[]{R.attr.colorControlNormal});
int color = ta.getColor(0, Color.BLACK);
ta.recycle();
tintMenu(menu, color);
}
/** @hide */
private static class TintConfig {
public TintConfig(Drawable drawable, int color) {
this.drawable = drawable;
this.color = color;
}
public Drawable drawable;
public int color;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TintConfig)) return false;
TintConfig that = (TintConfig) o;
if (color != that.color) return false;
if (!drawable.equals(that.drawable)) return false;
return true;
}
@Override
public int hashCode() {
int result = drawable.hashCode();
result = 31 * result + color;
return result;
}
}
private static final Class<?> CLS_TOOLBAR = android.support.v7.widget.Toolbar.class;
private static final Field FIELD_COLLAPSE_ICON;
private static final Field FIELD_COLLAPSE_BUTTON_VIEW;
// private static final Field FIELD_TINT_MANAGER;
static {
Field fieldCollapseIcon = null;
Field fieldCollapseButtonView = null;
// Field fieldTintManager = null;
for (Field f : CLS_TOOLBAR.getDeclaredFields()) {
switch (f.getName()) {
case "mCollapseIcon":
fieldCollapseIcon = f;
break;
case "mCollapseButtonView":
fieldCollapseButtonView = f;
break;
// case "mTintManager":
// fieldTintManager = f;
// break;
}
}
FIELD_COLLAPSE_ICON = fieldCollapseIcon;
FIELD_COLLAPSE_BUTTON_VIEW = fieldCollapseButtonView;
// FIELD_TINT_MANAGER = fieldTintManager;
if (FIELD_COLLAPSE_ICON != null) {
FIELD_COLLAPSE_ICON.setAccessible(true);
}
if (FIELD_COLLAPSE_BUTTON_VIEW != null) {
FIELD_COLLAPSE_BUTTON_VIEW.setAccessible(true);
}
// if (FIELD_TINT_MANAGER != null) {
// FIELD_TINT_MANAGER.setAccessible(true);
// }
}
public static void setSearchViewLayoutTransition(android.widget.SearchView view) {
int searchBarId = view.getContext().getResources().getIdentifier("android:id/search_bar", null, null);
LinearLayout searchBar = (LinearLayout) view.findViewById(searchBarId);
searchBar.setLayoutTransition(new LayoutTransition());
}
public static void setSearchViewLayoutTransition(android.support.v7.widget.SearchView view) {
LinearLayout searchBar = (LinearLayout) view.findViewById(R.id.search_bar);
searchBar.setLayoutTransition(new LayoutTransition());
}
/**
* Support Toolbar (21.0.1) does not respect {@code colorControlNormal} color and paints the
* "up" button always white. This method fixes that behavior. Applies only to
* {@link android.os.Build.VERSION_CODES#LOLLIPOP}, maybe higher.
*
* Easier is to set the theme's homeAsUpIndicator to appcompat-v7 library's back button which gets colored automatically.
* Do not use this method.
*
* @param toolbar
*/
@Deprecated
public static void fixToolbar(android.support.v7.widget.Toolbar toolbar) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
try {
// TintManager tm = (TintManager) FIELD_TINT_MANAGER.get(toolbar);
// Drawable d = (Drawable) FIELD_COLLAPSE_ICON.get(toolbar);
ImageButton ib = (ImageButton) FIELD_COLLAPSE_BUTTON_VIEW.get(toolbar);
Drawable d = ToolbarUtils.getControlDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha, toolbar.getContext());
FIELD_COLLAPSE_ICON.set(toolbar, d);
ib.setImageDrawable(d);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Provides native ActionBar (e.g. in PreferenceActivity which is only native) with a Material
* back button.
* <p/>
* The {@code android:actionBarStyle} must specify {@code colorControlNormal} which will define
* icon color.
*
* @param activity
* @param back Optional back button drawable. Defaults to back arrow. Will be colored.
*/
public static void fixActionBar(Activity activity, Drawable back) {
ActionBar ab = activity.getActionBar();
if (ab == null) return;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
Drawable d;
if (back == null) {
d = ToolbarUtils.getControlDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha, activity.getActionBar().getThemedContext());
} else {
d = ToolbarUtils.getControlDrawable(back, activity.getActionBar().getThemedContext());
}
int p = activity.getResources().getDimensionPixelOffset(R.dimen.mtrl_unit);
int width = activity.getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) + p;
int height = activity.getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_height_material);
ImageView image;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewGroup home = (ViewGroup) activity.findViewById(android.R.id.home).getParent();
image = ((ImageView) home.getChildAt(0));
} else {
image = ((ImageView) activity.findViewById(R.id.up));
}
image.setScaleType(ImageView.ScaleType.CENTER);
image.setMinimumWidth(width);
image.setMinimumHeight(height);
image.setImageDrawable(d);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment