在Android 4上打破了setColorFilter(),在Android 5上工作

时间:2015-02-25 11:27:03

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

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

要更改颜色,我在主视图的背景上使用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()的工作方式?

5 个答案:

答案 0 :(得分:6)

最终,问题似乎是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);
}

答案 1 :(得分:2)

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

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

有趣的琐事:在我的案例setColorFilter上,XML创建的TextView drawableLeft工作正常,但只有通过点击处理程序/延迟调用时才能正常工作。在onCreate / onResume等中调用时,没有任何事情发生。

答案 2 :(得分:1)

我在棒棒糖前有同样的问题,我解决了替换问题:

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

使用:

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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

为了在所有Android版本上为所需数量的不同状态绘制drawable,您可以使用此帮助程序类:

@Mock
SingleChronicleQueue chronicle;
@Mock
ExcerptAppender appender;
@Captor
ArgumentCaptor<WriteBytesMarshallable> argumentCaptorLambda;

Persister persister = new Persister();
@Test
public void shouldPersistByteMessage() throws IOException {
    persister.write(MESSAGEBYTES);

    verify(appender).writeBytes(argumentCaptorLambda.capture());
    WriteBytesMarshallable lastValue =  argumentCaptorLambda.getValue();

    //final byte[] persistedBytes = ?? how to get writeBuffer here ??
    //assertThat(readPersistedMessage(persistedBytes), is(MESSAGE));
}

}