Android自定义View工具:Paint&Canvas(二)

本文涉及的产品
模型训练 PAI-DLC,5000CU*H 3个月
交互式建模 PAI-DSW,5000CU*H 3个月
模型在线服务 PAI-EAS,A10/V100等 500元 1个月
简介: 本篇接着来看Canvas

接上篇,Android自定义View工具:Paint&Canvas(一),上篇主要介绍的是Paint,本篇接着来看Canvas。

Canvas常用方法一览:

Canvas常用方法 备注
绘制颜色
drawARGB 通过设置ARGB值绘制颜色
drawRGB 通过设置RGB值绘制颜色
drawColor 绘制颜色
绘制图形
drawPoint,drawPoints 绘制点,点集合
drawLine,drawLines 绘制线,线集合
drawRect 绘制矩形
drawCircle 绘制圆
drawOval 绘制椭圆
drawArc 绘制弧
画布操作
translate、rotate、scale、save、restore 依次为位移、旋转、缩放、保存画布和恢复画布
drawPath 按路径绘制
  • Canvas绘制颜色:

canvas.drawARGB(int a, int r, int g, int b)
canvas.drawRGB(int r, int g, int b)
canvas.drawColor(int color) 
canvas.drawColor(int color, PorterDuff.Mode mode)

颜色的四种模式:

颜色模式 备注
ARGB8888 四通道高精度(32位)
ARGB4444 四通道低精度(16位)
RGB565 屏幕默认模式(16位)
Alpha8 仅有透明通道(8位)

ARGB:

类型 备注
A(Alpha) 透明度,取值范围 [0,255],0代表完全透明,255代表完全不透明
R(Red) 红色,取值范围 [0,255],0代表无色,255代表红色
G(Green) 绿色,取值范围 [0,255],0代表无色,255代表绿色
B(Blue) 蓝色,取值范围 [0,255],0代表无色,255代表蓝色

其中ARGB的取值范围均为0~255(即16进制的0x00~0xff)
A 从0x00到0xff表示从透明到不透明。
RGB 从0x00到0xff表示颜色从浅到深。
当RGB全取最小值(0或0x000000)时颜色为黑色,全取最大值(255或0xffffff)时颜色为白色
示例代码:

canvas.drawARGB(255, 0, 0, 0);
canvas.drawARGB(255, 255, 0, 0);
canvas.drawARGB(255, 0, 255, 0);
canvas.drawARGB(255, 0, 0, 255);

效果图分别为:

ARGB.png

  • Canvas绘制形状:

canvas.drawPoint(float x, float y, Paint paint) //绘制点
canvas.drawPoints( float[] pts, Paint paint) //绘制多个点

canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint) //绘制线
canvas.drawLines(float[] pts, Paint paint)//绘制多条线

canvas.drawRect(float left, float top, float right, float bottom, Paint paint) //绘制矩形
canvas.drawRect(RectF rect, Paint paint) //绘制矩形

canvas.drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint) //绘制圆角矩形
canvas.drawRoundRect(RectF rect, float rx, float ry, Paint paint) //绘制圆角矩形

canvas.drawCircle(float cx, float cy, float radius,Paint paint) 绘制圆

canvas.drawOval(float left, float top, float right, float bottom,Paint paint) //绘制椭圆
canvas.drawOval(RectF oval,Paint paint) //绘制椭圆

canvas.drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter,Paint paint) //绘制圆弧
canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint) ////绘制圆弧

1. 画点:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(15f);
canvas.drawPoint(400, 400, mPaint);
float[] pts = {500, 500, 500, 600};
canvas.drawPoints(pts, mPaint);

drawPoint前两个参数为点的坐标(x,y),而drawPoints方法第一个参数是float[]数组,每两个代表一个坐标,如上面{500, 500, 500, 600}代表两个坐标点(500,500)和(500,600),效果图:
point.png
2. 画线:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(15f);
canvas.drawLine(200, 500, 800, 500, mPaint);
float[] pts = {200, 200, 500, 500,
               800, 200, 500, 500};
canvas.drawLines(pts, mPaint);

drawLine前四个参数代表起始坐标(x1,y1)和终点坐标(x2,y2),而drawLines第一个参数是float[]数组,每四个代表一组起始坐标和终点坐标的组合,如上图{200, 200, 500, 500,800, 200, 500, 500}代表两组坐标点:起始坐标(200,200)终点坐标(500, 500),以及起始坐标(800, 200)和终点坐标(500, 500),效果图:

line.png

3. 画矩形:

Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
//1.直接通过坐标画矩形
canvas.drawRect(200, 200, 1000, 600, mPaint);
//2.通过Rect 画矩形
Rect mRect = new Rect(200, 650, 1000, 1050);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawRect(mRect, mPaint);
//3.通过RectF 画矩形
RectF mRectF = new RectF(200, 1100, 1000, 1500);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
canvas.drawRect(mRectF, mPaint);

通过获得四个点的值,即左上角(x1,y1)和右下角(x2,y2)的坐标来确定矩形,效果图:

rectangle.png
4. 画圆:

drawCircle(float cx, float cy, float radius, Paint paint)

(cx,cy)是圆点坐标,radius是圆半径,示例:

Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10F);
mPaint.setColor(Color.RED);
canvas.drawCircle(600, 600, 200, mPaint);

效果图:

circle.png
5. 画椭圆:

drawOval( RectF oval,Paint paint)
drawOval(float left, float top, float right, float bottom, Paint paint)

第一个方法看源码还是调用的第二个方法,示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(10f);
//第一种,通过left top right bottom来确定矩形,然后画矩形的内切椭圆
canvas.drawOval(300, 100, 900, 500, mPaint);
RectF mRectF = new RectF(100, 600, 1000, 900);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
//第二种,通过RectF 画椭圆,和第一种原理是一样的
canvas.drawOval(mRectF, mPaint);

效果图:
oval.png
6. 画圆角矩形:

//第一个方法必须SDK>=21以上
drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
drawRoundRect(RectF rect, float rx, float ry, Paint paint)

示例:

float rRadius=20f;
Paint mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10f);
//第一种 必须SDK>=21以上使用
canvas.drawRoundRect(200, 100, 1000, 500, rRadius, rRadius, mPaint);
//第二种通过RectF 来画圆角矩形
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
RectF mRect = new RectF(200, 600, 1000, 1000);
canvas.drawRoundRect(mRect, rRadius, rRadius, mPaint);

RectF构造函数中的四个参数分别为上,左,右,下坐标,即左上角和右下角坐标,rx,ry表示的圆弧半径(椭圆圆弧),效果图:

roundrectangle.png
7. 画弧:

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,Paint paint)
drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, Paint paint)

第一个方法默认也会去调用第二个方法,看第二个方法:

drawArc 备注
left 左上角坐标的X轴坐标
top 左上角坐标的Y轴坐标
right 右下角坐标X轴坐标
bottom 右下角坐标Y轴坐标
startAngle 起始角度
sweepAngle 扫过的角度
useCenter 是否使用中心
paint 画笔

示例:

Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(10f);
//通过下面前两个圆弧对比很明显,useCenter为true,最后图形是一个扇形,
//useCenter为false,则最后图形是起始点和结束点之间的连线和圆弧围成的面积。
canvas.drawArc(100, 100, 500, 500, 0, 90, true, mPaint);
canvas.drawArc(100, 500, 500, 900, 0, 90, false, mPaint);
//通过RectF 设置的区域范围画弧
RectF mRectF = new RectF(100, 900, 500, 1300);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(mRectF, 0, 90, true, mPaint);

效果图:
arc.png

  • Canvas画布操作:

canvas.translate(float dx, float dy) //平移
canvas.rotate(float degrees) //旋转
canvas.rotate(float degrees, float px, float py) //改变旋转中心并旋转
canvas.scale(float sx, float sy) //缩放
canvas.scale(float sx, float sy, float px, float py) //改变缩放中心并缩放
canvas.save(); //保存画布
canvas.restore(); //恢复画布

Canvas画布操作可以让我们用更容易的方式来绘制图形,Canvas画布操作只会对后面的绘制起作用,对前面已经绘制的不起作用。
1. translate
translate是对坐标系的平移,且是可以多次叠加的,默认是在屏幕的左上角(0,0),示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
//将坐标系移到Canvas宽度的一半,高度400的位置
canvas.translate(getMeasuredWidth() / 2, 400);
//绘制一个红色圆
mPaint.setColor(Color.RED);
canvas.drawCircle(0, 0, 100, mPaint);
//坐标系原点在前面位置的基础上再往下移动200像素
canvas.translate(0, 200);
//绘制一个黄色圆
mPaint.setColor(Color.YELLOW);
canvas.drawCircle(0, 0, 100, mPaint);
//坐标系原点在前面位置的基础上再往下移动200像素
canvas.translate(0, 200);
//绘制一个绿色圆
mPaint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 100, mPaint);

