【问题标题】:Android: Cloning a drawable in order to make a StateListDrawable with filtersAndroid:克隆可绘制对象以使用过滤器制作 StateListDrawable
【发布时间】:2011-12-20 05:39:12
【问题描述】:

我正在尝试制作一个通用框架函数,当按下/聚焦/选择/等时,使任何 Drawable 变得突出显示。

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

我的问题是我无法克隆可绘制对象并在应用过滤器的情况下创建一个单独的实例。这是我想要实现的目标:

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(),但没有帮助..

有什么想法吗?

更新:

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

【问题讨论】:

  • 您好,您找到解决可绘制对象丢失过滤器的方法了吗?我遇到了同样的问题:(我最终通过克隆位图并逐像素应用过滤器从源图像生成其他图像。是的,这效率低下,但我只处理了一堆小图像。跨度>
  • 我无法使用 StateListDrawable 解决它,但如果您没有使用 StateListDrawable 并且仍然丢失过滤器,请确保您的位图是可变的。有很好的相关问题:stackoverflow.com/questions/5499637/…,我还发现 LightingColorFilter 在 PorterDuff 失败的地方工作.. 喜欢这个 android :)
  • 这个链接上的一个很好的答案stackoverflow.com/questions/10889415/…
  • ImageView.setImageDrawable 触发了类似的副作用,由于接受了答案,我得以解决。
  • 我正在尝试做同样的事情,它以某种方式按预期工作,ColorFilter 没有丢失......不同之处在于我确实改变了drawable。

标签: android android-widget drawable


【解决方案1】:

尝试以下方法:

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

【讨论】:

  • 谢谢!这个方法似乎成功地克隆了一个drawable。我试图编写的函数虽然不起作用..似乎当一个可绘制对象插入到 StateList 中时,它会丢失它的过滤器:(
  • +1 帮助我修复了 MapView 中的一个非常奇怪的错误,在该错误中,在 AlertDialog 中重新使用 ItemizedOverlay 中的 Drawable 会使 ItemizedOverlay 在触发时移动。制作 Drawable 的新实例解决了这个问题。
  • 如果我们尝试使用 setAlpha 方法,请确保工作正常。在这种情况下,两个可绘制的更改位图。然后我得到第一个可绘制为:getResources().getDrawable(),第二个为:getResources().getDrawable().mutate()。
  • 非常感谢,它解决了我从 API Mapsforge 应用边界函数时遇到的问题。现在我可以在任何地方成功使用可绘制对象了!
  • @Flavio - 我用滤色器尝试了这个,但它为我的所有可绘制实例着色!结果看起来你必须使用.mutate()(见我的回答)。
【解决方案2】:

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

因此,如果您使用滤色器和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 如果您不使用 mutate 更改颜色的所有实例 - 您需要调用 mutate 才能仅更改克隆的颜色
  • 检查API ref(“使这个drawable可变。-返回这个drawable”)和source code(“返回这个”)。调用 mutate() 是必需的,但返回的实例是相同的。这不会创建克隆,只会更改可绘制实例的内部状态以允许对其进行修改而不影响同一可绘制实例的其他实例。
  • 嗯,我不知道这个问题,但这个答案正是我需要的...... tU
  • 那些是最好的链接,你给的参考
【解决方案3】:

这对我有用。

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

【讨论】:

  • 是的,我不知道为什么,但只有这种组合 newDrawable() 和 mutate() 对我有用,任何其他单个 mutate() 或单个 newDrawable() 对我都不起作用
【解决方案4】:

这是我的解决方案,基于 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));

【讨论】:

  • 也适合我!这是一个有趣的解决方案,谢谢!) android糟透了,这么糟糕的API不能正常工作:(
  • 我认为这是迄今为止解决 (StateListDrawable + BitmapDrawable) 中的错误的最佳解决方案!
【解决方案5】:

我回答了一个相关问题here

基本上,StateListDrawables 似乎确实失去了它们的过滤器。我从最初想使用的位图的更改副本创建了一个新的 BitmapDrawale。

【讨论】:

    【解决方案6】:

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

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

    【讨论】:

    • 这行得通,但你不需要改变它。
    【解决方案7】:
    Drawable clone = drawable.mutate().getConstantState().newDrawable().mutate();
    

    如果getConstantState() 返回null

    【讨论】:

      猜你喜欢
      • 2021-12-18
      • 2013-04-06
      • 2011-10-17
      • 2017-04-26
      • 2013-02-18
      • 2015-10-27
      • 1970-01-01
      • 2011-12-09
      • 2019-11-18
      相关资源
      最近更新 更多