Android笔记 自定义View(四):Canvas使用之绘制背景色

前面介绍了Canvas和Paint的相关概念,下面就详细看下它们是怎么使用的

目录

一、绘制背景颜色

1、PorterDuffxfermode、Xfermode和PorterDuff

Xfermode

PorterDuffXfermode

PorterDuff.Mode

二、总结


一、绘制背景颜色

Canvas绘制背景颜色常用有四个方法,具体看下面:

//设置单一颜色为Canvas的背景颜色。
drawColor(int color) 

/*
* 使用指定的颜色和模式填充Canvas。
*/
drawColor(int color, PorterDuff.Mode mode)

/*
* @param
*  a 在画布上绘制的透明度,取值范围(0..255). 
*  r 在画布上绘制的红色色值,取值范围(0..255). 
*  g 在画布上绘制的绿色色值,取值范围(0..255).  
*  b 在画布上绘制的蓝色色值,取值范围(0..255).  
* 使用指定的ARGB填充Canvas。
*/
drawARGB(int a, int r, int g, int b)

/*
* @param
*  r 在画布上绘制的红色色值,取值范围(0..255). 
*  g 在画布上绘制的绿色色值,取值范围(0..255).  
*  b 在画布上绘制的蓝色色值,取值范围(0..255).  
* 使用指定的RGB颜色,填充Canvas。
*/
public void drawRGB (int r, int g, int b)

上面的几个方法都很简单,注释都有说明。这里大坑就是PorterDuff.Mode这个东东,被它坑了很长时间。下面我来说一下自己的了解,不全之处请见谅。

1、PorterDuffxfermode、Xfermode和PorterDuff

查看PorterDuff类的注释文档:

/**
 * <p>This class contains the list of alpha compositing and blending modes
 * that can be passed to {@link PorterDuffXfermode}, a specialized implementation
 * of {@link Paint}'s {@link Paint#setXfermode(Xfermode) transfer mode}.
 * All the available modes can be found in the {@link Mode} enum.</p>
 */

PorterDuff是一种图像混合模式。就是将源像素和背景像素的颜色进行混合,最终显示的颜色取决于其RGB颜色分量和Alpha值。它通过Paint的setXfermode(Xfermode) 方法将值传递给PorterDuffXfermode,PorterDuffXfermode是Xfermode的实现类。我们先看相关源码:


/**
 * 设置或者清除过渡模式
 * xfermode 为Null时,恢复默认值
 */
public Xfermode setXfermode(Xfermode xfermode) {
    int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
    int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
    if (newMode != curMode) {
        nSetXfermode(mNativePaint, newMode);
    }
    mXfermode = xfermode;
    return xfermode;
}

Xfermode

public class Xfermode {
    static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
    int porterDuffMode = DEFAULT;
}

Xfermode主要是保存一个默认的值。它有三个实现类:AvoidXfermode, PixelXorXfermode以及PorterDuffXfermode。前两个类因为不支持硬件加速在API level 16被标记为Deprecated了,用也可以,但是需要关闭硬件加速。那么需要我们深入理解的就是PorterDuffXfermode类。

PorterDuffXfermode

public class PorterDuffXfermode extends Xfermode {
    public PorterDuffXfermode(PorterDuff.Mode mode) {
        porterDuffMode = mode.nativeInt;
    }
}

它的概念来自于1984年在ACM SIGGRAPH计算机图形学出版物上发表了“Compositing digital images(合成数字图像)”的Tomas Porter和Tom Duff,有兴趣的可以查阅下。在其构造方法里只有一个参数 PorterDuff.Mode,由它来指定图形合成时颜色值的计算方式。在Paint可以通过以下方式定义:

Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));

上面的操作实际上就是选择一个PorterDuff.Mode值进行赋值。在自定义View时,当给Paint的Xfermode赋值时,那么使用该Paint时,就使用了该模式。我们往下看:

PorterDuff.Mode

它将所绘制的图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,从而更新Canvas中最终的像素颜色值。首先我们看一下官方demo里的效果图:

这张图片从一定程度上形象地说明了图形混合的作用,两个图形一圆一方通过一定的计算产生不同的组合效果,在API中Android为我们提供了18种(比上图多了两种ADD和OVERLAY)模式: 

属性 说明 颜色
CLEAR 清理相应的颜色 [0, 0]
SRC 显示上层绘制图片 [Sa, Sc]
DST 显示下层绘制图片 [Da, Dc]
SRC_OVER 正常绘制显示,上下层绘制叠盖。(后者覆盖前者) [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc]
DST_OVER

上下层都显示。下层居上显示。

[Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc]
SRC_IN 取两层绘制交集。显示上层。 [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc]
DST_IN 取两层绘制交集。显示下层。 [Sa * Da, Sa * Dc]
SRC_OUT 取上层绘制非交集部分。 [Sa * (1 - Da), Sc * (1 - Da)]
DST_OUT 取下层绘制非交集部分。 [Da * (1 - Sa), Dc * (1 - Sa)]
SRC_ATOP 取下层非交集部分与上层交集部分 [Da, Sc * Da + (1 - Sa) * Dc]
DST_ATOP 取上层非交集部分与下层交集部分 [Sa, Sa * Dc + Sc * (1 - Da)]
XOR 异或:去除两图层交集部分 [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
DARKEN 取两图层全部区域,交集部分颜色加深 [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]
LIGHTEN 取两图层全部,点亮交集部分颜色 [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
MULTIPLY 取两图层交集部分叠加后颜色 [Sa * Da, Sc * Dc]
SCREEN 取两图层全部区域,交集部分变为透明色 [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
ADD   Saturate(S + D)
OVERLAY   Saturate(S + D)

注意:我按照上面的模式设置画笔的时候发现效果跟上面的效果天差地别,后来网上查找资料发现要实现官方demo效果图中的效果,要满足以下几个条件:

https://blog.csdn.net/wingichoy/article/details/50534175

1、关闭硬件加速。(或者设置为:LAYER_TYPE_HARDWARE)

2、只对两个都是bitmap才有效果。且两个bitmap大小尽量一样。

3、背景色为透明色。

仅仅上面几条得出的效果也可能不完全一样,实际效果要根据实际情况来看。下面看一下我自己实现的几种的效果,有兴趣的可以自己实现下。

public class CustomView extends View {

    Paint mDstPaint,mSrcPaint;
    Bitmap mSrcBitmap,mDstBitmap;
    Canvas mSrcCanvas,mDstCanvas;

    public CustomView(Context context) {
        this(context,null);
    }

    public CustomView(Context context, AttributeSet attrs) {
        this(context,attrs,0);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDstPaint = new Paint();
        mSrcPaint = new Paint();
        mDstPaint.setColor(Color.BLUE);
        mDstPaint.setAntiAlias(true);
        mSrcPaint.setColor(Color.YELLOW);
        mSrcPaint.setAntiAlias(true);
        //开启硬件离屏缓存:解决黑色问题,效率比关闭硬件加速高。暂时没有发现其他影响
        setLayerType(LAYER_TYPE_HARDWARE, null);

        //准备画布
        mSrcBitmap = Bitmap.createBitmap(150,150, Bitmap.Config.ARGB_8888);
        mSrcCanvas = new Canvas(mSrcBitmap);
        mDstBitmap =  Bitmap.createBitmap(150,150, Bitmap.Config.ARGB_8888);
        mDstCanvas = new Canvas(mDstBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //dst
        mDstCanvas.drawRect(50,50,150,150,mDstPaint);
        mSrcCanvas.drawCircle(50,50,50,mSrcPaint);

        //准备好两张位图后在设置画笔模式,然后将图片画上去
        mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawBitmap(mDstBitmap,0,0,mDstPaint);
        canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint);
    }
}

 

效果:

                    

二、总结

上面总结一些Canvas绘制背景色的方法,主要是颜色的混合模式使用得当会做出十分酷炫的效果。以一个我自己做的例子作为总结:

java代码:

public class LogoLoadingView extends View {
    
    private Paint paint;
    private Bitmap bitmap;
    private int currentTop;
    private RectF rectF;
    private PorterDuffXfermode xfermode;

    public LogoLoadingView(Context context) {
        super(context);
        init();
    }

    public LogoLoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init(){
        setLayerType(LAYER_TYPE_HARDWARE,null);
        paint=new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        //设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
        paint.setDither(true);
        //加快显示速度,本设置项依赖于dither和xfermode的设置
        paint.setFilterBitmap(true);
        bitmap= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        xfermode=new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
        currentTop=bitmap.getHeight();
        rectF=new RectF(0,currentTop,bitmap.getWidth(),bitmap.getHeight());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        rectF.top=currentTop;

        canvas.drawBitmap(bitmap,0,0,null);
        paint.setXfermode(xfermode);
        paint.setColor(Color.RED);
        canvas.drawRect(rectF,paint);
        paint.setXfermode(null);

        if (currentTop>0){
            currentTop--;
            invalidate();
        }
    }
}

xml代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    tools:context=".MainActivity">

    <com.wqd.app.view.LogoLoadingView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>

效果图:

祝:工作顺利!

 

 

原文链接:加载失败,请重新获取