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>

效果图:

祝:工作顺利!

 

 

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要在canvas绘制背景图,你可以使用`drawImage`方法,其中`image`参数是一个图像对象,可以是一个`image`或`canvas`对象。你可以使用`createPattern`方法来设置重复的背景模式。例如,你可以按以下方式设置背景图像: ```javascript var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); var img = new Image(); img.src = 'myBackgroundImage.png'; // 设置背景图像的源地址 img.onload = function() { var pattern = ctx.createPattern(img, 'repeat'); // 创建一个重复的背景图案 ctx.fillStyle = pattern; // 将背景图案作为填充样式 ctx.fillRect(0, 0, canvas.width, canvas.height); // 使用填充样式填充整个画布 } ``` 这段代码将在canvas中创建一个与画布大小相匹配的背景图案,使用`repeat`参数来指定图案的重复方式。你可以根据需要选择其他重复方式,如`repeat-x`、`repeat-y`或`no-repeat`。123 #### 引用[.reference_title] - *1* [canvas绘制图片、背景图【从0开始学习canvas—第4篇】](https://blog.csdn.net/weixin_43586120/article/details/104722771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *2* *3* [31 canvas使用图片及设置背景](https://blog.csdn.net/u014331138/article/details/109358423)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值