Canvas绘图基础

2020-09-16  本文已影响0人  竖起大拇指

Canvas坐标系和绘图坐标系

Canvas绘图中牵扯到两种坐标系:Canvas坐标系与绘图坐标系。

/**
 * Created by maozonghong
 * on 2020/9/16
 */
class AxisView @JvmOverloads constructor(context: Context,attr:AttributeSet?) : View(context,attr) {

    var paint= Paint(Paint.ANTI_ALIAS_FLAG)

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        var canvasWidth=canvas?.width?:0
        var canvasHeight=canvas?.height?:0

        paint.style=Paint.Style.STROKE
        paint.strokeCap=Paint.Cap.ROUND
        paint.strokeWidth=6*context.resources.displayMetrics.density

        //黄色画X轴
        paint.color=-0x33bc
        //绘制X轴
        canvas?.drawLine(0f,0f,canvasWidth.toFloat(),0f,paint)

        //蓝色画Y轴
        paint.color=-0x995501
        //绘制Y轴
        canvas?.drawLine(0f,0f,0f,canvasHeight.toFloat(),paint)


        //对坐标系平移之后 第二次绘制坐标轴
        canvas?.translate(canvasWidth/4f,canvasWidth/4f) //坐标系右下角平移
        paint.color=-0x33bc
        //绘制X轴
        canvas?.drawLine(0f,0f,canvasWidth.toFloat(),0f,paint)
        paint.color=-0x995501
        //绘制Y轴
        canvas?.drawLine(0f,0f,0f,canvasHeight.toFloat(),paint)


        //再次平移坐标系并在此基础上旋转坐标系,第三次绘制坐标轴
        canvas?.translate(canvasWidth/4f,canvasWidth/4f) //在上次平移的基础上 再把坐标系向右下角平移
        //基于当前绘图坐标系的原点旋转
        canvas?.rotate(90f)

        paint.color=-0x33bc
        //绘制X轴
        canvas?.drawLine(0f,0f,canvasWidth.toFloat(),0f,paint)
        paint.color=-0x995501
        //绘制Y轴
        canvas?.drawLine(0f,0f,0f,canvasHeight.toFloat(),paint)


    }
}

效果如下所示:


device-2020-09-16-133843.png

说明:
第一次绘制绘图坐标系时,绘图坐标系默认情况下和Canvas坐标系重合,所以绘制出的坐标系紧贴View的上侧和左侧;
第二次首先将坐标轴向右下角平移了一段距离,然后绘制出的坐标系也就整体向右下角平移了;
第三次再次向右下角平移,并旋转了90°,即最后的绘图坐标系。

drawARGB

Canvas中的drawARGB可以用来对整个Canvas以某种统一的颜色整体绘制,四个参数分别时Alpha,Red,Green,Blue,取值都是0-255。使用代码如下:

override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        //设置背景色
        canvas?.drawARGB(255, 139, 197, 186)
}

效果如下:


device-2020-09-16-134743.png

drawText

Canvas中用drawText方法绘制文字,代码如下:

override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        paint.textSize=25f
        var textHeight=50f

        var canvasWidth=canvas?.width?:0
        var halfCanvasWidth=canvasWidth/2
        var translateY=textHeight

        //绘制正常文本
        canvas?.save()
        canvas?.translate(0f,translateY)
        canvas?.drawText("正常绘制文本",0f,0f,paint)
        canvas?.restore()

        translateY+=textHeight*2
        //设置字体颜色为蓝色
        paint.color=-0x995501

        canvas?.save()
        canvas?.translate(0f,translateY)
        canvas?.drawText("绘制蓝色文本",0f,0f,paint)
        canvas?.restore()

        paint.color= Color.BLACK

        translateY+=textHeight*2

        //设置左对齐
        paint.textAlign=Paint.Align.LEFT

        paint.strokeWidth=10f

        canvas?.save()
        canvas?.translate(halfCanvasWidth.toFloat(),translateY)
        canvas?.drawText("左对齐文本",0f,0f,paint)
        canvas?.restore()

        translateY+=textHeight*2

        //设置居中对齐
        paint.textAlign=Paint.Align.CENTER
        canvas?.save()
        canvas?.translate(halfCanvasWidth.toFloat(),translateY)
        canvas?.drawPoint(0f,0f,paint)
        canvas?.drawText("居中对齐文本",0f,0f,paint)
        canvas?.restore()

        translateY+=textHeight*2
        //设置向右对齐
        paint.textAlign=Paint.Align.RIGHT

        canvas?.save()
        canvas?.translate(halfCanvasWidth.toFloat(),translateY)
        canvas?.drawPoint(0f,0f,paint)
        canvas?.drawText("右对齐文本",0f,0f,paint)
        canvas?.restore()

        paint.textAlign=Paint.Align.LEFT //重新设置为左对齐

        translateY+=textHeight*2

        //设置下划线
        paint.isUnderlineText=true
        canvas?.save()
        canvas?.translate(0f,translateY)
        canvas?.drawText("下划线文本",0f,0f,paint)
        canvas?.restore()

        //重新设置为没有下划线
        paint.isUnderlineText=false
        translateY+=textHeight*2

        //绘制加粗文字
        paint.isFakeBoldText=true
        canvas?.save()
        canvas?.translate(0f,translateY)
        canvas?.drawText("粗体文本",0f,0f,paint)
        canvas?.restore()

        //重新将画笔设置为非粗体状态
        paint.isFakeBoldText=false

        translateY+=textHeight*2

        //文本绕绘制起点顺时针旋转
        canvas?.save()
        canvas?.translate(0f,translateY)
        canvas?.rotate(20f)
        canvas?.drawText("文本绕绘制起点旋转20°",0f,0f,paint)
        canvas?.restore()

    }

效果如下:


device-2020-09-16-143726.png

说明:

drawPoint

Canvas中用drawPoint方法绘制点,代码如下:

override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        var canvasWidth=canvas?.width?:0
        var canvasHeight=canvas?.height?:0
        var x=canvasWidth/2
        var deltaY=canvasHeight/3
        var y=deltaY/2

        paint.color=-0x995501
        paint.strokeWidth=50*context.resources.displayMetrics.density

        //绘制Cap为Butt的点
        paint.strokeCap=Paint.Cap.BUTT
        canvas?.drawPoint(x.toFloat(),y.toFloat(),paint)

        //绘制Cap为ROUND的点
        canvas?.translate(0f,deltaY.toFloat())
        paint.strokeCap=Paint.Cap.ROUND
        canvas?.drawPoint(x.toFloat(),y.toFloat(),paint)

        //绘制Cap为SQUARE的点
        canvas?.translate(0f,deltaY.toFloat())
        paint.strokeCap=Paint.Cap.SQUARE
        canvas?.drawPoint(x.toFloat(),y.toFloat(),paint)

    }

效果如下:


device-2020-09-16-150942.png

说明:

drawLine

Canvas通过drawLine方法绘制一条线,通过drawLines方法绘制多段线。

// 画一条直线
// 在坐标(100,200),(700,200)之间绘制一条直线
   canvas.drawLine(100,200,700,200,mPaint1);

// 绘制一组线
// 在坐标(400,500),(500,500)之间绘制直线1
// 在坐标(400,600),(500,600)之间绘制直线2
        canvas.drawLines(new float[]{
                400,500,500,500,
                400,600,500,600
        },mPaint2);
    }

drawRect

Canvas通过drawRect方法绘制矩形,使用代码如下:

override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        var canvasWidth=canvas?.width?:0
        var canvasHeight=canvas?.height?:0

        var left1=10f
        var top1=10f
        var right1=canvasWidth/3f
        var bottom1=canvasHeight/3f
        paint.color=-0x995501
        canvas?.drawRect(left1,top1,right1,bottom1,paint)

        paint.color=-0x33bc
        var left2=canvasWidth/3*2f
        var top2=10f
        var right2=canvasWidth-10f
        var bottom2=canvasHeight/3f

        canvas?.drawRect(left2,top2,right2,bottom2,paint)
    }

界面如下所示:

device-2020-09-16-152948.png

left和right表示矩形的左边和右边分别到绘图坐标系y轴正半轴的距离,top和bottom表示矩形的上边和下边分别到绘图坐标系x轴正半轴的距离。

drawCircle

