AndroidBaseKnowledge-Paint

绘制文字

首先我们可以看下基础的文字绘制的例子,这里我们用到的API有以下几个

setTextSize:设置文字

setColor:设置文字颜色

setTextScaleX:设置文字水平方向上的缩放程度

setTextSkewX:设置文字水平方向上的倾斜程度

setTypeface(Typeface typeface):设置文字字体

setFakeBlodText:设置文字是否为粗体

setStrikeThruText:给文字添加删除线

setUnderlineText:给文字添加下划线

setLetterSpacing:设置字符之间的间距

image-20201227213626218.png

参考代码片段如下:

//设置画笔字体大小为45
mTextPaint.textSize = 45f
//设置画笔色彩为黑色
mTextPaint.color = Color.BLACK
//设置画笔的字体为Typeface.SERIF
mTextPaint.typeface = Typeface.SERIF

//绘制普通的文本,在当前view宽度的1/10, 高度的1/5处开始绘制文字
canvas?.drawText(resources.getString(R.string.android_knowledge_canvas_draw_text), (measuredWidth / 10).toFloat(), (measuredHeight / 5).toFloat(), mTextPaint)

//调整画笔的横向缩放为扩大为原来的两倍
mTextPaint.textScaleX = 2f

//调整画笔的横向缩放为缩小为原来的0.8倍
mTextPaint.textScaleX = 0.8f

//将横向缩放恢复为默认状态
mTextPaint.textScaleX = 1f
//调整画笔的水平方向倾斜度为向右侧倾斜
mTextPaint.textSkewX = -1f

//调整画笔的水平方向倾斜度为向左侧倾斜
mTextPaint.textSkewX = 1f

//回正文字
mTextPaint.textSkewX = 0f
//使用粗体
mTextPaint.isFakeBoldText = true

//取消使用粗体
mTextPaint.isFakeBoldText = false
//给文字添加删除线
mTextPaint.isStrikeThruText = true

//给文字添加下划线
mTextPaint.isUnderlineText = true

//扩大文字间距
mTextPaint.letterSpacing = 1.2f

//缩小文字间距
mTextPaint.letterSpacing = 0.8f

setTextAlign

  • Align用于在绘制文本的时候表示对齐方式,主要有三种形式
    • LEFT
    • CENTER
    • RIGHT

image-20201229002353764.png

绘制图形

setStyle

Paint的style有三种

  • FILL 填充模式
  • STROKE 画线模式
  • FILL_AND_STROKE 填充+画线模式

他们的效果是这样的

image-20210103135721832.png

mCanvasPaint.style = Paint.Style.FILL

mCanvasPaint.style = Paint.Style.STROKE

mCanvasPaint.style = Paint.Style.FILL_AND_STROKE

看起来FILL 和 FILL_AND_STROKE没有什么区别,不着急,我们当前的strokeWidth放大看下就知道了

mCanvasPaint.strokeWidth = 80f

image-20210103140101834.png

这个时候就能看出区别了,FILL_AND_STROKE样式绘制出来的圆大小等于FILL + STROKE的大小

setAlpha(设置画笔透明度)

alpha的值取值范围是在0~255之间,0为完全透明,255为完全不透明,我们先画一个纯黑色的圆,之后我们可以通过以下代码设置alpha之后再画一个圆

mCanvasPaint.alpha = 25

image-20210109154739650

可以看到两个圆的颜色已经有了明显差异

setStrokeWidth(设置画笔宽度)

我们可以通过以下代码绘制一条直线

mCanvasPaint.color = Color.BLACK
mCanvasPaint.strokeWidth = 10f

canvas?.drawLine(measuredWidth / 10f, 60f, measuredWidth * 9 / 10f, 60f, mCanvasPaint)

image-20210102201155558.png

问:如果我们给画笔设置的strokeWidth为0会有什么效果呢?

答:在画笔宽度为0的情况下(hairline mode),画笔也可以绘制出内容,只是绘制的内容始终是1像素,不受画布缩放的影响

