自定义View的绘制

这是一篇总结性地文章,内容来自扔物线文章以及网易公开课,本文是自己对文章的总结

一、View的坐标系
每一个子View都有一个自己的坐标系,并且互不影响,坐标系的原点是View的左上角,遵循右正左负,下正上负
如这张图所示,在左上定点为画布的原点,圆心的左边是(300,300)

自定义View的绘制
文章图片
image.png 二、canvas的作用 1.canvas可以指定绘制的位置,以及绘制什么样的图层
canvas.drawColor() 在整个绘制层上绘制一层背景色
三、Paint负责绘制的风格 绘制的颜色,抗锯齿,绘制线条的方式,实心还是空心等等和风格相关的都是由paint来做的


Paint.setAntiAlias(boolean aa) 设置抗锯齿开关
Paint.setStyle(Paint.Style.STROKE) 画出图形的线条 不会填充整个图形
Paint.setStrokeCap(cap) 设置线条端点形状
Paint. setShader 设置着色器
Paint.setColorFilter 设置颜色的过滤效果 比如加滤镜
Paint.setStrokeJoin 设置线条拐角的方式
Paint.setDither(boolean dither) 加入抖动效果有规律地扰乱图像来让图像对于肉眼更加真实
Paint.setFilterBitmap(filterBitmap)优化图像放大后的效果,消除马赛克
Paint.setPathEffect 图像外围轮廓线的效果
Paint.setShadowLayer 给绘制的内容加一层阴影 BlurMaskFilter 模糊效果
1. Paint.setStrokeCap(cap)效果示意图

自定义View的绘制
文章图片
画笔圆角效果
2. Paint.setFilterBitmap(true)效果示意图
左边是未开启优化的,右边是开启优化的,可以看到少了许多马赛克的效果

自定义View的绘制
文章图片
设置双线性过滤
3. Paint.setStrokeJoin效果示意图

自定义View的绘制
文章图片
拐角的效果
4.使用着色器可以实现一种规律性的变化。着色器分为三类 线性渐变new LinearGradient,从中心进行辐射渐变RadialGradient,像雷达
一样扫描的渐变SweepGradient, 使用图像来图形上色 BitmapShader,将多个着色器组合起来使用的ComposeShader


线性的着色器示例

自定义View的绘制
文章图片
image.png
/** * 1.线性渲染,LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) * (x0,y0):渐变起始点坐标 * (x1,y1):渐变结束点坐标 * color0:渐变开始点颜色,16进制的颜色表示,必须要带有透明度 * color1:渐变结束颜色 * colors:渐变数组 * positions:位置数组,position的取值范围[0,1],作用是指定某个位置的颜色值,如果传null,渐变就线性变化。 * tile:用于指定控件区域大于指定的渐变区域时,空白区域的颜色填充方法 */ mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float[]{0.f,0.7f,1}, Shader.TileMode.REPEAT); mPaint.setShader(mShader); canvas.drawRect(0,0,1000,1000, mPaint);

环形的着色器示例

自定义View的绘制
文章图片
image.png
/* 环形渲染,RadialGradient(float centerX, float centerY, float radius, @ColorInt int colors[], @Nullable float stops[], TileMode tileMode) * centerX ,centerY:shader的中心坐标,开始渐变的坐标 * radius:渐变的半径 * centerColor,edgeColor:中心点渐变颜色,边界的渐变颜色 * colors:渐变颜色数组 * stoops:渐变位置数组,类似扫描渐变的positions数组,取值[0,1],中心点为0,半径到达位置为1.0f * tileMode:shader未覆盖以外的填充模式。 */ mShader = new RadialGradient(250, 250, 250, new int[]{Color.GREEN, Color.YELLOW, Color.RED}, null, Shader.TileMode.CLAMP); mPaint.setShader(mShader); canvas.drawCircle(250, 250, 250, mPaint);

位图的着色器示例

自定义View的绘制
文章图片
image.png
/** * 位图渲染,BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) * Bitmap:构造shader使用的bitmap * tileX:X轴方向的TileMode * tileY:Y轴方向的TileMode REPEAT, 绘制区域超过渲染区域的部分,重复排版 CLAMP, 绘制区域超过渲染区域的部分,会以最后一个像素拉伸排版 MIRROR, 绘制区域超过渲染区域的部分,镜像翻转排版 */ mShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR); mPaint.setShader(mShader); canvas.drawRect(0,0,500, 500, mPaint);