效果图:

translate.png

2. rotate
rotate是对坐标系的旋转,rotate有两个方法:

canvas.rotate(float degrees) 
canvas.rotate(float degrees, float px, float py) 

第二个方法中的(px,py)可以移动旋转中心然后旋转,示例:

Paint mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL);
//将坐标系移动到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
RectF rectF = new RectF(-300, -300, 0, 0);
//绘制一个红色矩形
mPaint.setColor(Color.RED);
canvas.drawRect(rectF, mPaint);
//将坐标系旋转180度,不会影响前面已经绘制的图形
canvas.rotate(180);
mPaint.setStyle(Paint.Style.FILL);
//绘制一个绿色矩形
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);

效果图:
rotate.png
上面代码如果改变旋转中心再旋转:

//将坐标系中心左移(-150,0),并旋转180度
canvas.rotate(180,-150,0);

效果图:

rotateCenter.png

可以旋转中心向左移了150像素,而原来的旋转中心已经不再是(0,0)了,而成了(150,0),旋转中心变了,旋转后绘制的图形位置也就不一样了。

3. scale

canvas.scale(float sx, float sy)
canvas.scale(float sx, float sy, float px, float py)

scale是缩放,缩放中心默认是坐标原点,且多次缩放是可以叠乘的,和rotate一样有2个方法,其中多出来的两个参数类似rotate的旋转中心,px,py是控制缩放中心位置,sx,sy是X轴和Y轴的缩放倍数,示例:

Paint mPaint = new Paint();
//坐标原点移到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
RectF rectF = new RectF(-300, -300, 0, 0);
mPaint.setColor(Color.RED);
//左上角绘制红色圆
canvas.drawRect(rectF, mPaint);
//X轴 Y轴分别缩放到原来的1/2并以原点(0,0)位对称点进行翻转
canvas.scale(-0.5f, -0.5f);
//绘制绿色的圆
mPaint.setColor(Color.GREEN);
canvas.drawRect(rectF, mPaint);

效果图:
scale1.png
修改上面scale方法:

canvas.scale(-0.5f, -0.5f, 100, 0)

效果图:
scale2.png
缩放中心向右移动100像素,X轴 Y轴分别缩放到原来的1/2,并且以新的缩放中心为对称点进行翻转,在修改一下scale参数:

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

效果图分别为:
initpintu.jpg
总结一下sx sy

sx sy取值范围 备注
(1,+∞) 根据缩放中心放大到原来的n倍
1 跟原来大小一样,没变化
(0,1) 根据缩放中心缩放
0 sx sy有一个取0图形就消失了
(-1,0) 根据缩放中心缩放并翻转
-1 翻转
(-∞,-1) 根据缩放中心放大并翻转

4. save、restore:

save:保存之前Canvas的状态,save之后,可以调用Canvas的平移、缩放、旋转、错切、裁剪等操作。
restore:恢复Canvas之前保存的状态,防止save后对Canvas执行的操作对后续的绘制有影响。
canvas的save 和 restore是成对使用(restore只能比save少,不能多),示例:

Paint paint = new Paint();
//保存画布
canvas.save();
//坐标原点移到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//以屏幕中心为坐标原点在(60,50)为圆心处绘制红色圆
paint.setColor(Color.RED);
canvas.drawCircle(60, 50, 100, paint);
//恢复画布
canvas.restore();
//恢复画布后,坐标原点(0,0)默认在屏幕左上角,
//即以屏幕左上角为坐标原点在(60,50)为圆心处绘制黑色圆
paint.setColor(Color.BLACK);
canvas.drawCircle(60, 50, 50, paint);

效果图:
保存恢复画布.png

上面代码中如果去掉save和restore过程,即:

Paint paint = new Paint();
//canvas.save();
//坐标原点移到屏幕中心
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//以屏幕中心为坐标原点在(60,50)为圆心处绘制红色圆
paint.setColor(Color.RED);
canvas.drawCircle(60, 50, 100, paint);
//canvas.restore();
//没有了save和restore操作,坐标原点没有恢复到屏幕左上角,还是在屏幕中心
//即以屏幕中心为坐标原点在(60,50)为圆心处绘制黑色圆
paint.setColor(Color.BLACK);canvas.drawCircle(60, 50, 50, paint);

效果图是:
nosave.png