举例:在上面的例子中我们设置了画笔宽度为10像素,那么画布放大1倍,10像素就会变成20像素。为了更直观的看到这个变化,我们可以用一个绘圆的例子来看下

//画笔放大一倍
canvas?.drawCircle(measuredWidth / 6f, 210f, 100f, mCanvasPaint)
canvas?.scale(2f, 2f)
canvas?.drawCircle(measuredWidth / 6f, 210f, 100f, mCanvasPaint)

绘制出的图像就是这样的,右下角的圆就是画布的x、y均被放大了一倍之后的效果,可以看到随着画布的放大,绘制出的圆边缘也变宽了

image-20210102202443864.png

而如果我们设置的画笔strokeWidth为0的话,绘制出来的效果是这样的:

mCanvasPaint.strokeWidth = 0f

//画笔放大一倍
canvas?.drawCircle(measuredWidth / 6f, 210f, 100f, mCanvasPaint)
canvas?.scale(2f, 2f)
canvas?.drawCircle(measuredWidth / 6f, 210f, 100f, mCanvasPaint)

image-20210102202840284.png

差异是很明显的

setStrokeCap(设置画笔在离开画板的时候最后一点的图形)

  • BUTT(默认为此模式)

  • ROUND

  • SQUARE

通过代码我们绘制出四条直线,来比较下:

mCanvasPaint.strokeWidth = 50f

mCanvasPaint.strokeCap = Paint.Cap.BUTT;
canvas?.drawLine(measuredWidth / 6f, 420f, measuredWidth - measuredWidth / 6f, 420f, mCanvasPaint)

mCanvasPaint.strokeCap = Paint.Cap.ROUND;
canvas?.drawLine(measuredWidth / 6f, 480f, measuredWidth - measuredWidth / 6f, 480f, mCanvasPaint)

mCanvasPaint.strokeCap = Paint.Cap.SQUARE;
canvas?.drawLine(measuredWidth / 6f, 540f, measuredWidth - measuredWidth / 6f, 540f, mCanvasPaint)

image-20210102205105616.png

setStrokeJoin

setStrokeJoin表示图形连接处的样式,有锐角、圆形、斜面三种,通过setStrokeJoin方法设置

  • MITER
  • ROUND
  • BEVEL

我们可以用一个例子直观的看到这三种style的样子

mCanvasPaint.strokeWidth = 10f

mImagePath.moveTo(measuredWidth / 8f, measuredHeight / 10f)
mImagePath.lineTo(measuredWidth / 4f, measuredHeight - measuredHeight / 8f)

mCanvasPaint.strokeJoin = Paint.Join.MITER
mImagePath.lineTo(measuredWidth / 2f, measuredHeight / 8f)
canvas!!.drawPath(mImagePath, mCanvasPaint)

mCanvasPaint.strokeJoin = Paint.Join.ROUND
mImagePath.lineTo(measuredWidth * 3 / 4f, measuredHeight - measuredHeight / 8f)
canvas.drawPath(mImagePath, mCanvasPaint)

mCanvasPaint.strokeJoin = Paint.Join.BEVEL
mImagePath.lineTo(measuredWidth * 7 / 8f, measuredHeight / 8f)
canvas.drawPath(mImagePath, mCanvasPaint)

image-20210102212257962.png

setAntiAlias 抗锯齿功能

抗锯齿的详细定义:https://zh.wikipedia.org/wiki/%E5%8F%8D%E9%8B%B8%E9%BD%92

抗锯齿开关状态下效果对比,可以看下图:

image-20210103141142151.png

明显的左边没有开启抗锯齿的圆毛边要比右边开了抗锯齿的圆多

setColorFilter

setColorFilter 用于给画笔设置不同的滤色器,我们可以通过设置不同的滤色器从而获得惊艳的效果

ColorMatrixColorFilter

ColorMatrix是一个*54的矩阵**,默认的矩阵为:

[ 1 0 0 0 0 - red vector
0 1 0 0 0 - green vector
0 0 1 0 0 - blue vector
0 0 0 1 0 ] - alpha vector

这个矩阵和RGBA之间的转化关系为:

R = aR + bG + cB + dA + e;

G = fR + gG + hB + iA + j;

B = kR + lG + mB + nA + o;

A = pR + qG + rB + sA + t;

最后一列表示偏移量,可以通过函数ColorMatrix.setScale函数来控制各个颜色分量的比重

举个例子:通过设置ColorFilter来过滤掉颜色

//设置画笔颜色
mCanvasPaint.color = Color.argb(255, 255, 128, 103)

//过滤掉红色
colorMatrix.setScale(0f, 1f, 1f, 1f)
//仅保留红色
colorMatrix.setScale(1f, 0f, 0f, 1f)

image-20210103120511045.png

LightingColorFilter

LightingColorFilter和ColorMatrixColorFilter原理都是通过控制RGBA 的数值实现滤镜的效果。

ColorMatrixColorFilter 是分别操作颜色的各个通道,而LightingColorFilter 则是直接传入两个RGBA的色彩参数

如果我们用R’G’B’表示过滤后的颜色,RGB代表原始颜色,两者的转化关系是这样的:

R’ = R * colorMultiply.R + colorAdd.R

G’ = G * colorMultiply.G + colorAdd.G

B’ = B * colorMultiply.B + colorAdd.B

举个例子:

//设置画笔颜色
mCanvasPaint.color = Color.argb(255, 128, 128, 103)

//R通道修改为255
mCanvasPaint.colorFilter = LightingColorFilter(-0xff0001, 0x00FF0000)
//R通道修改为0
mCanvasPaint.colorFilter = LightingColorFilter(-0xff0001, 0x00000000)

我们用三种颜色绘制出三个圆就可以看到三种颜色的区别

image-20210103121735534.png

setMaskFilter

BlurMaskFilter

BlurMaskFilter为模糊过滤器,可以在图形边缘添加一些毛玻璃效果。可以设置的模糊类型有

  • NORMAL
  • SOLID
  • OUTER
  • INNER

这个API不支持硬件加速,需要设置setLayerType(LAYER_TYPE_SOFTWARE, null)

image-20210103131508482.png

EmbossMaskFilter (已经deprecated)

EmbossMaskFilter用于设置浮雕效果,其原理是模拟光照效果,通过颜色的亮暗营造出浮雕的3D立体效果。

EmbossMaskFilter的构造函数中有四个参数用于控制效果

  • direction 光源方向(float数组)
  • ambient 背景光(0~1)
  • specular 镜面反射系数
  • blurRadius 模糊半径

这个API不支持硬件加速,需要设置setLayerType(LAYER_TYPE_SOFTWARE, null)

目前这个类已经被Google 标记为了deprecated。

image-20210109170511481.png

mCanvasPaint.color = Color.YELLOW
mCanvasPaint.maskFilter = EmbossMaskFilter(floatArrayOf(1f, 1f, 1f), 0.1f, 8f, 3f)
canvas?.drawCircle(
(measuredWidth / 3 / 2).toFloat(),
(measuredHeight / 2).toFloat(), 200f, mCanvasPaint
)

mCanvasPaint.maskFilter = EmbossMaskFilter(floatArrayOf(1f, 1f, -1f), 0.1f, 8f, 3f)
canvas?.drawCircle(
(measuredWidth * 3 / 3 / 2).toFloat(),
(measuredHeight / 2).toFloat(), 200f, mCanvasPaint
)

mCanvasPaint.maskFilter = EmbossMaskFilter(floatArrayOf(0f, 0f, 1f), 0.1f, 8f, 3f)
canvas?.drawCircle(
(measuredWidth * 5 / 3 / 2).toFloat(),
(measuredHeight / 2).toFloat(), 200f, mCanvasPaint
)