Canvas中用drawCircle方法绘制圆形,使用代码如下:

  override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        paint.color=-0x995501
        //默认绘图为填充模式
        paint.style=Paint.Style.FILL

        var canvasWidth=canvas?.width?:0
        var canvasHeight=canvas?.height?:0

        var halfCanvasWidth=canvasWidth/2f

        var translateY=canvasHeight/4f
        var R=translateY/2f

        //绘制圆
        canvas?.translate(0f,translateY/4f)

        canvas?.drawCircle(halfCanvasWidth,R,R,paint)

        //绘制两个圆 形成圆环
        //1.首先绘制大圆
        canvas?.translate(0f,translateY+translateY/4f)
        canvas?.drawCircle(halfCanvasWidth,R,R,paint)
        //2.然后绘制小圆 让小圆覆盖大圆 形成圆环效果
        var r=R*0.75f

        paint.color= Color.WHITE
        canvas?.drawCircle(halfCanvasWidth,R,r,paint)


        //通过线条绘图模式绘制圆环
        canvas?.translate(0f,translateY+translateY/4f)
        paint.color=-0x995501
        paint.style=Paint.Style.STROKE
        var strokeWidth=R*0.25f
        paint.strokeWidth=strokeWidth
        canvas?.drawCircle(halfCanvasWidth,R,R,paint)
    }

效果如下:


device-2020-09-16-162106.png

代码说明:

drawOval

Canvas中提供了drawOval方法绘制椭圆,其使用代码如下所示 :

 override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        var canvasWidth=canvas?.width?:0

        var canvasHeight=canvas?.height?:0

        var quarter=canvasHeight/4f

        var density=context.resources.displayMetrics.density

        var left=10*density

        var top=0f

        var right=canvasWidth-left

        var bottom=quarter

        var rectF= RectF(left,top,right,bottom)

        //绘制椭圆形轮廓线
        paint.style=Paint.Style.STROKE
        paint.strokeWidth=2*density
        paint.color=-0x995501
        canvas?.translate(0f,quarter/4f)
        canvas?.drawOval(rectF,paint)

        //绘制椭圆形填充面
        paint.style=Paint.Style.FILL
        canvas?.translate(0f,quarter+quarter/4f)
        canvas?.drawOval(rectF,paint)

        //画两个椭圆,形成轮廓线和填充色不同的效果
        canvas?.translate(0f,quarter+quarter/4f)
        paint.style= Paint.Style.FILL
        canvas?.drawOval(rectF,paint)

        paint.style= Paint.Style.STROKE
        paint.color= Color.GREEN
        canvas?.drawOval(rectF,paint)

    }

效果如下所示:


device-2020-09-16-163829.png

下面对以上代码进行说明:

  1. 其方法签名是public void drawOval (RectF oval, Paint paint),RectF有四个字段,分别是left、top、right、bottom,
    这四个值对应了椭圆的左、上、右、下四个点到相应坐标轴的距离,具体来说,left和right表示椭圆的最左侧的点和最右侧的点到绘图坐标系的y轴的距离,top和bottom表示椭圆的最顶部的点和最底部的点到绘图坐标系的x轴的距离,这四个值就决定了椭圆的形状,right与left的差值即为椭圆的长轴,bottom与top的差值即为椭圆的短轴,如下图所示:


    这里写图片描述
  2. 通过Paint的setStyle方法将画笔的style设置成STROKE,即画线条模式,这种情况下,用画笔画出来的是椭圆的轮廓线,而非填充面,如上图中的第一个图形所示。

  3. 当将画笔Paint的style设置为FILL时,即填充模式,这种情况下,用画笔画出来的是椭圆的填充面,如上图中的第二个图形所示。

  4. 如果我们想绘制带有其他颜色轮廓线的椭圆面,我们需要绘制两个椭圆。首先以FILL模式画一个椭圆的填充面,然后更改画笔颜色,以STROKE模式画椭圆的轮廓线,如上图中的最后一个图形所示。这样从外观上看,好像是椭圆面与椭圆的轮廓线颜色不同。

drawArc

Canvas中提供了drawArc方法用于绘制弧,这里的弧指两种:弧面和弧线,弧面即用弧围成的填充面,弧线即为弧面的轮廓线。其使用代码如下:

