Android自定义系列——4.Canvas操作

2024-06-24 05:18

本文主要是介绍Android自定义系列——4.Canvas操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.画布操作
为什么要有画布操作?
画布操作可以帮助我们用更加容易理解的方式制作图形。
⑴位移(translate)
translate是坐标系的移动,可以为图形绘制选择一个合适的坐标系。 请注意,位移是基于当前位置移动,而不是每次基于屏幕左上角的(0,0)点移动,如下:

// 省略了创建画笔的代码// 在坐标原点绘制一个黑色圆形
mPaint.setColor(Color.BLACK);
canvas.translate(200,200);
canvas.drawCircle(0,0,100,mPaint);// 在坐标原点绘制一个蓝色圆形
mPaint.setColor(Color.BLUE);
canvas.translate(200,200);
canvas.drawCircle(0,0,100,mPaint);

先将坐标系移动一段距离绘制一个圆形,之后再移动一段距离绘制一个圆形,两次移动是可叠加的。

⑵缩放(scale)
缩放提供了两个方法

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

这两个方法中前两个参数是相同的分别为x轴和y轴的缩放比例。而第二种方法比前一种多了两个参数,用来控制缩放中心位置的。

缩放比例(sx,sy)取值范围详解:

取值范围(n)说明
(-∞, -1)先根据缩放中心放大n倍,再根据中心轴进行翻转
-1根据缩放中心轴进行翻转
(-1, 0)先根据缩放中心缩小到n,再根据中心轴进行翻转
0不会显示,若sx为0,则宽度为0,不会显示,sy同理
(0, 1)根据缩放中心缩小到n
1没有变化
(1, +∞)根据缩放中心放大n倍
// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,-400,400,0);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.scale(0.5f,0.5f);                // 画布缩放mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

接下来我们使用第二种方法让缩放中心位置稍微改变一下,如下:

// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,-400,400,0);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.scale(0.5f,0.5f,200,0);          // 画布缩放  <-- 缩放中心向右偏移了200个单位mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

由于本次未对缩放中心进行偏移,所有默认的缩放中心就是坐标原点,中心轴就是x轴和y轴。

本次缩放可以看做是先根据缩放中心(坐标原点)缩放到原来的0.5倍,然后分别按照x轴和y轴进行翻转。

// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,-400,400,0);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.scale(-0.5f,-0.5f,200,0);          // 画布缩放  <-- 缩放中心向右偏移了200个单位mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

本次对缩放中心点y轴坐标进行了偏移,故中心轴也向右偏移了。
注意:和位移(translate)一样,缩放也是可以叠加的。

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

调用两次缩放则 x轴实际缩放为0.5x0.5=0.25 y轴实际缩放为0.5x0.1=0.05

⑶旋转(rotate)
旋转提供了两种方法:

public void rotate (float degrees)public final void rotate (float degrees, float px, float py)

和缩放一样,第二种方法多出来的两个参数依旧是控制旋转中心点的。

默认的旋转中心依旧是坐标原点:

// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,-400,400,0);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.rotate(180);                     // 旋转180度 <-- 默认旋转中心为原点mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

改变旋转中心位置:

// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,-400,400,0);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.rotate(180,200,0);               // 旋转180度 <-- 旋转中心向右偏移200个单位mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

好吧,旋转也是可叠加的

canvas.rotate(180);
canvas.rotate(20);

调用两次旋转,则实际的旋转角度为180+20=200度。

为了演示这一个效果,我做了一个不明觉厉的东西:

⑷错切(skew)
skew这里翻译为错切,错切是特殊类型的线性变换。

错切只提供了一种方法:

public void skew (float sx, float sy)

参数含义:
float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值,
float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值.

变换后:

X = x + sx * y
Y = sy * x + y

示例:

// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,0,200,200);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.skew(1,0);                       // 水平错切 <- 45度mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

如你所想,错切也是可叠加的,不过请注意,调用次序不同绘制结果也会不同

// 将坐标系原点移动到画布正中心
canvas.translate(mWidth / 2, mHeight / 2);RectF rect = new RectF(0,0,200,200);   // 矩形区域mPaint.setColor(Color.BLACK);           // 绘制黑色矩形
canvas.drawRect(rect,mPaint);canvas.skew(1,0);                       // 水平错切
canvas.skew(0,1);                       // 垂直错切mPaint.setColor(Color.BLUE);            // 绘制蓝色矩形
canvas.drawRect(rect,mPaint);

