StateListDrawable to switch colorfilters

后端 未结 5 970
傲寒
傲寒 2020-12-15 11:34

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

5条回答
  •  失恋的感觉
    2020-12-15 11:58

    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

    • you need to create a new drawable from the constant state or you'll modify the constant state and thus any instance of that drawable around your activity.
    • you need to use my custom addState method, it has the same name of the framework method addState but I've added an additional argument (ColorFilter). This method does NOT exist in the framework superclass!

    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.

提交回复
热议问题