override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        var density=context.resources.displayMetrics.density
        var canvasWidth=canvas?.width?:0
        var canvasHeight=canvas?.height?:0
        var ovalHeight=canvasHeight/6f
        var left=10*density
        var top=0f
        var right=canvasWidth-left
        var bottom=ovalHeight

        var rectF= RectF(left,top,right,bottom)

        //设置线宽
        paint.strokeWidth=2*density
        paint.color=-0x995501
        //默认设置画笔颜色为填充颜色
        paint.style=Paint.Style.FILL

        //绘制用drawArc绘制完整的椭圆
        canvas?.translate(0f,ovalHeight/5f)
        canvas?.drawArc(rectF,0f,360f,true,paint)

        //绘制椭圆的四分之一 起点是钟表的3点位置 从三点绘制到六点的位置
        canvas?.translate(0f,ovalHeight+ovalHeight/5f)
        canvas?.drawArc(rectF,0f,90f,true,paint)

        //绘制椭圆的四分之一,将userCenter设置为false
        canvas?.translate(0f,ovalHeight+ovalHeight/5f)
        canvas?.drawArc(rectF,0f,90f,false,paint)

        //绘制椭圆的四分之一 只绘制轮廓线
        paint.style=Paint.Style.STROKE
        canvas?.translate(0f,ovalHeight+ovalHeight/5f)
        canvas?.drawArc(rectF,0f,90f,true,paint)

        //绘制带有轮廓线的椭圆的四分之一
        //1.先绘制椭圆的填充部分
        paint.style= Paint.Style.FILL
        canvas?.translate(0f,ovalHeight+ovalHeight/5f)
        canvas?.drawArc(rectF,0f,90f,true,paint)
        //2.再绘制椭圆的轮廓线部分
        paint.style= Paint.Style.STROKE
        paint.color= Color.GREEN
        canvas?.drawArc(rectF,0f,90f,true,paint)


    }

效果如下:


device-2020-09-16-165828.png

说明:

public void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

oval是RecF类型的对象,其定义了椭圆的形状。 startAngle指的是绘制的起始角度,钟表的3点位置对应着0度,如果传入的startAngle小于0或者大于等于360,那么用startAngle对360进行取模后作为起始绘制角度。 sweepAngle指的是从startAngle开始沿着钟表的顺时针方向旋转扫过的角度。如果sweepAngle大于等于360,那么会绘制完整的椭圆弧。如果sweepAngle小于0,那么会用sweepAngle对360进行取模后作为扫过的角度。 useCenter是个boolean值,如果为true,表示在绘制完弧之后,用椭圆的中心点连接弧上的起点和终点以闭合弧;如果值为false,表示在绘制完弧之后,弧的起点和终点直接连接,不经过椭圆的中心点。

drawBitmap

Canvas中提供了drawBitmap方法用于绘制Bitmap,其使用代码如下所示:

 override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        //如果bitmap不存在,那么就不执行下面的绘制代码

        if (bitmap == null) {
            return
        }

        //直接完全绘制Bitmap

        canvas?.drawBitmap(bitmap, 0f, 0f, paint)

        //绘制Bitmap的一部分,并对其拉伸
        //srcRect定义了要绘制Bitmap的哪一部分

        val srcRect = Rect()
        srcRect.left = 0
        srcRect.right = bitmap.width
        srcRect.top = 0
        srcRect.bottom = (0.33f * bitmap.height).toInt()
        val radio= (srcRect.bottom - srcRect.top) / bitmap.width
        
        //dstRecF定义了要将绘制的Bitmap拉伸到哪里
        val dstRecF = RectF()
        dstRecF.left = 0f
        dstRecF.right = canvas?.width?.toFloat()?:0f
        dstRecF.top = bitmap.height.toFloat()
        val dstHeight = (dstRecF.right - dstRecF.left) * radio
        dstRecF.bottom = dstRecF.top + dstHeight
        canvas?.drawBitmap(bitmap, srcRect, dstRecF, paint)
    }

public void drawBitmap (Bitmap bitmap, float left, float top, Paint paint)

该方法除了传入bitmap对象外,还需要传入left和top,left和top组成了一个坐标,决定了在Canvas中从哪个地方绘制Bitmap。在我们的代码中,left和top都设置为0,所以我们就在Canvas的左上角绘制了bitmap。

public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)

该方法有两个功能:1.只绘制原有bitmap对象的一部分,2.还可以将要绘制的bitmap缩放到指定的区域。

上一篇 下一篇

猜你喜欢

热点阅读