友情链接:

Canvas API

Android自定义View【实战教程】3⃣️----Paint类、Path类以及PathEffect类详解

#神马是Canvas

###基本概念
Canvas:可以理解为是一个为我们提供了各种工具的画布,我们可以在上面尽情的绘制(旋转,平移,缩放等等)。可以理解为系统分配给我们一个一个内存空间,然后提供了一些对这个内存空间操作的方法(API), 实际存储是在下面的bitmap。

###两种画布
这里canvas可以绘制两种类型的画图,分别是view和surfaceView。
View:是普通画图,适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的。
SurfaceView:主要用在游戏,高品质动画方面的画图。
区别:在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。

###Canvas坐标系与绘图坐标系
Canvas绘图中牵扯到两种坐标系:Canvas坐标系与绘图坐标系。

  • Canvas坐标系
    Canvas坐标系指的是Canvas本身的坐标系,Canvas坐标系有且只有一个,且是唯一不变的,其坐标原点在View的左上角,从坐标原点向右为x轴的正半轴,从坐标原点向下为y轴的正半轴。
  • 绘图坐标系
    Canvas的drawXXX方法中传入的各种坐标指的都是绘图坐标系中的坐标,而非Canvas坐标系中的坐标。默认情况下,绘图坐标系与Canvas坐标系完全重合,即初始状况下,绘图坐标系的坐标原点也在View的左上角,从原点向右为x轴正半轴,从原点向下为y轴正半轴。但不同于Canvas坐标系,绘图坐标系并不是一成不变的,可以通过调用Canvas的translate方法平移坐标系,可以通过Canvas的rotate方法旋转坐标系,还可以通过Canvas的scale方法缩放坐标系,而且需要注意的是,translate、rotate、scale的操作都是基于当前绘图坐标系的,而不是基于Canvas坐标系,一旦通过以上方法对坐标系进行了操作之后,当前绘图坐标系就变化了,以后绘图都是基于更新的绘图坐标系了。也就是说,真正对我们绘图有用的是绘图坐标系而非Canvas坐标系。

我们看下面代码就可以明白:

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//未平移 在原点canvas.drawLine(0, 0, width, 0, mPaint);//绘制x轴canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴//第一次移动canvas.translate(200,200);canvas.drawLine(0, 0, width, 0, mPaint);//绘制x轴canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴canvas.restore();//第二次移动并旋转canvas.translate(200,200);canvas.rotate(30);canvas.drawLine(0, 0, width, 0, mPaint);//绘制x轴canvas.drawLine(0, 0, 0, height, mPaint);//绘制y轴}

每次绘制同样的(startX, startY,stopX,stopY, paint)线,
但是我们发现平移或者旋转之后画出的线坐标发生了变化

那么有童鞋问了,如果我不想让坐标发生变化,或者再回去原点怎么搞?
别担心,只需要执行canvas.restore(),下面详细讲解。

Canvas保存和还原

  • canvas.save()
    保存当前坐标
  • canvas.restore()
    回复上一次坐标,如果有保存,回到最后一次保存的坐标,如果没保存,则会报错java.lang.IllegalStateException: Underflow in restore - more restores than saves ,要先存再取。
  • restoreToCount(int saveCount)
    回到第几次的保存坐标状态

#对Canvas的操作 — 平移,旋转,缩放

###Canvas平移

/** * 画布向(dx,dy)方向平移 *  * 参数1: 向X轴方向移动dx距离 * 参数2: 向Y轴方向移动dy距离   */canvas.translate(float dx, float dy);

###Canvas缩放

/** * 在X轴方向放大为原来sx倍,Y轴方向方大为原来的sy倍 * 默认原点为左上角* 参数1: X轴的放大倍数 * 参数2: Y轴的放大倍数 */
canvas.scale(float sx, float sy);/** * 在X轴方向放大为原来sx倍,Y轴方向方大为原来的sy倍 * 参数1: X轴的放大倍数 * 参数2: Y轴的放大倍数 * 参数3: 原点X坐标* 参数4: 原点Y坐标*/
canvas.scale(float sx, float sy, float px, float py);

###Canvas旋转

/** * 原点为中心,旋转degrees度(顺时针方向为正方向 )* 参数: 旋转角度 */
canvas.rotate(float degrees);/** * 以(px,py)为中心,旋转30度,顺时针方向为正方向 * 参数1: 旋转角度* 参数2: 原点X坐标* 参数3: 原点Y坐标 */
canvas.rotate(float degrees, float px, float py);

#绘制
###画文字

