zoukankan      html  css  js  c++  java
  • 使用装饰器模式动态设置Drawable的ColorFilter

    使用装饰器模式动态设置Drawable的ColorFilter

    欢迎各位关注我的新浪微博:微博

    转载请标明出处(kifile的博客)

    非常多时候我们都希望Android控件点击的时候,有按下效果,选中时有选中效果。通常我们都是通过使用selector来生成一个StateListDrawable来实现。

    但是这样我们会面临一个问题,假设使用selector的xml文件生成。那么对于不同的状态,我们就会须要不同的图片,才可以实现drawable的动态改变。

    但是有时候,我们的按下状态同普通状态之间唯一的差别仅仅是颜色的差异。

    那么这个时候,我们真的有必要在resources中放入多个颜色不同的图片吗?

    也许非常多人不会太在意几张图片的空间消耗,但是有时候,放着放着,包体就变大了。为了减小包体,我们真的有必要仅仅放置一张图片,然后设置他在不同状态下的色值。

    首先附上我写的一个Drawable装饰器:

    package com.kifile.android.drawable;
    
    import android.graphics.Canvas;
    import android.graphics.ColorFilter;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffColorFilter;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.util.StateSet;
    
    /**
     * 依据当前状态选择色值过滤器的Drawable.
     * <p/>
     * 使用{@link #addState(int[], int)} 加入色值.
     * 
     * @author kifile
     */
    public class ColorFilterStateListDrawable extends Drawable implements Drawable.Callback {
    
        private Drawable mDrawable;
    
        private StateListState mStateSets;
    
        private int[] mCurrentState;
    
        public ColorFilterStateListDrawable(Drawable drawable) {
            if (drawable == null) {
                throw new IllegalArgumentException("drawable cannot be null.");
            }
            mStateSets = new StateListState();
            mDrawable = drawable;
            mDrawable.setCallback(this);
        }
    
        public void addState(int[] stateSet, int color) {
            mStateSets.addStateSet(stateSet, color);
        }
    
        @Override
        public void draw(Canvas canvas) {
            ColorFilter filter = selectColorFilter();
            if (filter != null) {
                mDrawable.setColorFilter(filter);
            }
            mDrawable.draw(canvas);
        }
    
        private ColorFilter selectColorFilter() {
            if (mCurrentState == null) {
                return null;
            }
            int index = mStateSets.indexOfStateSet(mCurrentState);
            int color = mStateSets.getColor(index);
            return new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
        }
    
        @Override
        public boolean getPadding(Rect padding) {
            return mDrawable.getPadding(padding);
        }
    
        @Override
        public int getIntrinsicHeight() {
            return mDrawable.getIntrinsicHeight();
        }
    
        @Override
        public int getIntrinsicWidth() {
            return mDrawable.getIntrinsicWidth();
        }
    
        @Override
        public int getMinimumHeight() {
            return mDrawable.getMinimumHeight();
        }
    
        @Override
        public int getMinimumWidth() {
            return mDrawable.getMinimumWidth();
        }
    
        @Override
        public int getChangingConfigurations() {
            return mDrawable.getChangingConfigurations();
        }
    
        @Override
        protected void onBoundsChange(Rect bounds) {
            super.onBoundsChange(bounds);
            mDrawable.setBounds(bounds);
        }
    
        @Override
        public void setAlpha(int alpha) {
            mDrawable.setAlpha(alpha);
        }
    
        @Override
        public void setColorFilter(ColorFilter cf) {
            mDrawable.setColorFilter(cf);
        }
    
        @Override
        public int getOpacity() {
            return mDrawable.getOpacity();
        }
    
        @Override
        protected boolean onStateChange(int[] state) {
            mCurrentState = state;
            return mDrawable.setState(state);
        }
    
        @Override
        public boolean isStateful() {
            return true;
        }
    
        @Override
        public void invalidateDrawable(Drawable who) {
            if (who == mDrawable && getCallback() != null) {
                getCallback().invalidateDrawable(this);
            }
        }
    
        @Override
        public void scheduleDrawable(Drawable who, Runnable what, long when) {
            if (who == mDrawable && getCallback() != null) {
                getCallback().scheduleDrawable(this, what, when);
            }
        }
    
        @Override
        public void unscheduleDrawable(Drawable who, Runnable what) {
            if (who == mDrawable && getCallback() != null) {
                getCallback().unscheduleDrawable(this, what);
            }
        }
    
        private static class StateListState {
    
            private static final int INITIAL_SIZE = 5;
    
            int mNumChildren;
    
            int[][] mStateSets;
    
            int[] mColors;
    
            StateListState() {
                mStateSets = new int[INITIAL_SIZE][];
                mColors = new int[INITIAL_SIZE];
            }
    
            int addStateSet(int[] stateSet, int color) {
                final int pos = addChild(color);
                mStateSets[pos] = stateSet;
                return pos;
            }
    
            public final int addChild(int color) {
                final int pos = mNumChildren;
                if (pos >= mColors.length) {
                    growArray(pos, pos + 10);
                }
                mColors[pos] = color;
                mNumChildren++;
                return pos;
            }
    
            private void growArray(int oldSize, int newSize) {
                int[] newColors = new int[newSize];
                System.arraycopy(mColors, 0, newColors, 0, oldSize);
                mColors = newColors;
    
                final int[][] newStateSets = new int[newSize][];
                System.arraycopy(mStateSets, 0, newStateSets, 0, oldSize);
                mStateSets = newStateSets;
            }
    
            int indexOfStateSet(int[] stateSet) {
                final int[][] stateSets = mStateSets;
                final int N = getChildCount();
                for (int i = 0; i < N; i++) {
                    if (StateSet.stateSetMatches(stateSets[i], stateSet)) {
                        return i;
                    }
                }
                return -1;
            }
    
            public final int getChildCount() {
                return mNumChildren;
            }
    
            public int getColor(int index) {
                if (index >= 0 && index < mColors.length) {
                    return mColors[index];
                }
                return 0;
            }
        }
    
    }
    

    简介一下写这个装饰器的基本思路吧

    这个类事实上也挺简单的,就是使用装饰器模式包装了一个Drawable对象,然后将涉及到会引起界面变化的类都分发到所包装的drawable里。

    核心代码事实上在于,初始化drawable的时候,通过addState加入一个状态和该状态指定的Color色值,然后在draw()的时候,通过当前的状态去匹配当前应该显示的色值,然后通过setColorFilter设置应该显示的色值,从而令drawable显示的色值发生变化。

    另外匹配当前状态的代码參考自StateListDrawable。

  • 相关阅读:
    一个神奇的类库,可以执行字符串表达式
    Common lang一些边界方法总结(好记性不如烂笔头,需要慢慢积累).一定要利用好现有的轮子,例如Apache common与Google Guava
    借助强大的IDEA开发ide高效实现equals,hashcode以及toString方法
    数字签名是什么?
    字符串组合
    空格替换
    字符串替换 (replace)
    为什么要区分稳定和非稳定排序
    算法稳定性
    华为2018校招技术岗笔试
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5064662.html
Copyright © 2011-2022 走看看