Last active
August 29, 2015 14:10
-
-
Save consp1racy/96958a1dedf5a99d4ca5 to your computer and use it in GitHub Desktop.
Material Toolbar Utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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