最近有一个需求,父控件ondraw方法里面会调用同事一个复杂的绘制逻辑。然而最近需要把已绘制的内容根据UI的变化变色。于是打算使用安卓自带的PorterDuffXfermode混合模式的特性实现,毕竟使用GLShader实现显然过于overkill,rendererScript可能不兼容,也比较麻烦。
在这里,我只要使用DST_IN模式进行颜色替换即可,为何能实现颜色替换呢,可以观察其逻辑的符号表达:[Sa * Da, Sa * Dc],也就是说只要设置一个纯色不透明DST图层,那么SRC图层就会按照自己每个像素的不透明度乘以DST图层设定的颜色。
逻辑已通,上Demo代码:
- package com.example.demosAboutCanvas;
-
- import android.app.Activity;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapShader;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.PaintFlagsDrawFilter;
- import android.graphics.PorterDuff;
- import android.graphics.PorterDuffXfermode;
- import android.graphics.Shader;
- import android.os.Bundle;
- import android.util.AttributeSet;
- import android.view.View;
-
- import androidx.annotation.Nullable;
-
- /**演示已绘制到Canvas的内容如何进行后期变色**/
- public class ChangeColorAfterDraw extends Activity {
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(new TestView(this));
- }
-
- private class TestView extends View {
-
- public TestView(Context context) {
- super(context);
- setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
-
- public TestView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
-
- public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- //创建SRC图层,实际上是红色的字来的:
- Bitmap srcBmp = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas cvsFromText = new Canvas(srcBmp);
- Paint textPaint = new Paint();
- textPaint.setAntiAlias(true);
- textPaint.setColor(Color.RED);
- textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- textPaint.setTextSize(100);
- cvsFromText.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
- cvsFromText.drawText("Test", 0, 300, textPaint);
- //创建DST图层:
- Bitmap dstBmp = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
- Canvas cvsFromText2 = new Canvas(dstBmp);
- cvsFromText2.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
- cvsFromText2.drawColor(Color.GREEN);
- //先绘制换色用的DST图层(违背直觉,但这规则就是如此)
- canvas.drawBitmap(dstBmp, 0, 0, null);
- //再绘制实际内容SRC图层
- Paint blendPaint = new Paint();
- blendPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); //使用混合规则DST_IN, [Sa * Da, Sa * Dc],这样SRC图层不透明的地方就可以按透明度系数换成DST图层的颜色了。
- canvas.drawBitmap(srcBmp, 0, 0, blendPaint);
- }
- }
- }
可以发现里面有一段反直觉的代码,就是我先绘制了替换颜色用的DST图层的内容,再绘制了要被替换颜色的源图层SRC图层的内容,这就是为什么我说这个东西用起来反直觉的原因,绝大部分绘图引擎都是越先绘制的图层越成为SRC,而在这是相反的。最后在绘制的时候设定Paint的模式为DST_IN,即可实现已绘制的SRC内容换色的操作了。在这里原本SRC图层的Test字母为红色,而最终显色效果被替换为DST图层的绿色。当然,DST图层如果是更复杂的图片,那相当于只有显示文字的部分显示了图片,可以做多种艺术字的效果了。