IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Android canvas -> 正文阅读

[游戏开发]Android canvas

1.Canvas

Canvas是一个画布,可以在这个画布上进行各种绘图操作。
在Android中,想要获得一个Canvas对象主要有三种方法:

①继承一个View,并重写onDraw()方法。View的Canvas对象会被当做参数传递过来,在这个Canvas上进行的操作会直接反映在View中。

②调用SurfaceHolder.lockCanvas()返回一个Canvas对象。

③通过构造方法自己创建一个Canvas对象。

Bitmap bitmap = Bitmap.createBitmap(100f, 100f, Config.ARGB_8888);

Canvas canvas = new Canvas(bitmap);

?

Canvas的坐标系:

画布以左上角为原点(0,0),向右为X轴的正方向,向下为Y轴的正方向:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_8,color_FFFFFF,t_70,g_se,x_16

?

2.Canvas的常用方法

①对整个Canvas坐标系的操作

//平移画布。dx,dy为x,y轴上的移动距离

public void translate(float dx, float dy)

//以坐标点(0,0)旋转画布。degress为旋转角度

public void rotate(float degrees)

//以坐标点(px,py)旋转画布。degress为旋转角度

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

//以坐标点(0,0)缩放画布。sx,sy为x,y轴上的缩放倍数

public void scale(float sx, float sy)

//以坐标点(px,py)缩放画布。sx,sy为x,y轴上的缩放倍数

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

//倾斜画布。sx,sy为x,y轴上的倾斜角度的tan值

public void skew(float sx, float sy)

?

3.Canvas的回退栈

当我们使用canvas的辅助函数,对canvas进行操作时,这些操作都是不可逆的。比如,在绘制某个内容之前,使用clipRect(0,0,100,100),那么之后的绘制就只能在[0,0,100,100]这个矩形内,除非在通过手动调用api,让canvas回到之前的某个状态。

Canvas在进行上面的平移、缩放、旋转、倾斜后,画布的状态也就随之改变。这可能对后面的绘图操作产生很多麻烦。比如我们为了某些效果不得不对画布进行一些操作,但操作完了,画布状态也就改变了。

为了避免发生这种情况,就可以在特定的位置进行保存和恢复,在进行变换前,使用save保存canvas当前的状态,然后进行变换,接着绘制我们要绘制的内容,最后再通过restore恢复之前保存的状态。

如果在一次绘制中,多次调用save方法,那么会将每次save时canvas的状态压入类似一个栈中,每一个状态都对应一个数字,代表其是栈中的第几个,可以通过方法restoreToCount(count),将canvas回退到指定的那个。也可以调用restore,一个一个的回退canvas的状态。

public int save()? ?每次调用该方法,都会把当前画布的状态进行保存,并存放在一个栈结构中

public void restore()? ?每次调用该方法,都会把栈中最顶层的画布状态取出来,并按照这个画布状态恢复当前画布。如果当前栈中没有保存的画布状态,则会抛出异常。

canvas还提供了一个restoreToCount(int saveCount) 来恢复画布状态。每次调用save()方法保存画布状态时都会返回一个int型的值。我们可以把该值直接传入restoreToCount()方法中直接恢复画布状态。状态恢复后,会将该状态和该状态顶部的其他画布状态一同出栈。

public void restoreToCount(int saveCount)? ?恢复指定的画布状态

需要注意的是,不管是调用restore还是restoreToCount,都需要在save的数量范围内,否者系统就会抛出异常。

?

4.Canvas的绘图操作

Canvas 拥有大量的drawXXX()方法,这些方法用于在画布上绘制各种图形,下面为一些常用的绘图方法:

//绘制颜色

public void drawColor(@ColorInt int color)

public void drawRGB(int r, int g, int b)

public void drawARGB(int a, int r, int g, int b)

//绘制圆

public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint)

//绘制点

public void drawPoint(float x, float y, @NonNull Paint paint)

//绘制多个点

public void drawPoints(@Size(multiple = 2) @NonNull float[] pts, @NonNull Paint paint)

//绘制一条直线

public void drawLine(float startX, float startY, float stopX, float stopY, @NonNull Paint paint)

//绘制多条直线

public void drawLines(@Size(multiple = 4) @NonNull float[] pts, @NonNull Paint paint)

//绘制一个矩形

public void drawRect(@NonNull RectF rect, @NonNull Paint paint)

public void drawRect(@NonNull Rect r, @NonNull Paint paint)

public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)

//绘制一个圆角矩形

public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint)

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, @NonNull Paint paint)

