自定义 View - Canvas - 画布操作和快照
操作 | API | 备注 |
---|---|---|
移动画布 | translate(float dx, float dy) | -- |
画布旋转 | rotate(float degrees) | -- |
画布旋转 | rotate(float degrees, float px, float py) | -- |
画布缩放 | scale(float sx, float sy) | -- |
画布缩放 | scale(float sx, float sy, float px, float py) | -- |
画布错切 | skew(float sx, float sy) | -- |
画布状态保存 | save() | -- |
画布状态保存 | save(int saveFlags) | API26弃用 |
画布状态还原 | restore() | -- |
画布状态还原 | restore(int saveCount) | -- |
一、画布移动
操作 | API | 备注 |
---|---|---|
移动画布 | translate(float dx, float dy) | -- |
画布移动操作在绘制自定义 View 中非常常用,它能减少不必要的计算,同时让我们在绘制的时候,只需要关注某个小模块的内部位置关系。思路清晰了,做自定义自然也会得心应手一些。
上面方法意为把画布中心移动到某个位置 (dx, dy),在移动的时候,画布上已经绘制的视图并不会移动。下面是我移动了两次的例子:
canvas.translate(50,50);
drawXY(canvas);//绘制坐标系
canvas.translate(350,250)
drawXY(canvas);//绘制坐标系
效果图-g.png
二、旋转
操作 | API | 备注 |
---|---|---|
画布旋转 | rotate(float degrees) | -- |
画布旋转 | rotate(float degrees, float px, float py) | -- |
参数是旋转角度和旋转中心,在没有旋转中心参数传入的时候,默认旋转中心为 (0 , 0) 。需要注意的是,由于屏幕的坐标系 y 轴向下,所以转动的正方向和习惯的有些不同。
三、缩放
操作 | API | 备注 |
---|---|---|
画布缩放 | scale(float sx, float sy) | -- |
画布缩放 | scale(float sx, float sy, float px, float py) | -- |
scale 方法中主要看前面两个参数,后面两个参数为旋转中心,在不输入值的时候,默认为(0 , 0)。
degree 取值 | 备注 |
---|---|
(1, +∞) | 放大 |
1 | 不变 |
(0, 1) | 缩小 |
(-1, 0) | 缩小并翻转 180 |
-1 | 翻转 180 |
(-∞, -1) | 放大并翻转 180 |
这个东西让我想到了高中时候做的小孔成像实验。。。
言归正传,写点代码更容易理解,先是正值 0.5、1.0、1.5、2.0 的效果图:
效果图-g.png
再来是负值 -0.5、-1.0、-1.5、-2.0 的效果图:
效果图-g.pngdefree 绝对值取值 | 备注 |
---|---|
(1, +∞) | 放大 |
1 | 不变 |
(0, 1) | 缩小 |
然后我发现,对于 scale 来说,我们需要记住下面的表格
defree 绝对值取值 | 备注 |
---|---|
(1, +∞) | 放大 |
1 | 不变 |
(0, 1) | 缩小 |
关于正负翻转多少,其实可以参考坐标系里象限的相关知识,大概是这样:
效果图-g.png
隐约记起了自己背 “奇变偶不变,符号看象限” 的日子。
四、错切
操作 | API | 备注 |
---|---|---|
画布错切 | skew(float sx, float sy) | -- |
错切只提供了一种方法,两个参数为:
- sx 画布在 x 轴上的倾斜角度的 tan 值
- sy 画布在 y 轴上的倾斜角度的 tan 值
在转换后,会给每个点计算出新的位置,具体计算方法为:
xResult = x + sy * x
yResult = y + sx * y
试了一下 x 轴上 45 度的错切:
效果图-g.png
五、画布快照
操作 | API | 备注 |
---|---|---|
画布状态保存 | save() | -- |
画布状态保存 | save(int saveFlags) | API26弃用 |
画布状态还原 | restore() | -- |
画布状态还原 | restore(int saveCount) | -- |
为什么要有画布快照这个功能?这个功能有什么用?
如果大家已经试过位移、旋转等操作,你们有可能发现,如果我连续两次进行操作,第一次画布旋转 90 绘制一个矩形,第二次画布旋转 180 绘制一个矩形。得到的结果是这样的:
效果图-g.png
我们发现第二次旋转 180 绘制的图形,实际上旋转了 270。这是因为我们所有的操作是叠加的,并不会自动归位。这样对于简单操作来说,还是可以的,但是如果操作方式变得比较复杂,无疑会增加我们的工作量。
所以,我们需要屏幕快照来保存当前画布的状态,当绘制完成后,返回到原来的画布状态。
canvas.translate(originX,originY);
RectF rectF = new RectF(0, 0, 200, 200);
mRectPaint.setColor(Color.BLACK);
canvas.drawRect(rectF,mRectPaint);
canvas.save();
mRectPaint.setColor(Color.RED);
canvas.rotate(90);
canvas.drawRect(rectF,mRectPaint);
canvas.restore();
canvas.save();
mRectPaint.setColor(Color.GREEN);
canvas.rotate(180);
canvas.drawRect(rectF,mRectPaint);
canvas.restore();
效果图-g.png
需要注意的是,我们可以 save 次数大于 restore,但是 restore 的次数不能多于 save 次数,否则会出错。
关于画布操作中的位移、缩放、旋转、错切,其实都是对于 Matric 的封装。如果想了解这方面的东西,可以简单看看下面的文章。希望你的线性代数没有忘光。
谢谢观赏