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

来源:互联网 发布:vba 网页数据抓取 编辑:程序博客网 时间:2024/06/08 03:15

使用装饰器模式动态设置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。

1 0
原创粉丝点击