//绘制一个椭圆

public void drawOval(@NonNull RectF oval, @NonNull Paint paint)

public void drawOval(float left, float top, float right, float bottom, @NonNull Paint paint)

//绘制一个弧形

public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)

public void drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)

//绘制文本

public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint)

//沿Path路径绘制文本

public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)

//绘制位图

public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)

public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst, @Nullable Paint paint)

?

当我们使用canvas.drawXXX时,系统会在一个新的透明区域,绘制我们要绘制的内容,然后迅速与屏幕当前显示内容进行重叠,这个重叠的过程也会受xfermode或blendmode的影响。

如下示例,就演示了这个情况:

不设置xfermode:

override fun onDraw(canvas: Canvas) {

? ? //先将背景涂红

? ? canvas.drawColor(Color.RED)

? ? //在中心画一个绿色的圆

? ? paint.color = Color.GREEN

? ? canvas.drawCircle( width/2f, height/2f, radios/2f, paint)

}

得到的结果是这样的:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

?设置xfermode为DST:

override fun onDraw(canvas: Canvas) {

? ? //先将背景涂红

? ? canvas.drawColor(Color.RED)

? ? //在中心画一个绿色的圆

? ? paint.xfermode = PorterDuffXfermode( PorterDuff.Mode.DST)

? ? paint.color = Color.GREEN

? ? canvas.drawCircle( width/2f, height/2f, radios/2f, paint)

? ? paint.xfermode = null

}

得到的结果是这样的:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

?如果在绘制过程中只是给paint设置xfermode,而没有进行操作,如保存Canvas状态信息等,那么:

①如果设置的mode需要削掉DST(即已经在屏幕上显示的)部分或全部内容,那么这个mode不会生效

②如果设置的mode为SRC_OUT、DST_OUT或XOR时,那么SRC区域显示为黑色,再覆盖在已显示的内容上

?

5.使用layer综合绘制操作

既然只是直接使用paint.setXfermode设置的效果,会跟预期的不一致,那么应该怎么样才能获得预期的效果呢?

canvas提供了saveLayer方法,抽取一个透明区域,执行绘制方法,随后再一并将绘制的内容,覆盖在已显示内容上。

调用saveLayer()可以为canvas创建一个新的图层,在新的图层上的绘制并不会直接绘制在屏幕上,而是在restore()后绘制在上一个图层或者绘制在屏幕上(如果没有上一个图层)。创建一个新图层的好处之一是我们在处理xformode的时候,原图层上的图片和背景都会影响dst和src的合成。这时使用一个新图层是一个很好的选择。

//创建一个指定大小的图层

public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint)

public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint)

Canvas还提供了另外两个方法用于创建指定透明度的图层,在该图层上绘制的图形都会带有指定的透明度:

//创建一个指定大小和透明度的图层。参数alpha为透明度,取值为0到255

public int saveLayerAlpha(@Nullable RectF bounds, int alpha)

public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha)

使用和不使用saveLayer的大致工作流程:

①不使用layer

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

?②使用layer

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5a2f6Iqz6Iqz,size_20,color_FFFFFF,t_70,g_se,x_16

在调用saveLayer时,可以传入一个saveFlags参数,它有如下几个参数可以设置:

MATRIX_SAVE_FLAG? 只保存图层的matrix矩阵

CLIP_SAVE_FLAG? 只保存大小信息

HAS_ALPHA_LAYER_SAVE_FLAG? ? 表明该图层有透明度,和下面的标识冲突,都设置时以下面的标志为准

FULL_COLOR_LAYER_SAVE_FLAG? ?完全保留该图层颜色(和上一图层合并时,清空上一图层的重叠区域,保留该图层的颜色)

CLIP_TO_LAYER_SAVE_FLAG? ?创建图层时,会把canvas(所有图层)裁剪到参数指定的范围,如果省略这个flag将导致图层开销巨大(实际上图层没有裁剪,与原图层一样大)

ALL_SAVE_FLAG? 保存所有信息

?

?

?

?

需要注意,如果绘制过程需要对canvas进行多次的几何变换,那么需要倒叙来写几何变换过程。比如需要先平移再旋转,那么在写代码的时候,就需要先旋转再平移。

这里主要是因为屏幕的坐标系和canvas坐标系是两个坐标系,需要进行一定的的空间想象。

当然,也可以初始化一个Matrix,合理的使用preXXX和postXXX,对该Matrix进行几何变换操作,然后将其应用到canvas上。

?

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:58:26  更:2022-03-15 23:02:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 17:36:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码