效果如下:
绘制路径
//根据 path 绘制路径
void drawPath(@NonNull Path path, @NonNull Paint paint)
/**
- 1. 绘制路径线
*/
var path = Path()
//1. 设置起始点
path.moveTo(100f, 100f)
//2. 第二条线的起点就是moveTo 设置的启动
path.lineTo(100f,300f)
//3. 第三条线的起点就是第二条的终点,依次类推
path.lineTo(300f,500f)
path.lineTo(500f,200f)
//4. 闭合
path.close()
canvas.drawPath(path, mPaint)
/**
- 2. 绘制弧度路径
*/
var path2 = Path()
//绘制弧度的起始位置
path2.moveTo(100f,600f)
var rectF = RectF(100f,600f,600f,1000f)
//第一个参数生成椭圆的矩形,第二个参数是弧开始的角度以 X 轴正方向为 0 度,第三个参数是弧持续的角度
path2.arcTo(rectF,0f,90f)
canvas.drawPath(path2, mPaint)
上面注释很详细,就不在解释了
绘制圆/椭圆
//绘制椭圆
void drawOval(@NonNull RectF oval, @NonNull Paint paint)
//绘制圆
void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
/**
- 1. 绘制椭圆
*/
canvas.drawOval(RectF(100f,500f,600f,800f),mPaint)
/**
- 2. 绘制圆
*/
mPaint.setColor(Color.YELLOW)
mPaint.alpha = 100
canvas.drawCircle(400f,400f,200f,mPaint)
绘制 Bitmap
//
val bitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.gild_3)
//第二个,第三个参数代表起点位置
canvas.drawBitmap(bitmap,100f,100f,mPaint)
绘制 Text
//1.
void drawText(@NonNull char[] text, int index, int count, float x, float y,
@NonNull Paint paint)
//2.
void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)
//3.
void drawText(@NonNull String text, int start, int end, float x, float y,
@NonNull Paint paint)
//4.
void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
@NonNull Paint paint)
/**
-
- 取 0 ~ 5 位置的 text 进行绘制
*/
mPaint.textSize = 100f
canvas.drawText(charArrayOf(‘1’,‘2’,‘3’,‘4’,‘5’),0,5,200f,200f,mPaint)
/**
*/
canvas.drawText(“12345”,300f,300f,mPaint)
/**
- 3.取 0 ~ 5 位置的 text 进行绘制
*/
canvas.drawText(“12345”,0,5,400f,400f,mPaint)
根据路径绘制 Text
//从 hOffset 向外偏移 vOffset px 来绘制 text
void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
float vOffset, @NonNull Paint paint)
复制代码
mPaint.setColor(Color.GREEN)
mPaint.alpha = 100
mPaint.textSize = 100f
var path = Path()
//1. 设置起始点
path.moveTo(300f, 300f)
//2. 第二条线的起点就是moveTo 设置的启动
path.lineTo(300f,500f)
//3. 第三条线的起点就是第二条的终点,依次类推
path.lineTo(500f,800f)
path.lineTo(800f,200f)
//4. 闭合
path.close()
canvas.drawPath(path,mPaint)
//从0偏移100px的像素
canvas.drawTextOnPath(“12345asodnaspdnfpoashfeuinfapjn”,path,0f,100f,mPaint)
绘制 弧/扇形
//1.
void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
@NonNull Paint paint)
//2.
void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint)
var rectF = RectF(100f, 100f, 500f, 500f)
/**
- 1. 绘制弧
- @param ovar : 矩形坐标
- @param startAngle : 开始的角度
- @param sweepAngle : 结束的角度
- @param userCenter : 如果为true,则将椭圆的中心包括在圆弧中
- @param paint : 画笔
*/
canvas.drawArc(rectF, 0f, 90f, true, mPaint)
/**
- 2. 绘制弧
*/
canvas.drawArc(100f,500f,500f,900f,0f,90f,false,mPaint)
绘制圆角矩形
//1.
void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)
//2.
void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
@NonNull Paint paint)
/**
- 1. 根据 RectF 绘制圆角矩形
- @param rx: x 轴上的圆角半径
- @param ry: y 轴上的圆角半径
/
canvas.drawRoundRect(rectF,50f,50f,mPaint)
/* - 2. 根据输入矩形位置绘制圆角矩形
*/
canvas.drawRoundRect(100f,600f,500f,900f,100f,100f,mPaint)
Canvas 变换
translate - 图层平移
//dx/dy:x/y 点新位置
void translate(float dx, float dy)
scale- 图层缩小 0~1
//x,y 缩小系数 0~1 之间 越大说明越接近原始图像
void scale(float sx, float sy)
复制代码
/**
- 1. 原始矩形
*/
mPaint.color = Color.RED
mPaint.alpha = 100
canvas.drawRoundRect(rectF,50f,50f,mPaint)
/**
- 2.将原始图形缩小 0.5 倍
*/
var rectF2 = RectF(100f, 100f, 500f, 500f)
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.scale(0.5f,0.5f)
canvas.drawRoundRect(rectF2,50f,50f,mPaint)
rotate - 图层旋转
//1.
void rotate(float degrees)
//2.
void rotate(float degrees, float px, float py)
/**
- 1. 原始矩形
*/
mPaint.color = Color.RED
mPaint.alpha = 100
canvas.drawRoundRect(rectF,50f,50f,mPaint)
/**
- 2.将原始图形旋转 45°
*/
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.rotate(45f)
canvas.drawRoundRect(rectF,50f,50f,mPaint)
/**
- 3.将原始图形旋转 280°
- 以 坐标点 500,100 顺时针旋转 280°
*/
mPaint.color = Color.YELLOW
mPaint.alpha = 100
canvas.rotate(280f,500f,100f)
canvas.drawRoundRect(rectF,50f,50f,mPaint)
skew - 图层错切
//错切是在某方向上,按照一定的比例对图形的每个点到某条平行于该方向的直线的有向距离做放缩得到的平面图形。水平错切(或平行于X轴的错切)是一个将任一点(x,y)映射到点(x+my,y)的操作,m是固定参数,称为错切因子
//sx和sy分就是错切因子,为倾斜角度tan值,这里倾斜45度值为1
void skew (float sx, float sy)
/**
- 1. 原始图形
/
mPaint.color = Color.RED
mPaint.alpha = 100
canvas.drawRoundRect(rectF,50f,50f,mPaint)
/* - 2.图层开始错切
*/
canvas.skew(0f,0.5f)
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.drawRoundRect(rectF,50f,50f,mPaint)
Matrix
/**
- 原始图形
/
canvas.drawBitmap(mBitmap,100f,100f,mPaint)
/*
*1. 矩阵平移 500,500
*/
var matrix = Matrix()
matrix.setTranslate(500f,500f)
canvas.drawBitmap(mBitmap,matrix,mPaint)
/**
- 2. 矩阵缩放 0.5 倍
*/
var matrix2 = Matrix()
matrix2.setScale(0.5f,0.5f)
canvas.drawBitmap(mBitmap,matrix2,mPaint)
/**
- 3. 矩阵旋转 125°
*/
var matrix3 = Matrix()
matrix3.setRotate(125f,500f,500f)
canvas.drawBitmap(mBitmap,matrix3,mPaint)
/**
- 4. 错切
*/
var matrix4 = Matrix()
matrix4.setSkew(0.5f,0.5f)
canvas.drawBitmap(mBitmap,matrix4,mPaint)
裁剪画布
//裁剪
boolean clipRect(RectF rect, Region.Op op);
boolean clipRect(Rect rect, Region.Op op);
boolean clipRect(RectF rect);
boolean clipRect(Rect rect);
boolean clipRect(float left, float top, float right, float bottom, Region.Op op);
boolean clipRect(float left, float top, float right, float bottom);
boolean clipRect(int left, int top, int right, int bottom);
boolean clipPath(Path path, Region.Op op);
boolean clipPath(Path path);
boolean clipRegion(Region region, Region.Op op);
boolean clipRegion(Region region);
上图中的 1 代表原始的图层,未被裁剪;2 代表已经裁剪了的图层;3,不管怎么绘制只能在该区域内部绘制。
/**
- 1. 原始图形
/
mPaint.color = Color.RED
mPaint.alpha = 100
canvas.drawRect(300f,300f,700f,700f,mPaint)
canvas.drawText(“1.原始”,400f,600f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 100f
it.color = Color.WHITE
})
/* - 2. 在 RectF 矩形区域裁剪一块画布,绘制图形只能在该区域中绘制
*/
var rectf2 = RectF(100f, 100f , 500f, 500f);
canvas.clipRect(rectf2)
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.drawColor(mPaint.color)
canvas.drawText(“2.clip”,200f,200f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 100f
it.color = Color.WHITE
})
/**
- 3. 在 300,300;700,700 坐标点上绘制矩形
*/
mPaint.color = Color.YELLOW
mPaint.alpha = 100
canvas.drawRect(300f,300f,700f,700f,mPaint)
canvas.drawText(“3.裁剪之后”,350f,400f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 30f
it.color = Color.WHITE
})
画布的保存与恢复
save 和 restore 一般成对的出现,save 可以保存 canvas 当前的状态,随后进行平移,裁剪等一系列改变 canvas的操作,最后使用 restore 将 canvas 还原成 save 时候的状态。
int save() //每次调用该函数,都会保存当前画布状态,将其放入特定的栈中
void restore() //从栈顶去除这个状态,对画布进行恢复
通过在未裁剪的时候先调用 canvas.save 保存图层,裁剪完之后在调用 canvas.restore 来恢复之前的图层
/**
- 1. 原始图形
/
mPaint.color = Color.RED
mPaint.alpha = 100
canvas.drawRect(300f,300f,700f,700f,mPaint)
canvas.drawText(“1.原始”,400f,600f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 100f
it.color = Color.WHITE
})
/* - 2. 在 RectF 矩形区域裁剪一块画布,绘制图形只能在该区域中绘制
*/
var rectf2 = RectF(100f, 100f , 500f, 500f);
//将未裁剪的图层先进行保存
canvas.save()
canvas.clipRect(rectf2)
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.drawColor(mPaint.color)
canvas.drawText(“2.clip”,200f,200f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 100f
it.color = Color.WHITE
})
/**
- 3. 在 300,300;700,700 坐标点上绘制矩形
*/
//裁剪完之后出栈
canvas.restore()
mPaint.color = Color.YELLOW
mPaint.alpha = 100
canvas.drawRect(300f,300f,600f,600f,mPaint)
canvas.drawText(“3.裁剪之后”,350f,400f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 30f
it.color = Color.WHITE
})
Canvas 与图层
上一小节介绍了 save,restore 可以用来保存图层,下面在来介绍几个 API 同样也可以用于保存图层
//bounds:要保存的区域所对应的举行对象
//saveFlags:取值 ALL_SAVE_FLAG 表示保存全部内容
public int saveLayer(RectF bounds,Paint paint,int saveFlags)
public int saveLayer(float left,float top,float right,float bottom,Paint paint,int saveFlags)
上图的意思是绘制一个以红色的裁剪区域,然后在绘制一个圆可看图中注释 2 ,发现只能在裁剪区域绘制了,但是如果调用 canvas.restoreToCount 之后在绘制,就不会受影响了可看图中注释 3 区域。
/**
- 1. 原始图像
/
mPaint.color = Color.RED
mPaint.alpha = 200
canvas.drawRect(300f,300f,1000f,1000f,mPaint)
canvas.drawText(“1. 裁剪图层”,750f,750f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 30f
it.color = Color.WHITE
})
/* - 2. 保存
*/
val saveLayer = canvas.saveLayer(300f, 300f, 1000f, 1000f, mPaint,ALL_SAVE_FLAG)
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.drawCircle(500f,500f,300f,mPaint)
canvas.drawText(“2. 绘制圆”,350f,700f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 30f
it.color = Color.WHITE
})
/**
- 3.恢复图层
/
canvas.restoreToCount(saveLayer)
/*
*/
mPaint.color = Color.BLUE
mPaint.alpha = 100
canvas.drawCircle(400f,400f,200f,mPaint)
canvas.drawText(“3. 恢复”,350f,250f,Paint(Paint.ANTI_ALIAS_FLAG).also {
it.textSize = 30f
it.color = Color.WHITE
})
Canvas 基础常用的 API 这里都练习了一遍,下面就进行该篇最后的环节了,以一个示例 demo 结束 Canvas 的讲解。
Canvas 实战
- 绘制简易时钟
先来看一下效果,如下:
代码绘制结构:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//1. 绘制外圆
canvas.drawCircle(mX, mY, mR.toFloat(), mPaint!!)
//2. 绘制圆心
canvas.drawCircle(mX, mY, 15f, mPaintMinute!!)
//3. 绘制刻度
drawLines(canvas)
//4. 绘制整点
drawText(canvas)
//5. 更新时间
updateCurrentTime(canvas)
}
这里我们看一下第 5 步怎么实现:
/* *
- 获取当前系统时间
- @param canvas 画布*/
private fun updateCurrentTime(canvas: Canvas) {
//获取系统当前时间
val format = SimpleDateFormat(“HH-mm-ss”)
val time = format.format(Date(System.currentTimeMillis()))
val split = time.split(“-”.toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val hour = Integer.parseInt(split[0])
val minute = Integer.parseInt(split[1])
val second = Integer.parseInt(split[2])
//时针走过的角度
val hourAngle = hour * 30 + minute / 2
//分针走过的角度
val minuteAngle = minute * 6 + second / 10
//秒针走过的角度
val secondAngle = second * 6
//绘制时钟,以12整点为0°参照点
drawLine(canvas, hourAngle, 170, mPaintHour)
//绘制分钟
drawLine(canvas, minuteAngle, 60, mPaintMinute)
//绘制秒钟
canvas.rotate(secondAngle.toFloat(), mX, mY)
canvas.drawLine(mX, mY, mX, mY - mR + 80, mPaintSecond!!)
//每隔1s刷新界面
postInvalidateDelayed(1000)
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
重要知识点
下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。
高级进阶篇——高级UI,自定义View(部分展示)
UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!
- 面试题部分合集
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
oid开发知识点,真正体系化!**
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
重要知识点
下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。
[外链图片转存中…(img-ezCLLGH6-1712204300403)]
高级进阶篇——高级UI,自定义View(部分展示)
UI这块知识是现今使用者最多的。当年火爆一时的Android入门培训,学会这小块知识就能随便找到不错的工作了。不过很显然现在远远不够了,拒绝无休止的CV,亲自去项目实战,读源码,研究原理吧!
[外链图片转存中…(img-zhnGhON9-1712204300403)]
- 面试题部分合集
[外链图片转存中…(img-r7SfzlaV-1712204300404)]