Android:克隆一个drawable,以便使用过滤器创建一个StateListDrawable

时间:2011-11-02 11:17:01

标签: android android-widget drawable

我正在尝试制作一个通用框架功能,当按下/聚焦/选择/等时,任何Drawable都会突出显示。

我的函数使用Drawable并返回StateListDrawable,其中默认状态是Drawable本身,而android.R.attr.state_pressed的状态是相同的drawable,只需使用setColorFilter应用过滤器。

我的问题是我无法克隆drawable并使用过滤器创建一个单独的实例。这是我想要实现的目标:

StateListDrawable makeHighlightable(Drawable drawable)
{
    StateListDrawable res = new StateListDrawable();

    Drawable clone = drawable.clone(); // how do I do this??

    clone.setColorFilter(0xFFFF0000, PorterDuff.Mode.MULTIPLY);
    res.addState(new int[] {android.R.attr.state_pressed}, clone);
    res.addState(new int[] { }, drawable);
    return res;
}

如果我没有克隆,那么过滤器显然适用于这两种状态。我试过玩mutate(),但它没有帮助..

有什么想法吗?

更新

接受的答案确实克隆了一个可绘制的答案。这对我没有帮助,因为我的一般功能在一个不同的问题上失败了。似乎当你向StateList添加一个drawable时,它会丢失所有的过滤器。

7 个答案:

答案 0 :(得分:153)

尝试以下方法:

Drawable clone = drawable.getConstantState().newDrawable();

答案 1 :(得分:96)

如果将过滤器/ etc应用于使用getConstantState().newDrawable()创建的drawable,那么该drawable的所有实例也将被更改,因为drawable使用constantState作为缓存!

因此,如果您使用滤镜和newDrawable()为圆圈着色,则会更改所有圆圈的颜色。

如果你想在不影响其他实例的情况下使这个可绘制的可更新,那么你必须改变现有的常量状态。

// To make a drawable use a separate constant state
drawable.mutate()

有一个很好的解释,请参阅:

http://www.curious-creature.org/2009/05/02/drawable-mutations/

http://developer.android.com/reference/android/graphics/drawable/Drawable.html#mutate()

答案 2 :(得分:13)

这对我有用。

Drawable clone = drawable.getConstantState().newDrawable().mutate();

答案 3 :(得分:10)

这是我的解决方案,基于此SO question

这个想法是ImageView在用户触摸时获取滤色器,并在用户停止触摸时删除滤色器。内存中只有1个可绘制/位图,因此无需浪费它。它可以正常工作。

class PressedEffectStateListDrawable extends StateListDrawable {

    private int selectionColor;

    public PressedEffectStateListDrawable(Drawable drawable, int selectionColor) {
        super();
        this.selectionColor = selectionColor;
        addState(new int[] { android.R.attr.state_pressed }, drawable);
        addState(new int[] {}, drawable);
    }

    @Override
    protected boolean onStateChange(int[] states) {
        boolean isStatePressedInArray = false;
        for (int state : states) {
            if (state == android.R.attr.state_pressed) {
                isStatePressedInArray = true;
            }
        }
        if (isStatePressedInArray) {
            super.setColorFilter(selectionColor, PorterDuff.Mode.MULTIPLY);
        } else {
            super.clearColorFilter();
        }
        return super.onStateChange(states);
    }

    @Override
    public boolean isStateful() {
        return true;
    }
}

用法:

Drawable drawable = new FastBitmapDrawable(bm);
imageView.setImageDrawable(new PressedEffectStateListDrawable(drawable, 0xFF33b5e5));

答案 4 :(得分:1)

我回答了相关问题here

基本上看起来StateListDrawables确实失去了他们的过滤器。我从最初想要使用的Bitmap的修改副本创建了一个新的BitmapDrawale。

答案 5 :(得分:0)

Drawable clone = drawable.mutate().getConstantState().newDrawable().mutate();

如果getConstantState()返回null

答案 6 :(得分:0)

使用newDrawable()获取可绘制的克隆,但是请确保它是可变的,否则您的克隆效果就消失了,我使用了以下几行代码,它可以按预期运行。 getConstantState()可能为注释建议的null,因此在克隆drawable时请处理此RunTimeException。

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}