⑸快照(save)和回滚(restore)

相关API简介
save把当前的画布的状态进行保存,然后放入特定的栈中
saveLayerXxx新建一个图层,并放入特定的栈中
restore把栈中最顶层的画布状态取出来,并按照这个状态恢复当前的画布
restoreToCount弹出指定位置及其以上所有的状态,并按照指定位置的状态进行恢复
getSaveCount获取栈中内容的数量(即保存次数)

状态栈:这个栈可以存储画布状态和图层状态。

画布和图层:画布是由多个图层构成的
实际上我们之前讲解的绘制操作和画布操作都是在默认图层上进行的。
在通常情况下,使用默认图层就可满足需求,但是如果需要绘制比较复杂的内容,如地图(地图可以有多个地图层叠加而成,比如:政区层,道路层,兴趣点层)等,则分图层绘制比较好一些。
你可以把这些图层看做是一层一层的玻璃板,你在每层的玻璃板上绘制内容,然后把这些玻璃板叠在一起看就是最终效果。

SaveFlags

名称简介
ALL_SAVE_FLAG默认,保存全部状态
CLIP_SAVE_FLAG保存剪辑区
CLIP_TO_LAYER_SAVE_FLAG剪裁区作为图层保存
FULL_COLOR_LAYER_SAVE_FLAG保存图层的全部色彩通道
HAS_ALPHA_LAYER_SAVE_FLAG保存图层的alpha(不透明度)通道
MATRIX_SAVE_FLAG保存Matrix信息( translate, rotate, scale, skew)

save
save 有两种方法:

// 保存全部状态
public int save ()// 根据saveFlags参数保存一部分状态
public int save (int saveFlags)

可以看到第二种方法比第一种多了一个saveFlags参数,使用这个参数可以只保存一部分状态,更加灵活,这个saveFlags参数具体可参考上面表格中的内容。

每调用一次save方法,都会在栈顶添加一条状态信息,以上面状态栈图片为例,再调用一次save则会在第5次上面载添加一条状态。

saveLayerXxx
saveLayerXxx有比较多的方法:

// 无图层alpha(不透明度)通道
public int saveLayer (RectF bounds, Paint paint)
public int saveLayer (RectF bounds, Paint paint, int saveFlags)
public int saveLayer (float left, float top, float right, float bottom, Paint paint)
public int saveLayer (float left, float top, float right, float bottom, Paint paint, int saveFlags)// 有图层alpha(不透明度)通道
public int saveLayerAlpha (RectF bounds, int alpha)
public int saveLayerAlpha (RectF bounds, int alpha, int saveFlags)
public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha)
public int saveLayerAlpha (float left, float top, float right, float bottom, int alpha, int saveFlags)

使用saveLayerXxx方法,也会将图层状态也放入状态栈中,同样使用restore方法进行恢复。

restore
状态回滚,就是从栈顶取出一个状态然后根据内容进行恢复。
同样以上面状态栈图片为例,调用一次restore方法则将状态栈中第5次取出,根据里面保存的状态进行状态恢复。

restoreToCount
弹出指定位置以及以上所有状态,并根据指定位置状态进行恢复。
以上面状态栈图片为例,如果调用restoreToCount(2) 则会弹出 2 3 4 5 的状态,并根据第2次保存的状态进行恢复。

getSaveCount

获取保存的次数,即状态栈中保存状态的数量,以上面状态栈图片为例,使用该函数的返回值为5。
不过请注意,该函数的最小返回值为1,即使弹出了所有的状态,返回值依旧为1,代表默认状态。

常用格式

save();      //保存状态
...          //具体操作
restore();   //回滚到之前的状态

这篇关于Android自定义系列——4.Canvas操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089259

相关文章

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

以canvas方式绘制粒子背景效果,感觉还可以

这个是看到项目中别人写好的,感觉这种写法效果还可以,就存留记录下 就是这种的背景效果。如果想改背景颜色可以通过canvas.js文件中的fillStyle值改。 附上demo下载地址。 https://download.csdn.net/download/u012138137/11249872

SQL Server中,always on服务器的相关操作

在SQL Server中,建立了always on服务,可用于数据库的同步备份,当数据库出现问题后,always on服务会自动切换主从服务器。 例如192.168.1.10为主服务器,12为从服务器,当主服务器出现问题后,always on自动将主服务器切换为12,保证数据库正常访问。 对于always on服务器有如下操作: 1、切换主从服务器:假如需要手动切换主从服务器时(如果两个服务