组合的着色器示例
/** * 组合渲染, * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, Xfermode mode) * ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, PorterDuff.Mode mode) * shaderA,shaderB:要混合的两种shader * Xfermode mode: 组合两种shader颜色的模式 * PorterDuff.Mode mode: 组合两种shader颜色的模式 */ BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600, new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP); mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY); mPaint.setShader(mShader); canvas.drawCircle(250, 250, 250, mPaint);

三、Canvas重点API
drawArc :画扇形
drawPath(Path,Paint):画自定义的图形
drawTextOnPath:沿着一趟线绘制文字
1.对于drawPath需要传入path对象。path对象又有几种属性。
addXXX 表示添加某图形
quadTo:贝塞尔曲线
moveTo:将圆点移动到指定的位置,不管画什么图形都是从当前位置开始画的,通过moveTo可以指定画笔起始的位置。
close() 自动封闭,假设画了三角形的两边后直接调用该方法,会自动将两点连接起来形成封闭。
setFillType(Path.FillType ft) 设置填充方式
2.drawText难点
1.drawText指定的x,y是要绘制的文字左下角的坐标,纵坐标y代表文字的基线

自定义View的绘制
文章图片
image.png
2.可以使用StaticLayout来实现绘制文字的自动换行

自定义View的绘制
文章图片
image.png
  1. setTypeface设置字体
  2. setStrikeThruText删除线setUnderlineText下划线
  3. setLetterSpacing字符的间距
  4. setFontFeatureSettings使用css的方式设置字体
  5. setTextAlign对其方式
  6. setTextLocale设置绘制文字的地域文字类型。大陆简体 台湾繁体
  7. getFontSpacing获取推荐的行距,可以在换行绘制时使用
canvas.drawText(texts[1], 100, 150 + paint.getFontSpacing, paint);

4.canvas的裁切
绘制的内容超出这个范围会被裁切掉,使用clipRect方法。clipPath
5.保存canvas的一次绘制状态
canvas.save, canvas.restore。几何变化一定要配合save和restore来配合使用。否则将后续绘制也受之前的影响
canvas的几何变换 canvas的几何变换代码执行顺序是倒序的,如果想要先旋转再平移,那么平移代码要写在最上面,再写旋转的代码。translate,scale,rotate,skew
6.canvas的平移
使用translate方法让中心点平移到某个位置,由于canvas的几何变换是反着的,所以如果要先平移到一个点,再移动回原点要先写移动回原点的代码,再写移动到其他位置的代码。
7.使用matris矩阵来变换
post..方法代表在前面插入,也就是变换代码是顺序执行的
per..方法是往后边的代码插入,也就是和canvas的变换顺序,会插入到其他变换代码的后面。
四、使用Camera来实现三维旋转 自定义View的绘制
文章图片
image.png 1.camera.save/restore/applytoCanvas
camera的操作是在图像的原点(0,0)进行的,因此使用camera变化是不对称的,如果想要在图形的中心点进行变化,需要配合canvas的translate方法将canvas移动到中心点进行变化,然后再移动回去。因为camera是不支持设置轴心的,轴心永远是(0,0点)。
2.使用setLocation来将相机的位置向后移动
相机后移投影的图像变小。一般情况不需要移动x,y 只需要移动z轴。因为相机是在z轴上。
camera.setLocation(0, 0, newZ);
**3.camera的旋转正方向
自定义View的绘制
文章图片
image.png View绘制的过程 1.新绘制的总是会覆盖之前绘制的内容
2.View的绘制过程是从drawBackground(不能重写),onDraw绘制主题,dispatchDraw绘制子view,drawForeground绘制前景色,而这些都是由draw方法触发的
3.如果是直接继承view,super.onDraw并没有什么用,因为onDraw本身就是空实现
4.ViewGroup在通过onDraw绘制完自身后,会通过onDispatchDraw方法绘制子View
5.ViewGroup默认会跳过draw方法而直接执行dispatchDraw,如果想让它绘制自身,使用setWillNotDraw(false)来执行完整的绘制流程

自定义View的绘制
文章图片
hencoder的图片 【自定义View的绘制】

    推荐阅读