Android技术知识Android开发经验谈Android开发

自定义 View - Canvas - 画布操作和快照

2017-12-11  本文已影响97人  Arnold_J
操作 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.png
defree 绝对值取值 备注
(1, +∞) 放大
1 不变
(0, 1) 缩小

然后我发现,对于 scale 来说,我们需要记住下面的表格

defree 绝对值取值 备注
(1, +∞) 放大
1 不变
(0, 1) 缩小

关于正负翻转多少,其实可以参考坐标系里象限的相关知识,大概是这样:


效果图-g.png

隐约记起了自己背 “奇变偶不变,符号看象限” 的日子。

四、错切
操作 API 备注
画布错切 skew(float sx, float sy) --

错切只提供了一种方法,两个参数为:

在转换后,会给每个点计算出新的位置,具体计算方法为:
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 的封装。如果想了解这方面的东西,可以简单看看下面的文章。希望你的线性代数没有忘光。

感谢:
1.矩阵的运算及其运算规则
2.Matrix 原理

谢谢观赏
上一篇 下一篇

猜你喜欢

热点阅读