【问题标题】:setColorFilter() broken on Android 4, working on Android 5setColorFilter() 在 Android 4 上损坏,在 Android 5 上工作
【发布时间】:2017-12-01 00:38:53
【问题描述】:

我正在尝试定期(每秒几次)在屏幕上闪烁不同的颜色。

要更改颜色,我在主视图的背景上使用Drawable.setColorFilter(int color, Mode mode)

  • myView.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.SRC);

出于调试目的,我添加了另一个使用View.setBackgroundColor(int color) 更改的视图。

问题setColorFilter() 调用在 Lollipop 上工作,但在以前的版本中被破坏(特别是 Nexus 7 v4. 4.4, Galaxy Nexus v4.2.1)。


我在Runnable 中调用颜色更改代码,该代码由Handler 定期触发。

处理程序在所有平台上都被调用(我可以看到由于调试setBackgroundColor() 调用而导致的背景变化)。

下面是颜色循环代码:

Handler mHandler;
RunnableOnTick thisRunnable;
View vDebug;
View vBroken;

class RunnableOnTick implements Runnable
{
    int backgroundColor;

    @Override
    public void run()
    {
        color = random.nextInt(2);

        switch (color)
        {
            case 0:
            {
                backgroundColor = Color.RED;
                break;
            }
            case 1:
            {
                backgroundColor = Color.GREEN;
                break;
            }
        }

        // this works on all platforms
        vDebug.setBackgroundColor(backgroundColor);

        // this works only on Lollipop
        vBroken.getBackground().setColorFilter(backgroundColor, PorterDuff.Mode.SRC);
        vBroken.invalidate();

        mHandler.postDelayed(thisRunnable, 100);
    }
}

我尝试了不同的 PorterDuff.Mode 值 - 仍然无法在 Android 4 上运行。

Android v4 和 v5 之间有什么不同会改变setColorFilter() 的工作方式?

【问题讨论】:

  • 您使用的是哪个Drawable?这可能取决于特定的实现。
  • @Dmitry 如果我没记错的话,我只是使用库存的绿色 android 启动器图标,因为这是我拼凑的概念验证。最后我用了另一种方法,几个月没看那个应用了。
  • 然后是BitmapDrawable。好吧,如果您不再拥有该应用程序,那么现在很难找到问题所在。我可以肯定地说颜色过滤器在 KitKat 中工作(你可以简单地尝试使用 ImageViewtint 属性 - 它由 ColorFilter 支持)
  • @Dmitry 我会看看我能拿出什么回购。我总是很想知道这个错误是否在我身上,一个经过良好测试的框架。还将测试提供的答案代码和评论。感谢您的意见。
  • @Richard 试试下面的方法

标签: android android-5.0-lollipop android-4.0-ice-cream-sandwich


【解决方案1】:

最终,问题似乎在于 KitKat 不支持在 Drawable 上使用 ColorFilter(或隐式 alpha),而 Drawable 又将位于 StateListDrawable 中。我的解决方案是使用相同的代码来构造复杂的 Drawable,然后将其渲染为简单的 BitMapDrawable:

 static Drawable createDrawable(Context context, int color, boolean disabled) {
OvalShape oShape = new OvalShape();
ShapeDrawable background = new ShapeDrawable(oShape);
background.getPaint().setColor(color);

ShapeDrawable shader = new ShapeDrawable(oShape);
shader.setShaderFactory(new ShapeDrawable.ShaderFactory() {
    @Override
    public Shader resize(int width, int height) {
        return new LinearGradient(0, 0, 0, height,
                new int[]{
                        Color.WHITE,
                        Color.GRAY,
                        Color.DKGRAY,
                        Color.BLACK
                }, null, Shader.TileMode.REPEAT);
    }
});

Drawable icon = ContextCompat.getDrawable(context, R.drawable.ic_chat_button).mutate();
icon.setColorFilter(context.getResources().getColor(R.color.control_tint_color), PorterDuff.Mode.SRC_IN);

Drawable layer = new LayerDrawable(new Drawable[]{ shader, background, icon });
layer.setAlpha(disabled ? 128 : 255);

// Note that on KitKat, setting a ColorFilter on a Drawable contained in a StateListDrawable
//  apparently doesn't work, although it does on later versions, so we have to render the colored
//  bitmap into a BitmapDrawable and then put that into the StateListDrawable
Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);

layer.setBounds(0, 0, layer.getIntrinsicWidth(), layer.getIntrinsicHeight());
layer.draw(canvas);

return new BitmapDrawable(context.getResources(), bitmap);
}

【讨论】:

  • 你对关于 KitKat 不支持它的声明有任何参考吗?
  • 不,我没有任何此类参考,但上述解决方案正在工作...标记我的答案..如果这有帮助..
  • 解决我的问题,不仅是 kitkat,而且我发现它不适用于 API
  • 这是我在 4.4 和 4.3.1 中仍然存在的问题,这两款都是三星设备。不幸的是,这个修复对我不起作用。
【解决方案2】:

AppCompat 中存在一个问题,我认为 API 21 下的化合物 Drawable 与此相关: https://code.google.com/p/android/issues/detail?id=191111

简单的解决方案不是使用 XML 中的可绘制对象,而是在代码中创建它们,然后应用 setColorFilter。这就是@Hardeep 解决方案奏效的原因。

有趣的琐事:在我的情况下,setColorFilter on XML-created TextView drawableLeft 工作得很好,但只有在通过点击处理程序调用/延迟时才可以。在onCreate / onResume 等中调用时,什么也没发生。

【讨论】:

  • 哈哈,感谢你有趣的琐事,花了将近 3 个小时寻找解决方案,在 OnClick 上发布了一个延迟的可运行文件
