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

tal*_*kol 87 android drawable android-widget

我正在尝试创建一个通用框架函数,使得任何Drawable在按下/聚焦/选择/等时都会突出显示.

我的函数接受一个Drawable并返回一个StateListDrawable,其中默认状态是Drawable本身,而状态for 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;
}
Run Code Online (Sandbox Code Playgroud)

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

有任何想法吗?

更新:

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

Fla*_*vio 154

请尝试以下方法:

Drawable clone = drawable.getConstantState().newDrawable();
Run Code Online (Sandbox Code Playgroud)

  • @Flavio - 我用彩色滤镜尝试了这个,但它为我的drawable的所有实例着色了!事实证明,你必须使用`.mutate()`(参见我的回答). (16认同)
  • 如果我们尝试使用setAlpha方法,请正确工作.在这种情况下,两个drawable更改位图.然后我得到第一个drawable:getResources().getDrawable(),第二个:getResources().getDrawable().mutate(). (8认同)
  • +1帮助我在MapView中修复一个非常奇怪的错误,其中在AlertDialog中重新使用ItemizedOverlay中的Drawable使ItemizedOverlay在触发时移动.制作Drawable的新实例解决了这个问题. (3认同)

Pet*_*tai 99

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

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

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

// To make a drawable use a separate constant state
drawable.mutate()
Run Code Online (Sandbox Code Playgroud)

有一个很好的解释,请看:

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

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

  • 检查[API ref](http://developer.android.com/reference/android/graphics/drawable/Drawable.html#mutate())("使这个可绘制的可变. - 返回此drawable")和[source代码(http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/4.2.2_r1.2-robolectric-0/android/graphics/drawable/BitmapDrawable.java#426 )("归还").调用mutate()是必需的,但返回的实例是相同的.这不会创建克隆,这只会更改可绘制实例的内部状态以允许修改它而不会影响同一drawable的其他实例. (2认同)

Yan*_* Bi 14

这对我有用.

Drawable clone = drawable.getConstantState().newDrawable().mutate();
Run Code Online (Sandbox Code Playgroud)


Mal*_*asz 10

这是我的解决方案,基于这个SO问题.

想法是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

Drawable drawable = new FastBitmapDrawable(bm);
imageView.setImageDrawable(new PressedEffectStateListDrawable(drawable, 0xFF33b5e5));
Run Code Online (Sandbox Code Playgroud)