为什么效果不一样呢?这是因为如果去掉save和restore过程,所有图像都是在坐标原点移动到屏幕中心后绘制的;如果有save和restore,那么在restore之后进行的图像绘制原点又回到了屏幕左上角,可见save和restore调用时机会影响到最后绘图结果,save和restore之间往往是进行一系列的Canvas操作。

  • drawPath
canvas.drawPath( Path path, Paint paint)

drawPath内容较多,见下篇:Android Canvas之Path操作

相关实践学习
使用PAI-EAS一键部署ChatGLM及LangChain应用
本场景中主要介绍如何使用模型在线服务(PAI-EAS)部署ChatGLM的AI-Web应用以及启动WebUI进行模型推理,并通过LangChain集成自己的业务数据。
机器学习概览及常见算法
机器学习(Machine Learning, ML)是人工智能的核心,专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能,它是使计算机具有智能的根本途径,其应用遍及人工智能的各个领域。 本课程将带你入门机器学习,掌握机器学习的概念和常用的算法。
相关文章
|
3天前
|
API Android开发 开发者
Android经典实战之使用ViewCompat来处理View兼容性问题
本文介绍Android中的`ViewCompat`工具类,它是AndroidX库核心部分的重要兼容性组件,确保在不同Android版本间处理视图的一致性。文章列举了设置透明度、旋转、缩放、平移等功能,并提供了背景色、动画及用户交互等实用示例。通过`ViewCompat`,开发者可轻松实现跨版本视图操作,增强应用兼容性。
20 5
|
18天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
39 2
|
1月前
|
Android开发
Android面试题之自定义View注意事项
在Android开发中,自定义View主要分为四类:直接继承View重写onDraw,继承ViewGroup创建布局,扩展特定View如TextView,以及继承特定ViewGroup。实现时需注意:支持wrap_content通过onMeasure处理,支持padding需在onDraw或onMeasure/onLayout中处理。避免在View中使用Handler,使用post系列方法代替。记得在onDetachedFromWindow时停止线程和动画以防止内存泄漏。处理滑动嵌套时解决滑动冲突,并避免在onDraw中大量创建临时对象。
23 4
|
1月前
|
Android开发
Android面试题之View的invalidate方法和postInvalidate方法有什么区别
本文探讨了Android自定义View中`invalidate()`和`postInvalidate()`的区别。`invalidate()`在UI线程中刷新View,而`postInvalidate()`用于非UI线程,通过消息机制切换到UI线程执行`invalidate()`。源码分析显示,`postInvalidate()`最终调用`ViewRootImpl`的`dispatchInvalidateDelayed`,通过Handler发送消息到UI线程执行刷新。
27 1
|
19天前
|
机器学习/深度学习 人工智能 算法
探索AI在医疗影像分析中的应用探索安卓开发中的自定义View组件
【7月更文挑战第31天】随着人工智能技术的飞速发展,其在医疗健康领域的应用日益广泛。本文将聚焦于AI技术在医疗影像分析中的运用,探讨其如何通过深度学习模型提高诊断的准确性和效率。我们将介绍一些关键的深度学习算法,并通过实际代码示例展示这些算法是如何应用于医学影像的处理和分析中。文章旨在为读者提供对AI在医疗领域应用的深刻理解和实用知识。
22 0
|
26天前
|
消息中间件 调度 Android开发
Android经典面试题之View的post方法和Handler的post方法有什么区别?
本文对比了Android开发中`View.post`与`Handler.post`的使用。`View.post`将任务加入视图关联的消息队列,在视图布局后执行,适合视图操作。`Handler.post`更通用,可调度至特定Handler的线程,不仅限于视图任务。选择方法取决于具体需求和上下文。
26 0
|
Android开发
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(二)
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(二)
157 0
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(二)
|
移动开发 Android开发 C++
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(一)
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(一)
135 0
【Android 逆向】Android 逆向通用工具开发 ( Android 平台运行的 cmd 程序类型 | Android 平台运行的 cmd 程序编译选项 | 编译 cmd 可执行程序 )(一)
|
1天前
|
JavaScript 前端开发 Java
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
9 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
|
5天前
|
Java Android开发 iOS开发
探索安卓与iOS开发的差异:平台选择对项目成功的影响
在移动应用开发的世界中,选择正确的平台是关键。本文通过比较安卓和iOS开发的核心差异,揭示平台选择如何影响应用的性能、用户体验和市场覆盖。我们将深入探讨各自的开发环境、编程语言、用户界面设计原则以及发布流程,以帮助开发者和企业做出明智的决策。
24 9