【解决方案3】:

我在使用棒棒糖之前遇到了同样的问题,我解决了替换问题:

vBroken.getBackground().setColorFilter(backgroundColor, PorterDuff.Mode.SRC);

与:

    Drawable d = vBroken.getBackground();
    d.setColorFilter(backgroundColor, PorterDuff.Mode.MULTIPLY);
    vBroken.setBackground(d);

【讨论】:

  • 完美解决,这个一定要选为接受的,谢谢@mengoni
【解决方案4】:

对我来说,ColorFilter 不会应用于 StateListDrawable 中的颜色项。

创建一个 Drawable 来表示该颜色并在 StateListDrawable 中使用该颜色,看到 setColorFilter 在我测试的棒棒糖之前的设备上工作。

作为旁注,我制作了一个纯白色 Drawable,以便在完全不透明的情况下应用着色颜色。

【讨论】:

  • 这是正确的答案。颜色项不能通过 setColorFilter 更改,应更改为任何可绘制对象,例如1px x 1px 可绘制项目
【解决方案5】:

你有没有直接这样尝试过?!

vBroken.getBackground().setColorFilter(Color.argb(255, 255, 255, 255),PorterDuff.Mode.MULTIPLY));

【讨论】:

    【解决方案6】:

    为了在所有 android 版本上为尽可能多的不同状态绘制可绘制对象,您可以使用这个帮助类:

    public class MagicDrawable extends StateListDrawable {
    
    private Map<Integer, Integer> stateColorsMap;
    private int defaultColor;
    
    public static MagicDrawable create(Drawable drawable, int defaultColor, int pressedColor, int selectedColor){
        Map<Integer, Integer> map = new HashMap();
        map.put(android.R.attr.state_selected, selectedColor);
        map.put(android.R.attr.state_pressed, pressedColor);
    
        MagicDrawable stateDrawable = new MagicDrawable(drawable, defaultColor, map);
        return stateDrawable;
    }
    
    public static MagicDrawable createSelected(Drawable drawable, int defaultColor, int selectedColor){
        Map<Integer, Integer> map = new HashMap();
        map.put(android.R.attr.state_selected, selectedColor);
    
        MagicDrawable stateDrawable = new MagicDrawable(drawable, defaultColor, map);
        return stateDrawable;
    }
    
    public static MagicDrawable createPressed(Drawable drawable, int defaultColor, int pressedColor){
        Map<Integer, Integer> map = new HashMap();
        map.put(android.R.attr.state_pressed, pressedColor);
    
        MagicDrawable stateDrawable = new MagicDrawable(drawable, defaultColor, map);
        return stateDrawable;
    }
    
    /**
     *  Create state list drawable programmatically - just pass drawable, default color of drawable, and Map of (state , color)
     * @param drawable resourse for icons
     * @param defaultColor color for normal state
     * @param stateColorsMap map of for ex. android.R.attr.state_pressed and ColorManager.someColor
     *
     *                        Map<Integer, Integer> map = new HashMap<>();
    map.put(android.R.attr.state_pressed, ColorManager.rose);
     */
    public MagicDrawable(Drawable drawable, int defaultColor, Map<Integer, Integer> stateColorsMap) {
        super();
        this.stateColorsMap = stateColorsMap;
        this.defaultColor = defaultColor;
    
        drawable.setColorFilter(defaultColor, PorterDuff.Mode.SRC_IN);
    
        if (stateColorsMap != null) {
            for (int state : stateColorsMap.keySet()) {
                addState(new int[]{state}, drawable);
            }
        }
    
        addState(new int[] {}, drawable);
    }
    
    @Override
    protected boolean onStateChange(int[] states) {
    
        if (stateColorsMap == null) {
            super.setColorFilter(defaultColor, PorterDuff.Mode.SRC_IN);
            return super.onStateChange(states);
        }
    
        boolean colorSet = false;
    
        for (int state : states) {
    
            for (int st : stateColorsMap.keySet()){
    
                if (state == st) {
                    super.setColorFilter(stateColorsMap.get(st), PorterDuff.Mode.SRC_IN);
                    colorSet = true;
                    break;
                }
            }
        }
    
        if (!colorSet) {
            super.setColorFilter(defaultColor, PorterDuff.Mode.SRC_IN);
        }
    
        return super.onStateChange(states);
    }
    
    @Override
    public boolean isStateful() {
        return true;
    }
    

    }

    【讨论】:

      【解决方案7】:
      /**
           * Tint / Colorise the Supplied {@code drawable} into another color of supplied {@code colorResId}
           * @param context
           * @param drawable
           * @param colorResId
           * @return
           */
          public Drawable tintThisDrawable(Context context ,Drawable drawable,@ColorRes int colorResId)
          {
              Resources res = context.getResources();
              int color = res.getColor(colorResId);
              if (drawable != null) {
                  drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
              }
              return drawable;
          }
      

      我记得这个函数在我的项目中的某个地方工作。请自行测试。

      【讨论】:

      • 这几乎就是他的回答中所写的内容
      • 好吧!现在我看到了,它处理的是drawables而不是颜色。如果他可以使用 Drawable 设置背景,这将起作用。
      • 这正是他正在做的事情。请在回答之前阅读问题。
      • 即使它有效 - 它仍然无法回答问题。
      • 我看到的一个区别是我尝试使用View.getBackground().setColorFilter()。也许那个电话和你所展示的有区别?我会在下周左右再次尝试调查。
      猜你喜欢
      • 1970-01-01
      • 2011-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-16
      相关资源
      最近更新 更多