作为Android开发,必然在项目中或多或少的会用到Paint,那么你是否有想过Paint的主要职责是什么吗?其实,Paint类保存有关如何绘制几何图形,文本和位图的样式和颜色信息。
下面就来具体看一下,Paint有哪些可用设置或方法。
Paint的一些setter方法
由于Paint中保存了很多“画笔”的相关配置,那么就先从它的setter类型的方法看一下,都是有哪些可进行设置的项。
基础设置
-
setAntiAlias(boolean) —— 抗锯齿
表示是否打开抗锯齿。抗锯齿会根据特定的算法(插入特定像素,使不规则的图形边缘不会看起来有那么强的毛刺感),使绘制的不规则图形(如圆形、文字等)边缘看起来更平滑。在绘制棱角分明的图像(如矩形、位图等)时,是不需要打开抗锯齿的。
-
setStyle(Paint.Style) —— 设置填充方式
Paint.Style.STROKE
Paint.Style.FILL
Paint.Style.FILL_AND_STROKE
-
setShadowLayer —— 绘制内容时在内容底部加阴影
//radius 阴影的模糊范围 //dx dy 阴影的偏移量 //shadowColor 阴影颜色 setShadowLayer(float radius, float dx, float dy, int shadowColor)
-
setMaskFilter —— 绘制内容时在内容上面蒙一层阴影
setMaskFilter(MaskFilter maskfilter)
MaskFilter的现有子类:
BlurMaskFilter(float radius,//模糊半径 BlurMaskFilter.Blur style)//模糊类型
BlurMaskFilter.Blur.INNER//图像范围内加模糊
BlurMaskFilter.Blur.NORMAL//图像范围内外都加模糊
BlurMaskFilter.Blur.OUTER//图像留白,绘制外部模糊
BlurMaskFilter.Blur.SOLID//图像区域不受模糊影响,外部绘制模糊
EmbossMaskFilter(
float[] direction, //3个标量[x,y,z]的数组,指定光源的方向
float ambient, //环境光强度 0到1
float specular, //高光系数
float blurRadius //光线范围
)
-
setPathEffect(PathEffect) —— 绘制图形时轮廓效果(虚线、曲线、折线、波浪线等)
PathEffect的现有子类:
CornerPathEffect 拐角变圆角
DiscretePathEffect 随机偏离
DashPathEffect(float[] intervals, float phase) 虚线
intervals 指定了虚线的格式:数组中元素必须为偶数(最少是 2 个),按照「画线长度、空白长度、画线长度、空白长度」……的顺序排列
phase 是虚线的偏移量
PathDashPathEffect 可以指定线段形状的虚线
SumPathEffect(PathEffect first, PathEffect second) 组合效果,分别使用first和second绘制一遍
ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 组合效果,先应用innerpe绘制一遍,再用outerpe将innerpe绘制的结果转变一下
颜色
-
setAlpha —— 透明度
-
setARGB —— 设置颜色
-
setColor —— 设置颜色
-
setColorFilter(ColorFilter) —— 设置颜色过滤
-
setShader(Shader) —— 设置着色方案
LinearGradient —— 线性渐变色
(float x0, float y0, //线性起点
float x1, float y1, //线性终点
int color0, int color1, //起止颜色
Shader.TileMode tile) //范围外处理方案
Shader.TileMode有如下三种类型:
Shader.TileMode.CLAMP 端点之外延续端点处的颜色
Shader.TileMode.MIRROR 镜像模式
Shader.TileMode.REPEAT 重复模式
RadialGradient —— 辐射渐变
(float centerX, float centerY, //辐射中心点
float radius, //辐射半径
int centerColor, int edgeColor, //起止颜色
TileMode tileMode)
SweepGradient —— 扫描渐变
(float cx, float cy, //扫描中心
int color0, int color1)//起止颜色
BitmapShader —— Bitmap着色器
(Bitmap bitmap,//着色用的bitmap
Shader.TileMode tileX, //横向超出范围处理方案
Shader.TileMode tileY)//纵向产出范围处理方案
ComposeShader —— 混合着色器
(Shader shaderA, //着色方案A
Shader shaderB, //着色方案B
PorterDuff.Mode mode)//B(后绘制为源图像SRC)对A(已经绘制的为目标图像DST)的叠加模式,稍后会具体介绍
线条相关
-
setStrokeWidth(float) —— 线条粗细
-
setStrokeCap(Paint.Cap) —— 线冒
Paint.Cap.BUTT 平头,默认
Paint.Cap.ROUND 圆头
Paint.Cap.SQUARE 方头
-
setStrokeJoin(Paint.Join) —— 拐点处理方式
Paint.Join.MITER 尖角,默认
Paint.Join.ROUND 圆角
Paint.Join.BEVEL 平角
-
setStrokeMiter(float ) —— 对Paint.Join.MITER的补充,极端情况下尖角会非常长又尖,通过设置此值,可以削掉太长的尖。
文字
-
fakeBoldText(boolean) —— 文字粗体
-
setFontFeatureSettings(String) —— 绘制文字使用的CSS样式
-
setLetterSpacing(float) —— 字符间距
-
setLinearText(boolean) —— 是否打开线性文本标识
在Android中文本的绘制需要使用一个bitmap作为单个字符的缓存,既然是缓存必定要使用一定的空间,我们可以通过setLinearText (true)告诉Android我们不需要这样的文本缓存。
-
setStrikeThruText (boolean) —— 添加
删除线 -
setTextAlign(Paint.Align) —— 文字对齐方式
Paint.Align.LEFT 左对齐
Paint.Align.CENTER 居中
Paint.Align.RIGHT 右对齐
-
setTextLocale(Locale) —— 设置地区(Locale.getDefault使用默认地区)
-
setTextScaleX(float) —— 文字横向缩放因子
-
setTextSize(float pixel) —— 文字大小
-
setTextSkewX(float) —— 文字横向错切因子
-
setTypeface(Typeface) —— 设置字体
-
setUnderlineText(boolean) —— 设置下划线
-
setWordSpacing(float pixel) —— 设置词间距,默认0
-
setSubpixelText(boolean) —— true,有助于LCD文字显示
Bitmap相关
-
setDither(boolena) —— 是否开启抖动
所谓抖动,是指把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。
-
setFilterBitmap(boolean) —— 是否使用双线性过滤来绘制 Bitmap
图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。
绘制重叠
- setXfermode(Xfermode)
Xfermode的唯一子类是PorterDuffXfermode(PorterDuff.Mode),在初始化时需要传入PorterDuff.Mode
-
setBlendMode(BlendMode)
在setXfermode的基础上,增加了几种混合模式
合成规则分两大类
蓝色为SRC(后绘制),红色为DST(已经绘制)
Alpha合成模式:
混合模式:
BlendMode新增的几种方式:
Paint的一些其它方法
文字尺寸相关
-
getFontMetrics(FontMetrics)/getFontMetricsInt(FontMetricsInt)
获取Paint(而非具体文字)的FontMetrics。
-
getFontSpacing
获取推荐的行距。
-
getTextBounds(String text, int start, int end, Rect bounds)
获取文字的显示(能看见的)范围。
-
float measureText(String text)
测量文字占用(包含视觉上看不见)的宽度。
-
getTextWidths(String text, float[] widths)
获取字符串中每个字符的宽度,并把结果填入参数 widths。
-
int breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth)
在给出宽度上限的前提下测量文字的宽度。如果文字的宽度超出了上限,那么在临近超限的位置截断文字。
-
ascent
当前字体和字号下,Paint的ascent。
-
descent
当前字体和字号下,Paint的descent。
光标相关
-
getRunAdvance
对于一段文字,计算出某个字符处光标的 x 坐标。
-
getOffsetForAdvance
给出一个位置的像素值,计算出文字中最接近这个位置的字符偏移量(即第几个字符最接近这个坐标)。
Path相关
-
getFillPath(Path src, Path dst)
默认情况下(线条宽度为0、没有PathEffect),那么获取到的path即为绘制的path(drawPath);其他情况下path跟源path会不一致:
-
getTextPath
获取目标文字对应的path。
关于FontMetircs
FontMetrics 是个相对专业的工具类,它提供了几个文字排印方面的数值:ascent, descent, top, bottom, leading。
图中有五条线,还有一个特殊的leading:
-
top/bottom:限制所有字形的顶部和底部范围;除了普通字符,有些字形的显示范围是会超过 ascent 和 descent 的,而 top 和 bottom 则限制的是所有字形的显示范围,包括这些特殊字形。由于是针对baseline的位移,所以top为负值,bottom为正值。
-
ascent/descent:限制普通字符的顶部和底部范围;普通的字符,上不会高过 ascent ,下不会低过 descent。 由于是针对baseline的位移,所以ascent为负值,descent为正值。
-
baseline:文字绘制的基线。
-
leading:上下相邻的两行,上行的 bottom 线和下行的 top 线的距离。
绘制文字时的纵向居中
在使用Canvas绘制文字时,使用方法:
//text 是文字内容,x 和 y 是文字的坐标
drawText(String text, float x, float y, Paint paint)
但是这里指定的y坐标为baseline的位置,所以如果想要文字根据某个y值纵向居中,那么就需要将baseline的位置适当下移,可以通过如下方式来计算需要下移的值:
//核心思想:计算出baseline需要偏移的距离,已使文字中心点在y值
//1.找到中心点位置(必定在baseline上方)
//2.中心点距离baseline有一个位移值(由于在baseline上方,所以是负值)
//3.绘制时移动这么一个位移值(减去负值即加正值,向下移动)
//方法1 不在乎绘制的文字是什么,统一方案居中,如果绘制内容为aaaa,那么会显得考下,因为aaaa本身高度就比较低
paint.getFontMetrics(fontMetrics)
deltY = (fontMetrics.ascent + fontMetrics.top)/2
//方法2 根据当前绘制的文字,精确的根据具体文字进行居中处理,效果会更好
paint.getTextBounds("Crazy Coder",0,"Crazy Coder".length(),rect)
deltY = (rect.top + rect.bottom) / 2
//那么纵向居中绘制时,就应该是这样的,减去负值
drawText("Crazy Coder",x,y - deltY,paint)
绘制文字时严格靠齐x坐标
在通过drawText绘制文字时,设置了x坐标,但是实际绘制之后的效果,其实并不是从x开始绘制文字,而是这样的效果:
可以看出,起始点(红点)在第一个字母H的前面一点,产生这种情况的原因是因为,每个字符都有左右边距,而且设置的字体越大,那么这个边距也就越大。
本身绘制文字时,文字占据的空间:
蓝色为本身占据的空间,橙色为getTextBounds获取到的空间。
那么关于横向绝对定义,可以通过如下方法处理:
paint.getTextBounds("Crazy Coder",0,"Crazy Coder".length(),rect)
deltX = rect.left
//横向绝对对齐,就应该是这样的
drawText("Crazy Coder",x - deltX,y,paint)
绘制文字时换行的处理
-
StaticLayout
StaticLayout 并不是一个 View 或者 ViewGroup ,而是 android.text.Layout 的子类,它是纯粹用来绘制文字的。 StaticLayout 支持换行,它既可以为文字设置宽度上限来让文字自动换行,也会在 \n 处主动换行。
它的构造方法:
StaticLayout( CharSequence source, //要绘制的文本 TextPaint paint, //绘制文本的画笔 int width, //宽度限制 Layout.Alignment align, //文字对齐方向 float spacingmult, //行间距的倍数,通常为1 float spacingadd, //行间距的额外增加值,通常为0 boolean includepad//是否在文字上下添加额外的空间,来避免某些过高的字符的绘制出现越界 )
-
Paint.breakText 实现图文混排
使用此方法的核心在于:
1.根据当前行可用宽度widthMax,通过breakText获知该行可以绘制的文字个数count
int count = paint.breakText( "Crazy Coder Crazy Coder Crazy Coder Crazy Coder..." true, widthMax, cutWidth )
2.绘制当前行文字,起始文字角标为上一行末尾文字角标index,终止角标为index + count
3.绘制当前行文字时,需要计算好当前行的y坐标,每绘制一行,即currentY = currentY + piant.getFontSpace() * (lineCount - 1)
canvas.drawText( "Crazy Coder Crazy Coder Crazy Coder Crazy Coder..." index, index + count, startX, currentY, paint )