Canvas进阶之translate、scale、rotate

咱们先定义几个需要的数据,为了屏幕适配,数据均为dp:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. // 刻度尺高度

  2. private static final int DIVIDING_RULE_HEIGHT = 70;

  3. // 距离左右间

  4. private static final int DIVIDING_RULE_MARGIN_LEFT_RIGHT = 10;

  5. // 第一条线距离边框距离

  6. private static final int FIRST_LINE_MARGIN = 5;

  7. // 打算绘制的厘米数

  8. private static final int DEFAULT_COUNT = 9;

然后将以上数据转为对应像素值:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. private void initData() {

  2. mDividRuleHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,

  3. DIVIDING_RULE_HEIGHT, mResources.getDisplayMetrics());

  4. mHalfRuleHeight = mDividRuleHeight / 2;

  5. mDividRuleLeftMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,

  6. DIVIDING_RULE_MARGIN_LEFT_RIGHT, mResources.getDisplayMetrics());

  7. mFirstLineMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,

  8. FIRST_LINE_MARGIN, mResources.getDisplayMetrics());

  9. }

有了以上数据,则可以确定外边框的Rect为:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. mOutRect = new Rect(mDividRuleLeftMargin, top, mTotalWidth - mDividRuleLeftMargin,

  2. mRuleBottom);

接下来看刻度线的绘制,根据厘米可以计算出中间的格数,根据厘米占用屏幕宽度和所占格数可以计算出每一格所占屏幕宽度:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. mLineInterval = (mTotalWidth - 2 * mDividRuleLeftMargin - 2 * mFirstLineMargin)

  2. / (DEFAULT_COUNT * 10 - 1);

有了每一格所占宽度,我们只需要在绘制刻度线的时候不断将画布右移对应宽度即可:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * 绘制刻度线

  3. * @param canvas

  4. */

  5. private void drawLines(Canvas canvas) {

  6. canvas.save();

  7. canvas.translate(mLineStartX, 0);

  8. int top = mMaxLineTop;

  9. for (int i = 0; i <= DEFAULT_COUNT * 10; i++) {

  10. if (i % 10 == 0) {

  11. top = mMaxLineTop;

  12. } else if (i % 5 == 0) {

  13. top = mMiddleLineTop;

  14. } else {

  15. top = mMinLineTop;

  16. }

  17. canvas.drawLine(0, mRuleBottom, 0, top, mLinePaint);

  18. canvas.translate(mLineInterval, 0);

  19. }

  20. canvas.restore();

  21. }

由于刻度尺上分三种长短的刻度线,我们也做对应处理,10的整数倍的刻度线最长,5的整数倍的刻度线中等长度,其余较短;

此时绘制出的刻度尺效果为:

此时刻度尺的基本样子就出来了,对应文字大家有兴趣可以自己加上;

俗话说,条条大路通罗马,我们除了使用canvas.translate ,还能不能使用别的方式进行实现呢,答案当然是可以,比如在绘制的时候根据for循环里的 i 值也可以直接计算出每一根刻度线的位置,然后直接进行绘制,相比之下,这两种方式的优劣大家也可以自行比较一下,好了,canvas.translate() 就说这么多;

二、canvas.scale( ) - 画布的缩放:

关于scale,Android 提供了以下两个接口:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Preconcat the current matrix with the specified scale.

  3. *

  4. * @param sx The amount to scale in X

  5. * @param sy The amount to scale in Y

  6. */

  7. public native void scale(float sx, float sy);

  8. /**

  9. * Preconcat the current matrix with the specified scale.

  10. *

  11. * @param sx The amount to scale in X

  12. * @param sy The amount to scale in Y

  13. * @param px The x-coord for the pivot point (unchanged by the scale)

  14. * @param py The y-coord for the pivot point (unchanged by the scale)

  15. */

  16. public final void scale(float sx, float sy, float px, float py) {

  17. translate(px, py);

  18. scale(sx, sy);

  19. translate(-px, -py);

  20. }

我们先看下scale(float sx , float sy),我们还是以上面的正方形作为栗子,调用canvas.scale(float sx , float sy)之后看下效果;

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. @Override

  2. protected void onDraw(Canvas canvas) {

  3. super.onDraw(canvas);

  4. canvas.drawColor(Color.BLUE);

  5. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  6. canvas.scale(0.5f, 0.5f);

  7. mPaint.setColor(Color.YELLOW);

  8. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  9. }