/*** 参数1:输入的内容 * 参数2:文本x轴的位置 * 参数3:文本Y轴的位置 * 参数4:画笔对象 */
drawText(String text, float x, float y,  Paint paint)/*** 参数1:输入的内容 * 参数2:要从第几个字开始绘制* 参数3:要绘制到第几个文字 * 参数4:文本x轴的位置 * 参数5:文本Y轴的位置 * 参数6:画笔对象 */
drawText(String text, int start, int end, float x, float y,Paint paint)

样例:

canvas.drawText("开始写字啦!", 200,200,mPaint);
canvas.drawText("开始写字啦!",2,3, 200,400,mPaint);

###画圆

/*** 参数1:圆心X * 参数2:圆心Y * 参数3:半径R * 参数4:画笔对象 */
drawCircle(float cx, float cy, float radius, Paint paint)

样例:

 mPaint.setStyle(Paint.Style.STROKE);canvas.drawCircle(300,300,80,mPaint);mPaint.setStyle(Paint.Style.FILL);canvas.drawCircle(300,500,80,mPaint);mPaint.setStyle(Paint.Style.FILL_AND_STROKE);canvas.drawCircle(300,700,80,mPaint);

###画线

/* * 参数1:startX * 参数2:startY * 参数3:stopX * 参数4:stopY * 参数5:画笔对象 */
canvas.drawLine(float startX, float startY, float stopX, float stopY,Paint paint);/* * 同时绘制多条线。 * 参数1:float数组:每四个一组为一条线。* 参数2:画笔对象 */
canvas.drawLines(@Size(multiple=4)float[] pts, Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));
canvas.drawLine(50,50,200,50,mPaint);mPaint.setColor(getResources().getColor(android.R.color.darker_gray));
canvas.drawLines(new float[]{200,200,300,200,300,300,300,400},mPaint);

###画椭圆

/*** 参数1: 矩形* 参数2: 画笔* /
canvas.drawOval(RectF oval, Paint paint);/***  参数1:float left *  参数2:float top *  参数3:float right *  参数4:float bottom *  参数5:画笔*/
canvas.drawOval(float left, float top, float right, float bottom, @NonNull Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));
canvas.drawOval(new RectF(50,50,400,400),mPaint);mPaint.setColor(getResources().getColor(android.R.color.darker_gray));
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {canvas.drawOval(50,500,700,700,mPaint);
}

###画弧度

/***  参数1:RectF对象。 *  参数2:开始的角度。(水平向右为0度顺时针反向为正方向) *  参数3:扫过的角度 *  参数4:是否和中心连线 *  参数5:画笔对象 */
canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint);/***  参数1:float left *  参数2:float top *  参数3:float right *  参数4:float bottom  *  参数5:开始的角度。(水平向右为0度顺时针反向为正方向) *  参数6:扫过的角度 *  参数7:是否和中心连线 *  参数8:画笔对象 */
canvas.drawArc(float left, float top, float right, float bottom,float startAngle, float sweepAngle, boolean useCenter,Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));
canvas.drawArc(new RectF(50,50,400,400),45,135,true,mPaint);mPaint.setColor(getResources().getColor(android.R.color.darker_gray));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {canvas.drawArc(50,500,700,700,45,135,false,mPaint);
}

###矩形

/** *  矩形 *  参数1:float left *  参数2:float top *  参数3:float right *  参数4:float bottom *  参数5:画笔*/
canvas.drawRect(float left, float top, float right, float bottom,Paint paint);  /** *  参数1:矩形 *  参数2:画笔*/
canvas.drawRectRect r,Paint paint);

样例:

mPaint.setColor(getResources().getColor(android.R.color.darker_gray));
canvas.drawRect(new RectF(50,50,400,400),mPaint);mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));
canvas.drawRect(50,500,700,700,mPaint);

###圆角矩形

/***  参数1:矩形 *  参数2:x半径*  参数3:y半径 *  参数4: 画笔*/
drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)/***  参数1:float left *  参数2:float top *  参数3:float right *  参数4:float bottom *  参数5:x半径*  参数6:y半径 *  参数4: 画笔*/
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)

样例:

mPaint.setColor(getResources().getColor(android.R.color.darker_gray));
canvas.drawRoundRect(new RectF(50,50,400,400),20,20,mPaint);mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {canvas.drawRoundRect(50,500,700,700,30,50,mPaint);
}

###画点

/** * 参数1、2:点的x、y坐标 */
canvas.drawPoint(60, 390, p);//画一个点  /** * 参数1:多个点,每两个值为一个点。最后个数不够两个的值,忽略。 */
canvas.drawPoints(new float[]{60,400,65,400,70,400}, p);//画多个点

样例:

mPaint.setColor(getResources().getColor(android.R.color.darker_gray));canvas.drawPoint(50,50,mPaint);mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));canvas.drawPoints(new float[]{100,100,200,200,300, 300, 400,400,500,500,600,600},mPaint);

