Android:克隆可绘制对象以使StateListDrawable具有过滤器


90

我试图做一个通用的框架功能,使任何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时,它会丢失所有过滤器。


嗨,您找到了解决可绘制内容丢失过滤器的方法吗?我碰到同样的问题:(我最终通过克隆位图和应用过滤器逐像素生成的源图像其他图像是的,这是低效的,但我只是一堆处理一次小图片。
port443

我无法使用StateListDrawable解决它,但是如果您不使用StateListDrawable并且仍然丢失过滤器,请确保您的位图是可变的。有很好的相关问题:stackoverflow.com/questions/5499637/…,我也发现LightingColorFilter在PorterDuff失败的地方工作。.喜欢这个android :)
talkol 2011年

这个链接上的一个很好的答案stackoverflow.com/questions/10889415/…–
Alan

有一个类似的副作用,由触发ImageView.setImageDrawable,由于已接受答案,我得以解决。
Giulio Piancastelli 2013年

我正在尝试做同样的事情,并且它按预期的方式工作,ColorFilter并没有丢失……区别在于我确实对可绘制对象进行了变异。
亨利

Answers:


162

请尝试以下操作:

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

1
谢谢!此方法似乎成功克隆了可绘制对象。我尝试编写的功能虽然无法正常工作。似乎将可绘制对象插入到StateList中时,它会丢失其过滤器:(
talkol 2011年

3
+1可以帮助我修复MapView中一个非常奇怪的错误,即在触发AlertDialog中从ItemizedOverlay中重新使用Drawable时,触发了ItemizedOverlay的移动。制作Drawable的新实例解决了该问题。
kskjon 2011年

9
如果我们尝试使用setAlpha方法,请执行正确的工作。在这种情况下,两个可绘制的更改位图。然后我得到第一个可绘制对象:getResources()。getDrawable(),第二个得到:getResources()。getDrawable()。mutate()。
尤拉·辛卡列夫

非常感谢,它解决了我从API Mapsforge应用边界函数时遇到的问题。现在,我可以在任何地方成功使用可绘制对象了!
xarlymg89

17
@Flavio-我尝试过使用彩色滤光片,但是它为我的可绘制对象的所有实例着色!事实证明您必须使用它.mutate()(请参阅我的回答)。
Peter Ajtai

104

如果将过滤器/ etc应用于使用创建的可绘制对象, getConstantState().newDrawable()则该可绘制对象的所有实例也会更改,因为可绘制对象将constantState用作缓存!

因此,如果您使用滤色器和a为圆形着色newDrawable(),则将更改所有圆形的颜色。

如果要在不影响其他实例的情况下使此drawable可更新,则必须更改该现有常量状态。

// 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()


实际上mutate()确实返回了完全相同的实例,但是其内部状态已更改,因此应用滤色器不会影响其他实例。您可以查看并修正答案吗?
clemp6r 2014年

1
@ clemp6r,如果您不使用mutate颜色更改的所有实例-您需要调用mutate才能仅更改克隆的颜色
Peter Ajtai 2014年

2
检查API ref(“使此可绘制的变量可变。-返回此可绘制的变量”)和源代码(“ return this”)。需要调用mutate(),但是返回的实例是相同的。这不会创建克隆,只会更改可绘制实例的内部状态以允许对其进行修改,而不会影响同一可绘制实例的其他实例。
clemp6r 2014年

好吧,我不知道这个问题,但是这个答案确实满足了我的需要……tU
Evren Ozturk 2014年

1
这些是最好的链接,您已引用了这些链接
Ashok Varma,2015年

15

这对我有用。

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

是我不知道为什么,但仅此组合newDrawable()和发生变异()为我工作的任何其他单一发生变异()或单newDrawable()不为我正常工作
米哈尔Ziobro

12

基于这个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;
    }
}

用法:

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

也为我工作!多数民众赞成在一个有趣的解决方案,谢谢!)PS android很烂,这么糟糕,不能正常工作的API :(
安东·基泽玛

我认为这是迄今为止解决(StateListDrawable + BitmapDrawable)中的错误的最佳解决方案!
Xavier.S

1

在这里回答了一个相关的问题

基本上,似乎StateListDrawables确实丢失了它们的过滤器。我从原来想使用的位图的副本中创建了一个新的BitmapDrawale。


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

万一getConstantState()返回null


0

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

Drawable.ConstantState state = d.mutate().getConstantState();
if (state != null) {
    Drawable drawable = state.newDrawable().mutate();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.