我们将画布在x,y方向上均缩放为 0.5 倍,使用默认基准点(原点 0,0),效果如下:

效果就相当于用个钉子钉在(0,0)处,然后把矩形的x,y缩放为一半,我们再来看看第二个接口scale(float sx , float sy, float px,float py):

前两个参数为将画布在x、y方向上缩放的倍数,而px和py 分别为缩放的基准点,从源码上可以非常清楚的看出和scale(float sx , float sy)的差别:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. translate(px, py);

  2. scale(sx, sy);

  3. translate(-px, -py);

即先将画布平移px,py,然后scale,scale结束之后再将画布平移回原基准点;

我们再在之前的基础上绘制一个同样的矩形,x , y 均缩放为 0.5 倍,缩放中心为矩形的中心:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. @Override

  2. protected void onDraw(Canvas canvas) {

  3. super.onDraw(canvas);

  4. canvas.drawColor(Color.BLUE);

  5. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  6. // 保存画布状态

  7. canvas.save();

  8. canvas.scale(0.5f, 0.5f);

  9. mPaint.setColor(Color.YELLOW);

  10. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  11. // 画布状态回滚

  12. canvas.restore();

  13. canvas.scale(0.5f, 0.5f, 200, 200);

  14. mPaint.setColor(Color.BLACK);

  15. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  16. }

一起来看下效果:

效果就相当于用个钉子钉在矩形的中心,然后进行缩放;

根据上面android 的实现,我们其实可以使用以下代码实现同样的效果:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. // 先将画布平移到矩形的中心

  2. canvas.translate(200, 200);

  3. // 将画布进行缩放

  4. canvas.scale(0.5f, 0.5f);

  5. // 将画布移回原基准点

  6. canvas.translate(-200, -200);

  7. mPaint.setColor(Color.BLACK);

  8. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

到此为止,我们也就了解了对画布的缩放,基于canvas.scale(),我们一起完成一个小例子:

上面是网络上找的一张让人产生视觉误差的静态图,我们模拟绘制出上面的效果;

思路非常的简单:

1. 绘制一个和屏幕等宽的正方形;

2. 将画布以正方形中心为基准点进行缩放;

3. 在缩放的过程中绘制原正方形;

注:每次绘制都得使用canvas.save()  和 canvas.restore()进行画布的锁定和回滚,以免除对后面绘制的影响(后面会单独讲)

先初始化画笔,注意此时画笔需要设置成空心:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * 初始化画笔

  3. */

  4. private void initPaint() {

  5. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

  6. // 将画笔设置为空心

  7. mPaint.setStyle(Style.STROKE);

  8. // 设置画笔颜色

  9. mPaint.setColor(Color.BLACK);

  10. // 设置画笔宽度

  11. mPaint.setStrokeWidth(mLineWidth);

  12. }

然后循环的将画布缩放的同时绘制原正方形:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * 绘制正方形

  3. *

  4. * @param canvas

  5. */

  6. private void drawSquare(Canvas canvas) {

  7. for (int i = 0; i < TOTAL_SQUARE_COUNT; i++) {

  8. // 保存画布

  9. canvas.save();

  10. float fraction = (float) i / TOTAL_SQUARE_COUNT;

  11. // 将画布以正方形中心进行缩放

  12. canvas.scale(fraction, fraction, mHalfWidth, mHalfHeight);

  13. canvas.drawRect(mSquareRect, mPaint);

  14. // 画布回滚

  15. canvas.restore();

  16. }

  17. }

一起来看下绘制的效果:

其实最终效果和网上找的还是有点小差别的,由于画布的缩放,越小的时候画笔宽度越细,而原图是所有的都一样宽度,但似乎画笔宽度缩放之后效果更佳,哈哈 … …

三、canvas.rotate( ) - 画布的旋转:

canvas.rotate( )和canvas.scale()可以类比起来看,如果理解了canvas.scale( ),那么canvas.rotate( )将会非常简单实用;

简单来讲,canvas.rotate( )即是将画布进行旋转,和canvas.scale( )类似的是,它也有两个可以使用的方法:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * Preconcat the current matrix with the specified rotation.

  3. *

  4. * @param degrees The amount to rotate, in degrees

  5. */

  6. public native void rotate(float degrees);

  7. /**

  8. * Preconcat the current matrix with the specified rotation.

  9. *

  10. * @param degrees The amount to rotate, in degrees

  11. * @param px The x-coord for the pivot point (unchanged by the rotation)

  12. * @param py The y-coord for the pivot point (unchanged by the rotation)

  13. */

  14. public final void rotate(float degrees, float px, float py) {

  15. translate(px, py);

  16. rotate(degrees);

  17. translate(-px, -py);

  18. }