###画图片

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
/** * 参数1:bitmap对象 * 参数2:图像左边坐标点 * 参数3:图像上边坐标点 */
canvas.drawBitmap(Bitmap bitmap, float left, float top, Paint paint);

样例:

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
canvas.drawBitmap(bitmap, 200,300, mPaint);

还有问题可以查看Canvas API

到这里基本属性就讲完了,接下来是一个练习。

#代码绘制安卓小机器人

下面是代码 , 相当简单,就是计算一下坐标,就不详细讲了,有问题可以留言。

public class AndroidView extends View {private float bodyWidth;private float bodyHeigh;private float armWidth;private float armHeight;private float legWidth;private float legHeight;private static final int INTERSPACE = 20;private Paint mPaint;private RectF bodyRect;private RectF legRect;private RectF armRect;public AndroidView(Context context) {this(context, null);}public AndroidView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public AndroidView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mPaint = new Paint();mPaint.setColor(getResources().getColor(android.R.color.holo_green_dark));}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);setBodyParams();setArmParams();setLegParams();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();//画身体canvas.drawRoundRect(bodyRect, 20, 20, mPaint);//画头canvas.translate(0, -(bodyWidth / 2 + INTERSPACE));canvas.drawArc(bodyRect, 0, -180, true, mPaint);//画左胳膊canvas.drawRoundRect(armRect, 30, 30, mPaint);//画右胳膊canvas.translate(bodyWidth + 5 * INTERSPACE, 0);canvas.drawRoundRect(armRect, 30, 30, mPaint);//画左腿canvas.translate(-(bodyWidth + 7 * INTERSPACE),bodyWidth*11/10);canvas.drawRoundRect(legRect, 30, 30, mPaint);//画右腿canvas.translate(2*INTERSPACE+legWidth,0);canvas.drawRoundRect(legRect, 30, 30, mPaint);//画左眼canvas.translate(0,-bodyHeigh-5*INTERSPACE);mPaint.setColor(getResources().getColor(android.R.color.white));canvas.drawCircle(getWidth()/2,getHeight()/2,INTERSPACE/2,mPaint);//画右眼canvas.translate(-(2*INTERSPACE+legWidth),0);mPaint.setColor(getResources().getColor(android.R.color.white));canvas.drawCircle(getWidth()/2,getHeight()/2,INTERSPACE/2,mPaint);canvas.restore();mPaint.setTextSize(60);mPaint.setColor(getResources().getColor(android.R.color.holo_red_dark));canvas.drawText("我是安卓小机器人",150,100,mPaint);}private void setBodyParams() {bodyWidth = getWidth() * 2 / 5;bodyHeigh = bodyWidth;bodyRect = new RectF();bodyRect.left = (getWidth() - bodyWidth) / 2;bodyRect.top = (getHeight() - bodyHeigh) / 2;bodyRect.right = bodyRect.left + bodyWidth;bodyRect.bottom = bodyRect.top + bodyHeigh;}private void setLegParams() {legWidth = getWidth() * 1 / 13;legHeight = getHeight() * 1 / 7;legRect = new RectF();legRect.left = (getWidth() - legWidth) / 2;legRect.top = (getHeight() - legHeight) / 2;legRect.right = legRect.left + legWidth;legRect.bottom = legRect.top + legHeight;}private void setArmParams() {armWidth = getWidth() * 1 / 13;armHeight = getHeight() * 1 / 6;armRect = new RectF();armRect.left = (getWidth() - bodyWidth) / 2 - INTERSPACE * 4;armRect.top = getHeight() / 2 + INTERSPACE * 2;armRect.right = armRect.left + armWidth;armRect.bottom = armRect.top + armHeight;}
}

DEMO下载地址:
http://download.csdn.net/detail/github_33304260/9841552

https://img-blog.csdn.net/20170625220855322?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2l0aHViXzMzMzA0MjYw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~
扫码关注公众号“伟大程序猿的诞生“,更多干货等着你~

~~号外~~福利~~号外~~
程序员的福音: “老曾筋骨祛痛贴”,百年祖传配方,专治腰间盘肩周颈椎坐骨神经腰腿疼痛等,博主亲测效果非常棒,因长期久坐写代码,坐姿不规范导致脖子疼,腰椎疼,用过之后疼痛逐渐缓解,现在已无任何疼痛,用过后让你写代码一身轻松,so easy,妈妈再也不用担心我们写代码了。
购买链接: https://k.weidian.com/tja7GYzB

扫码下方二维码,关注公众号“伟大程序猿的诞生“,回复“膏药”领取优惠券
扫码关注公众号“伟大程序猿的诞生“,更多干货新鲜文章等着你~

公众号回复“资料获取”,获取更多干货哦~
公众号回复“膏药”,领取优惠券哦~

