I want to create custom buttons to use in a TabHost. I haven been trying to just use the same image resource (png), but have the colorfilter change depending on the state. S
This is my class, hacked to support ColorFilter:
Usage:
final Drawable icon = getResources().getDrawable(iconResId);
final Drawable filteredIcon = // this is important
icon.getConstantState().newDrawable();
final FilterableStateListDrawable selectorDrawable =
new FilterableStateListDrawable();
selectorDrawable.addState(ICON_STATE_SELECTED, filteredIcon,
new PorterDuffColorFilter(mIconOverlayColor, PorterDuff.Mode.SRC_ATOP));
selectorDrawable.addState(ICON_STATE_DEFAULT, icon);
As you see the ColorFilter is not applied directly to the drawable, it is associated to it while adding a state to the selector Drawable.
What's important here is that
The code (dirty, but work for me):
/**
* This is an extension to {@link android.graphics.drawable.StateListDrawable} that workaround a bug not allowing
* to set a {@link android.graphics.ColorFilter} to the drawable in one of the states., it add a method
* {@link #addState(int[], android.graphics.drawable.Drawable, android.graphics.ColorFilter)} for that purpose.
*/
public class FilterableStateListDrawable extends StateListDrawable {
private int currIdx = -1;
private int childrenCount = 0;
private SparseArray filterMap;
public FilterableStateListDrawable() {
super();
filterMap = new SparseArray();
}
@Override
public void addState(int[] stateSet, Drawable drawable) {
super.addState(stateSet, drawable);
childrenCount++;
}
/**
* Same as {@link #addState(int[], android.graphics.drawable.Drawable)}, but allow to set a colorFilter associated to this Drawable.
*
* @param stateSet - An array of resource Ids to associate with the image.
* Switch to this image by calling setState().
* @param drawable -The image to show.
* @param colorFilter - The {@link android.graphics.ColorFilter} to apply to this state
*/
public void addState(int[] stateSet, Drawable drawable, ColorFilter colorFilter) {
// this is a new custom method, does not exist in parent class
int currChild = childrenCount;
addState(stateSet, drawable);
filterMap.put(currChild, colorFilter);
}
@Override
public boolean selectDrawable(int idx) {
if (currIdx != idx) {
setColorFilter(getColorFilterForIdx(idx));
}
boolean result = super.selectDrawable(idx);
// check if the drawable has been actually changed to the one I expect
if (getCurrent() != null) {
currIdx = result ? idx : currIdx;
if (!result) {
// it has not been changed, meaning, back to previous filter
setColorFilter(getColorFilterForIdx(currIdx));
}
} else if (getCurrent() == null) {
currIdx = -1;
setColorFilter(null);
}
return result;
}
private ColorFilter getColorFilterForIdx(int idx) {
return filterMap != null ? filterMap.get(idx) : null;
}
}
I've opened a bug about this: https://code.google.com/p/android/issues/detail?id=60183
UPDATE: the bug has been fixed in the framework, since Lollipop I think. I think the fix commit is this: https://android.googlesource.com/platform/frameworks/base/+/729427d%5E!/
or on Github: https://github.com/android/platform_frameworks_base/commit/729427d451bc4d4d268335b8dc1ff6404bc1c91e
My workaround should still work after Lollipop, it just don't use the fix by Google.