两个方法的区别也是在于基准点的选取,默认是以原点作为基准点,另一个则是以传入的x,y 作为基准点,是不是和scale 一模一样,咱们一起来rotate一下:

咱们先转转左上角的矩形,转多少度呢?先来个90度玩玩吧;

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. @Override

  2. protected void onDraw(Canvas canvas) {

  3. super.onDraw(canvas);

  4. canvas.drawColor(Color.BLUE);

  5. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  6. mPaint.setColor(Color.YELLOW);

  7. canvas.rotate(90);

  8. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  9. }

我们的预期是屏幕上有个旋转了的骚黄色矩形,一起来看看;

擦,黄色的矩形呢?

由于基准点是原点,我们直接旋转了90 度,所以已经将矩形旋转出屏幕,当然看不到了,我们将角度调小一点,改为45 度:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. @Override

  2. protected void onDraw(Canvas canvas) {

  3. super.onDraw(canvas);

  4. canvas.drawColor(Color.BLUE);

  5. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  6. mPaint.setColor(Color.YELLOW);

  7. canvas.rotate(45);

  8. canvas.drawRect(new Rect(0, 0, 400, 400), mPaint);

  9. }

此时我们可以可以清楚的看到黄色的矩形是红色矩形绕原点(0,0)旋转45度之后的结果;

我们再将旋转基准点改为矩形中心看看:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. canvas.rotate(45,200,200);

可以看到现在黄色矩形是红色矩形绕着中心旋转后的结果:

到这里,我们已经了解了canvas.rotate(float degrees)和 canvas.rotate(float degrees,float px , float py)的使用,同样也应该清楚后者的实现如下:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. translate(px, py);

  2. rotate(degrees);

  3. translate(-px, -py);

好了,我们再利用canvas.rotate()完成个闹钟表盘的小例子:

闹钟表盘其实和刻度尺类似,只是一个是在一条直线上绘制,一个是在一个圆周上绘制,说到底都是确定一个位置绘制刻度线;

既然是圆周,最简单的方式莫过于在闹钟的12点钟处划线,通过canvas的旋转绘制到对应圆周处,我们一起实现一下:

整个圆周是360 度,每隔 30 度为一个整时间刻度,整刻度与刻度之间有四个短刻度,划分出5个小段,每个段为6度,有了这些分析,我们则可以采用如下代码进行绘制:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * 绘制刻度

  3. *

  4. * @param canvas

  5. */

  6. private void drawLines(Canvas canvas) {

  7. for (int i = 0; i <= 360; i++) {

  8. if (i % 30 == 0) {

  9. mLineBottom = mLineTop + mLongLineHeight;

  10. mLinePaint.setStrokeWidth(mLineWidth);

  11. } else {

  12. mLineBottom = mLineTop + mShortLineHeight;

  13. mLinePaint.setStrokeWidth(mHalfLineWidth);

  14. }

  15. if (i % 6 == 0) {

  16. canvas.save();

  17. canvas.rotate(i, mHalfWidth, mHalfHeight);

  18. canvas.drawLine(mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint);

  19. canvas.restore();

  20. }

  21. }

  22. }

此时效果如下:

整体代码如下:

[html]  view plain  copy

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. /**

  2. * 闹钟表盘

  3. *

  4. * @author AJian

  5. */

  6. public class RotateClockView extends View {

  7. private static final int LONG_LINE_HEIGHT = 35;

  8. private static final int SHORT_LINE_HEIGHT = 25;

  9. private Paint mCirclePaint, mLinePaint;

  10. private DrawFilter mDrawFilter;

  11. private int mHalfWidth, mHalfHeight;

  12. // 圆环线宽度

  13. private int mCircleLineWidth, mHalfCircleLineWidth;

  14. // 直线刻度线宽度

  15. private int mLineWidth, mHalfLineWidth;

  16. // 长线长度

  17. private int mLongLineHeight;

  18. // 短线长度

  19. private int mShortLineHeight;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下~

)

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-9LHlL87m-1711927911480)]

总结

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后如何才能让我们在面试中对答如流呢?

答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

[外链图片转存中…(img-MK80KslP-1711927911481)]

这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。

[外链图片转存中…(img-xyNVsQCA-1711927911481)]

好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下~

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值