有问题添加本人微信号“fenghuokeji996” 或扫描博客导航栏本人二维码

Android自定义View【实战教程】5⃣️---Canvas详解及代码绘制安卓机器人相关推荐

  1. android自定义view案例,Android自定义View的实现方法实例详解

    一.自绘控件 下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次.新建一个CounterView继承自View,代码如下所示: 可以看到,首先我们在 ...

  2. Android 系统(201)---Android 自定义View实战系列 :时间轴

    Android 自定义View实战系列 :时间轴 Android开发中,时间轴的 UI需求非常常见,如下图: 本文将结合 自定义View & RecyclerView的知识,手把手教你实现该常 ...

  3. [自定义控件]android自定义view实战之太极图

    android自定义view实战之太极图 尊重原创,转载请注明出处: http://blog.csdn.net/qq137722697 自定义view是Android工程师进阶不可避免要接触的,我的学 ...

  4. 【Android自定义View实战】之自定义评价打分控件RatingBar,可以自定义星星大小和间距...

    [Android自定义View实战]之自定义评价打分控件RatingBar,可以自定义星星大小和间距

  5. Android自定义View实战:简约风歌词控件,Android开发者值得深入思考的几个问题

    57[02:41.62]从不知 她的痛苦 58[02:52.02] 59[02:54.11]喜欢你 那双眼动人 60[03:00.13]笑声更迷人 61[03:02.38] 62[03:03.14]愿 ...

  6. android 图片处理过程中添加进度条,『Android自定义View实战』给我一个图标,还你一个水波纹进度球...

    前言 我们都知道,平时表现进度的方式有千千万万种(没有UI想不到的,只有你做不到的= =.),其中有一种就是水波纹进度球的形式,网上很多种实现都是直接采用纯色填充的方式,即水波纹都是纯颜色填充,效果看 ...

  7. 【Android自定义View实战】之仿去哪儿网App图片按压显示指纹并缩放效果TouchFingerImageView

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/52986713 [DylanAndroid的csdn博客] 我们发现去哪儿网ap ...

  8. android 画圆弧动画,『Android自定义View实战』自定义带入场动画的弧形百分比进度条...

    写在前面 这是在简书发表的处女座,这个想法也停留在脑海中很久了,一直拖到现在(懒癌发作2333),先自我介绍一番,一枚刚毕业不久的Android程序猿,初出茅庐的Android小生,之前一直在CSDN ...

  9. Android自定义View实战:简约风歌词控件

    作者:jsyjst 前言 最近重构了之前的音乐播放器,添加了许多功能,比如歌词,下载功能等.这篇文章就让我们聊聊歌词控件的实现,先上效果图,如果感觉海星,就继续瞧下去! 看到这里,估计你对这个控件还有 ...

  10. 【Android自定义View实战】之仿QQ运动步数圆弧及动画,Dylan计步中的控件StepArcView

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/52936609 [DylanAndroid的csdn博客] 在之前的Androi ...

最新文章

  1. 24招加速你的Python,超级实用!
  2. 模板 - AC自动机
  3. 数字图像处理的三个层次
  4. python机器学习及实践_Python机器学习及实践
  5. Javascript——Math对象
  6. 人月神话阅读笔记06
  7. 语言学句法分析树形图怎么画_哇,好大一棵树! 如何优雅地画句法树形图 丨语言学午餐...
  8. 南开大学校园邮箱pop3地址
  9. 支付宝 支付返回 4000 ,系统繁忙请稍后再试
  10. 2018年中考计算机考试成绩,2018年北京中考考试科目、时间及成绩公布通知
  11. Jenkins Bitbucket Java 部署
  12. 三菱plc传送文件到服务器,三菱Q系列PLC通过FTP文件传输案例介绍
  13. Proguard的Keep使用方法
  14. “Android开发3年老板嫌我工资高,把我辞了
  15. 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java宠物医院管理系统fy9ez
  16. USB无线网卡共享台式机接入ADSL无线上网
  17. 计算机网络之:网段与IP地址
  18. 实验7-4 身份证号码最后一位
  19. 「 每日一练,快乐水题 」728. 自除数
  20. 《自控力》总结_完结

热门文章

  1. Java配置文件读取写入通用类库:PropUtils 属性文件类
  2. Java关键字this与super的用法详解
  3. C++_模板全特化、偏特化
  4. cmake_安装导入静态/动态库的三种方式(find_package INSTALL 使用绝对路径)
  5. python中的pickle解析
  6. pyspark的用法
  7. 关于deployment descripter(web.xml)的认识
  8. Centos6.3下Ganglia3.6.0安装配置
  9. 做某一项目常见知识点链接
  10. SQL Server 2008中文版关系数据库基础与实践教程pdf