这一整章都是关于安卓Canvas
类和一些相关的类,比如Paint
、Color
和Bitmap
。当结合在一起时,这些类在屏幕上绘图时会带来巨大的力量。有时候,安卓应用编程接口提供的默认用户界面并不是我们所需要的。如果我们想制作一个绘图应用,绘制图表,或者制作一个游戏,我们需要控制安卓设备提供的每个像素。
在本章中,我们将涵盖以下主题:
- 了解
Canvas
课程和一些相关课程 - 写一个基于
Canvas
的演示应用 - 看看安卓的坐标系,这样我们就知道在哪里画画了
- 了解如何绘制和操作位图图形
- 编写一个基于位图图形的演示应用
所以,让我们开始画画吧!
Canvas
类是android.graphics
包的一部分。在接下来的两章中,我们将使用android.graphics
包中的以下所有import
语句以及现在熟悉的View
包中的另一个语句。它们让我们可以从安卓应用编程接口访问一些强大的绘图功能:
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.widget.ImageView
首先,我们来谈谈Bitmap
、Canvas
和ImageView
,正如前面代码中所强调的。
由于安卓是为运行所有类型的移动应用而设计的,所以我们不能立即开始输入我们的绘图代码并期望它能工作。我们需要做一点准备(也就是更多的编码)来考虑我们的应用运行的具体设备。诚然,有些准备工作可能会有点违反直觉,但我们会一步一步来完成。
根据你如何使用Canvas
类,这个术语可能会有点误导。虽然Canvas
类是类,您将图形绘制到该类,例如绘画画布,但您仍然需要表面来转置画布。
在这种情况下(在我们的前两个演示应用中),表面将来自Bitmap
类。
请注意,位图是一种图像类型,安卓有一个Bitmap
类。Bitmap
类可以用来在屏幕上绘制位图图像,但是,正如我们将看到的,它也有其他用途。当谈到位图图像和Bitmap
类时,我会尽量清晰,这样区别就越明显越好。
我们可以这样想这个过程:我们得到一个Canvas
对象和一个Bitmap
对象,然后将Bitmap
对象设置为Canvas
对象的一部分进行绘制。
如果你从字面意义上理解“画布”这个词,这有点违反直觉,但是一旦它都设置好了,我们就可以忘记它,专注于我们想要绘制的图形。
Canvas
类提供能力进行绘制。它具有绘制形状、文本、线条和图像文件(如其他位图)等所有功能,甚至支持绘制单个像素。
Bitmap
类由Canvas
类使用,是绘制的表面。您可以将Bitmap
实例想象为位于Canvas
实例的图片框内。
除了Canvas
和Bitmap
之外,我们将使用Paint
类。这就容易理解多了;Paint
是用于配置特定属性的类,例如我们将在Bitmap
实例上绘制的颜色(在Canvas
实例内)。
然而,在我们开始画东西之前,还有另一块拼图。
ImageView
类是Activity
类将用于向用户显示输出的类。第三层抽象的原因是,正如我们在整本书中看到的那样,Activity
类需要传递一个View
对setContentView
函数的引用,以便向用户显示一些东西。在整本书中,这是我们在可视化设计器或 XML 代码中创建的布局。
然而,这一次,我们不需要用户界面——相反,我们想要绘制线条、像素、图像和形状。
有多个继承自View
的类,可以制作所有不同类型的应用,它们都将与Activity
类兼容,这是所有常规安卓应用(包括绘图应用和游戏)的基础。
因此,一旦绘制完成,有必要将绘制的Bitmap
类(通过其与Canvas
的关联)与ImageView
类相关联。最后一步是告诉Activity
类,我们的ImageView
将内容传递给setContentView
,让用户看到。
如果我们需要设置的代码结构的理论没有显得简单,那么当你看到后面相对简单的代码时,你会松一口气。
以下是我们到目前为止所学内容的快速总结:
- 每个应用都需要一个
Activity
类来与用户和底层操作系统进行交互。因此,如果我们想要成功,就必须遵循所需的层次结构。 - 我们将使用
ImageView
类,它继承自View
类。View
类是Activity
需要向用户显示我们的应用。 - 类提供了绘制线条、像素和其他图形的能力。它具有所有用于做事的功能,例如绘制形状、文本、线条和图像文件,甚至支持绘制单个像素。
Bitmap
类将与Canvas
类相关联,并且是被绘制的表面。Canvas
类使用Paint
类来配置细节,例如绘制的颜色。- 最后,一旦绘制了
Bitmap
实例,我们必须将其与ImageView
类相关联,该类又被设置为Activity
实例的视图。
结果将是我们在Canvas
实例中的Bitmap
实例上绘制的内容将通过调用setContentView
通过ImageView
实例显示给用户。唷!
如果不是 100%清楚也没关系。不是你看不清楚事情——这根本不是一种明确的关系。反复编写代码和使用技术会使事情变得更加清晰。看一下代码,执行本章和下一章中的演示应用,然后重新阅读这一部分。
让我们看看如何在代码中建立这种关系——不要担心输入代码;我们先研究一下。
让我们看一下代码和获取绘图所需的不同阶段,然后我们可以使用Canvas
演示应用快速开始真正的绘图。
第一步是把我们需要的类变成可用的实例。
首先,我们声明我们需要的所有实例。我们不能立即初始化实例,但我们可以确保在使用它们之前对它们进行初始化,因此我们使用lateinit
的方式与在动画演示应用中使用的方式相同:
// Here are all the objects(instances)
// of classes that we need to do some drawing
lateinit var myImageView: ImageView
lateinit var myBlankBitmap: Bitmap
lateinit var myCanvas: Canvas
lateinit var myPaint: Paint
前面的代码声明了ImageView
、Bitmap
、Canvas
和Paint
类型的引用。它们分别被命名为myImageView
、myBlankBitmap
、myCanvas
和myPaint
。
接下来,我们需要在使用新对象之前初始化它们:
// Initialize all the objects ready for drawing
// We will do this inside the onCreate function
val widthInPixels = 800
val heightInPixels = 600
// Create a new Bitmap
myBlankBitmap = Bitmap.createBitmap(widthInPixels,
heightInPixels,
Bitmap.Config.ARGB_8888)
// Initialize the Canvas and associate it
// with the Bitmap to draw on
myCanvas = Canvas(myBlankBitmap)
// Initialize the ImageView and the Paint
myImageView = ImageView(this)
myPaint = Paint()
// Do drawing here
请注意前面代码中的以下注释:
// Do drawing here
这是我们配置颜色和绘制图形的地方。另外,请注意在代码的顶部,我们声明并初始化了两个Int
变量,分别叫做widthInPixels
和heightInPixels
。当我们编写Canvas
演示应用时,我将更详细地介绍其中一些代码行。
我们现在准备抽签;我们所需要做的就是通过setContentView
功能将ImageView
实例分配给Activity
。
最后,在可以看到我们的绘图之前,我们告诉安卓使用我们的ImageView
实例,称为myImageView
,作为向用户显示的内容:
// Associate the drawn upon Bitmap with the ImageView
myImageView.setImageBitmap(myBlankBitmap);
// Tell Android to set our drawing
// as the view for this app
// via the ImageView
setContentView(myImageView);
正如到目前为止你已经在每个应用中看到的那样,setContentView
函数是Activity
类的一部分,这次我们传入myImageView
作为一个参数,而不是像我们在整本书中所做的那样是一个 XML 布局。就这样——我们现在要学的就是如何在Bitmap
实例上进行实际绘制。
在我们画一些图之前,开始一个真正的项目是有用的。我们将一步一步地将刚才讨论的代码复制并粘贴到正确的位置,然后实际上看到屏幕上绘制的一些东西。
所以,让我们画些画。
首先,创建一个新项目,用Canvas
探索绘画的话题。我们将重用我们所学的内容,这次,我们还将引用Bitmap
实例。
创建一个新项目并将其称为Canvas Demo
,并确保选择了空活动模板选项。
在这个应用中,我们会做一个我们以前没有见过的改变。我们将使用Activity
类的普通版本。因此MainActivity
将继承Activity
而不是AppCompatActivity
,就像以前一样。我们这样做是因为我们没有使用来自一个 XML 文件的布局,所以我们不需要像我们在之前所有项目中所做的那样AppCompatActivity
的向后兼容特性。
您应该按如下方式编辑类声明。
class MainActivity : Activity() {
您将还需要添加以下导入语句:
import android.app.Activity
这个应用的完整代码可以在Chapter20/Canvas Demo
文件夹的下载包中找到。
接下来,删除函数的所有内容,除了声明/签名、对 super.onCreate 的调用以及开大括号和闭大括号。
现在,我们可以在类声明之后,但在onCreate
函数之前添加以下高亮显示的代码。这是该步骤后的代码:
// Here are all the objects(instances)
// of classes that we need to do some drawing
lateinit var myImageView: ImageView
lateinit var myBlankBitmap: Bitmap
lateinit var myCanvas: Canvas
lateinit var myPaint: Paint
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
请注意,在 AndroidStudio 中,四个新类用红色下划线标出。这是因为我们需要添加适当的import
语句。您可以从本章的第一页复制它们,但是将鼠标光标依次放在每个错误上,然后按住 ALT 键并点击回车键会快得多。如果弹出选项提示,选择导入类。
一旦您对ImageView
、Bitmap
、Canvas
和Paint
执行了此操作,所有错误都将消失,相关的import
语句将被添加到代码的顶部。
现在我们已经声明了所需类的实例,我们可以初始化它们了。调用super.onCreate…
后,在onCreate
函数中添加以下代码,如下代码所示:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize all the objects ready for drawing
// We will do this inside the onCreate function
val widthInPixels = 800
val heightInPixels = 600
// Create a new Bitmap
myBlankBitmap = Bitmap.createBitmap(widthInPixels,
heightInPixels,
Bitmap.Config.ARGB_8888)
// Initialize the Canvas and associate it
// with the Bitmap to draw on
myCanvas = Canvas(myBlankBitmap)
// Initialize the ImageView and the Paint
myImageView = ImageView(this)
myPaint = Paint()
}
前面的代码与我们在理论上讨论Canvas
时看到的代码相同。但是Bitmap
类初始化并不简单,值得进一步探索。
位图更常见于基于图形的应用和游戏中,用于表示对象,例如用于绘画的不同画笔、玩家角色、背景、游戏对象等。在这里,我们只是用它来借鉴。在下一个项目中,我们将使用位图来表示我们的绘图主题,而不仅仅是要绘制的表面。
需要说明的功能是createBitmap
功能。从左到右的参数如下:
- 宽度(像素)
- 高度(像素)
- 位图配置
Bitmap
实例可以通过几种不同的方式进行配置;ARGB_8888
配置意味着每个像素由四个字节的内存表示。
安卓可以使用多种位图格式。这是一个很好的颜色范围,将确保我们使用的位图和我们要求的颜色将按照预期绘制完美。配置有高有低,但ARGB_8888
很适合这本书。
现在,我们可以进行实际的绘制。
在myPaint
初始化后,在onCreate
函数的右花括号内添加以下高亮显示的代码:
// Draw on the Bitmap
// Wipe the Bitmap with a blue color
myCanvas.drawColor(Color.argb(255, 0, 0, 255))
// Re-size the text
myPaint.textSize = 100f
// Change the paint to white
myPaint.color = Color.argb(255, 255, 255, 255)
// Draw some text
myCanvas.drawText("Hello World!",100f, 100f, myPaint)
// Change the paint to yellow
myPaint.color = Color.argb(255, 212, 207, 62)
// Draw a circle
myCanvas.drawCircle(400f, 250f, 100f, myPaint)
前面的代码使用:
myCanvas.drawColor
用颜色填充屏幕myPaint.textSize
属性定义了接下来将要绘制的文本的大小myPaint.color
属性决定了任何未来绘图的颜色myCanvas.drawText
功能实际上是将文本绘制到屏幕上。
如果我们分析传入drawText
的论点,可以看到文字会说“你好世界!”,它将从左侧 100 像素和我们的Bitmap
实例(myBitmap
)顶部 100 像素绘制。
接下来,我们再次使用color
属性来更改将用于绘制的颜色。最后,我们使用drawCircle
功能画一个从左边 400 像素,从顶部 100 像素的圆。该圆的半径为 100 像素。
到目前为止,我一直没有解释Color.argb
功能。
不出所料,Color
类帮助我们操纵和表现颜色。argb
函数返回使用 a lpha(用于不透明度和透明度)、 r ed、 g reen、 b lue 模型构建的颜色。该模型对每个元素使用从 0(无颜色)到 255(全颜色)的值。重要的是要注意——尽管看起来很明显——混合的颜色是不同颜色的光的强度,例如,结果与我们混合油漆时发生的情况非常不同。
为了设计一个 ARGB 价值并进一步探索这个模型,看看这个方便的网站:https://www.rapidtables.com/web/color/RGB_Color.html。网站帮助你挑选 RGB 值;然后,您可以试验阿尔法值。
用于清除绘图表面的值为255
、0
、0
和255
。这些值表示完全不透明(即纯色)、无红色、无绿色和完全蓝色。这就形成了蓝色。
对argb
函数的下一次调用是在对setColor
的第一次调用中,我们正在为文本设置所需的颜色。255
、255
、255
和255
值表示全不透明度、全红色、全绿色和全蓝色。当你把光和这些值结合起来,你就会变成白色。
对argb
的最后调用是在对setColor
的最后调用中,我们在这里设置颜色画圆;255
、21
、207
、62
为太阳黄色。
在运行代码之前,我们需要执行的最后一步是添加对setContentView
函数的调用,该函数将我们的ImageView
实例(myImageView
)作为要设置为该应用内容的视图。下面是在我们已经添加的代码之后,但是在onCreate
的右大括号之前的最后几行代码:
// Associate the drawn upon Bitmap with the ImageView
myImageView.setImageBitmap(myBlankBitmap);
// Tell Android to set our drawing
// as the view for this app
// via the ImageView
setContentView(myImageView);
最后,我们通过调用setContentView
告诉Activity
类使用myImageView
。
下面的截图展示了运行 Canvas 演示应用时的样子。我们可以看到一个 800×800 像素的图形。在下一章中,我们将使用更先进的技术来利用整个屏幕,我们还将了解线程以使图形实时移动:
如果你对安卓坐标系有更多的了解,它将帮助你理解我们在Canvas
绘图功能中使用的坐标结果。
正如您将看到的,绘制位图图形是微不足道的。然而,我们用来绘制图形的坐标系需要一个简单的解释。
当我们在屏幕上绘制位图图形时,我们传入我们想要将对象绘制到的坐标。给定安卓设备的可用坐标取决于其屏幕的分辨率。
例如,当保持横向时,谷歌像素手机的屏幕分辨率为 1920 像素(横向)乘以 1080 像素(向下)。
这些坐标的编号系统从左上角 0,0 开始,向下向右进行,直到右下角是像素 1919,1079。1,920/1,919 和 1,080/1,079 之间明显的 1 像素差异是因为编号从 0 开始。
因此,当我们在屏幕上绘制位图图形或其他任何东西(如Canvas
圆形和矩形)时,我们必须指定一个 x 、 y 坐标。
此外,位图图形(或Canvas
形状)当然包括许多像素。那么,给定位图图形的哪个像素绘制在我们将要指定的 x 、 y 屏幕坐标上?
答案是位图图形的左上角像素。请看下一张图表,它应该以谷歌 Pixel 手机为例阐明了屏幕坐标。作为解释安卓坐标绘图系统的图形手段,我将使用一个可爱的飞船图形:
此外,坐标是相对于你所画的。所以,在我们刚刚编码的Canvas
演示中,在下一个演示中,坐标是相对于Bitmap
对象(myBitmap
)的。在下一章中,我们将使用整个屏幕,并且前面的图表将更准确地表示正在发生的事情。
让我们再做一些绘图——这次是位图图形(再次是Bitmap
类)。我们将使用与在此应用中看到的相同的起始代码。
在深入研究代码之前,让我们先研究一下的一些理论,并仔细考虑我们将如何在屏幕上绘制图像。要绘制位图图形,我们将使用Canvas
类的drawBitmap
功能。
首先,我们需要在res/drawable
文件夹中的项目中添加一个位图图形——我们将在稍后的位图演示应用中实现这一点。现在,假设图形文件/位图的名称为myImage.png
。
接下来,我们将声明一个Bitmap
类型的对象,就像我们在前面的演示中用于背景的Bitmap
对象一样。
接下来,我们将需要使用我们的首选图像文件初始化myBitmap
实例,我们之前已经将该文件添加到项目的drawable
文件夹中:
myBitmap = BitmapFactory.decodeResource
(resources, R.drawable.myImage)
BitmapFactory
类的decodeResource
功能用于初始化myBitmap
。它需要两个参数;首先是Activity
级提供的resources
属性。顾名思义,这个函数提供对项目资源的访问,第二个参数R.drawable.myImage
指向drawable
文件夹中的myImage.png
文件。Bitmap
( myBitmap
)实例现在可以由Canvas
类绘制了。
现在,您可以使用以下代码通过Bitmap
实例绘制位图图形:
// Draw the bitmap at coordinates 100, 100
canvas.drawBitmap(myBitmap,
100, 100, myPaint);
以下是上一节中的宇宙飞船图形在屏幕上绘制时的样子(仅供我们讨论旋转位图时参考):
然而,我们经常需要绘制旋转或改变状态的位图。使用 Photoshop 还是很容易的,或者随便你喜欢的图像编辑软件碰巧是什么,从原来的位图创建更多的位图面向其他方向。然后,当我们来绘制我们的位图时,我们可以简单地决定哪种方式,并绘制适当的预加载位图。
然而,我认为如果我们只使用一个单一的源图像,并了解安卓提供的用我们的 Kotlin 代码操作图像的类,会更有趣和有启发性。然后,您将能够向应用开发人员的工具包中添加旋转和反转图形。
位图之所以被称为位图,是因为它就是位图:比特的图。虽然有许多位图格式使用不同的范围和值来表示颜色和透明度,但它们都是一样的。它们是值的网格或映射,每个值代表单个像素的颜色。
因此,要旋转、缩放或反转位图,我们必须对位图的图像、网格或贴图的每个像素或位执行适当的数学计算。计算并不十分复杂,但也不是特别简单。如果你把数学带到高中结束,你可能会很容易理解数学。
不幸的是,理解数学是不够的。我们还需要设计高效的代码以及理解位图格式,然后为每种格式修改我们的代码;这不是小事。幸运的是(正如我们所料),安卓应用编程接口已经为我们做好了一切——满足Matrix
类。
类被命名为Matrix
,因为它使用数学概念和规则对一系列称为矩阵的值进行计算——矩阵的复数。
安卓Matrix
类与同名电影系列无关。然而,作者建议所有有抱负的应用开发人员服用红色的“T2”药丸。
你可能对矩阵很熟悉,但是如果你不熟悉也不要担心,因为Matrix
类隐藏了所有的复杂性。此外,Matrix
类不仅允许我们对一系列值进行计算,而且它还具有一些预先准备的计算,使我们能够做一些事情,例如将一个点围绕另一个点旋转特定的度数。我们在对三角学一无所知的情况下得到这一切。
如果你对Matrix
课的幕后数学是如何工作的很感兴趣,并且想要一个绝对的旋转游戏对象数学入门指南,那就看看我网站上的这一系列安卓教程吧,它以一个可飞行和可旋转的宇宙飞船结束。这些教程是用 Java 编写的,但是应该很容易理解:
http://gamecode school . com/essentials/computing-2d 游戏中的航向-使用三角函数-part-1/
http://gamecode school . com/essentials/rotating-graphics-in-2d-games-use-三角函数-part-2/
http://gamecode school . com/Android/2d-旋转和航向-演示/
这本书将坚持使用安卓Matrix
类,但是我们将在下一章创建粒子系统时做稍微高级的数学运算。
首先,我们需要来创建一个Matrix
类的实例。下面的代码行通过调用默认构造函数以一种熟悉的方式实现了这一点:
val matrix = Matrix()
请注意,您现在不需要将这些代码添加到项目中;很快,我们将再次展示更多的内容。我只是觉得提前看到所有Matrix
相关的代码会更容易。
现在我们可以使用Matrix
类的许多简洁函数之一。preScale
功能取两个参数;一个用于水平变化,一个用于垂直变化。看看下面的代码行:
matrix.preScale(-1, 1)
preScale
函数将循环遍历每个像素位置,将所有水平坐标乘以-1
,将所有垂直坐标乘以1
。
这些计算的结果是,所有的垂直坐标将保持不变,因为如果你乘以 1,那么数字不会改变。但是,当您乘以负一时,像素的水平位置将反转。例如,水平位置 0、1、2、3 和 4 将变成 0、-1、-2、-3 和-4。
在这个阶段,我们已经创建了一个矩阵,可以对位图执行必要的计算。我们实际上还没有对位图做任何操作。为了使用Matrix
实例,我们调用Bitmap
类的createBitmap
函数,如下面一行代码所示:
myBitmapLeft = Bitmap
.createBitmap(myBitmapRight,
0, 0, 50, 25, matrix, true)
前面的代码假设myBitmapLeft
已经和myBitmapRight
一起初始化了。createBitmap
功能的参数解释如下:
myBitmapRight
是一个已经创建并缩放的Bitmap
对象,并且已经将图像(面向右侧)加载到其中。这是将用作创建新的Bitmap
实例的源的图像。源Bitmap
对象根本不会改变。0, 0
是我们希望新的Bitmap
实例映射到的水平和垂直起始位置。50, 25
参数是设置位图缩放大小的值。- 下一个参数是我们预先准备好的
Matrix
实例matrix
。 - 最后一个参数
true
指示createBitmap
功能需要过滤来正确处理Bitmap
类型的创建。
这就是myBitmapLeft
被吸引到屏幕上时的样子:
我们也可以使用旋转矩阵创建上下位图。
让我们看看旋转一个Bitmap
实例,然后我们就可以构建演示应用了。我们已经有了一个Matrix
类的实例,所以我们所要做的就是调用preRotate
函数来创建一个矩阵,该矩阵能够在preRotate
的单个参数中将每个像素旋转指定的度数。看看下面的代码行:
// A matrix for rotating
matrix.preRotate(-90)
这有多简单?matrix
实例现在可以逆时针(-
)旋转90
度,旋转我们传递给它的任何一系列数字(位图)。
下面一行代码的参数与我们之前剖析的对createBitmap
的调用相同,只是新的Bitmap
实例被分配给了myBitmapUp
,而matrix
的效果是执行旋转而不是preScale
功能:
mBitmapUp = Bitmap
.createBitmap(mBitmap,
0, 0, 25, 50, matrix, true)
这个就是myBitmapUp
画出来的样子:
您也可以使用相同的技术,但使用不同的值,在参数中以preRotate
向下面对位图。让我们继续使用演示应用,看看所有这些东西是如何运作的。
现在我们已经学习了理论,让我们绘制并旋转一些位图。首先,创建一个新项目,并将其称为Bitmap manipulation
。选择清空活动选项,所有其他设置如同在整本书中一样。
右键单击,选择复制,从Chapter20/Bitmap Manipulation/drawable
文件夹的下载包中复制bob.png
图形文件。鲍勃,以bob.png
为代表,是一个简单、静态的电子游戏角色。
在 Android Studio 中,在项目浏览器窗口中找到app/res/drawable
文件夹,将bob.png
图像文件粘贴到其中。下面的截图清楚地显示了这个文件夹的位置和里面的bob.png
图片的样子:
右键点击drawable
文件夹,选择粘贴将bob.png
文件添加到项目中。点击确定两次,确认将文件导入项目的默认选项。
在这个应用中,我们将进行与上一个应用相同的更改。我们将使用香草版本的Activity
类。因此MainActivity
将继承Activity
而不是AppCompatActivity
,就像以前一样。我们这样做是因为,同样,我们没有使用来自 XML 文件的布局,因此我们不需要AppCompatActivity
的向后兼容特性,就像我们在所有之前的项目中所做的那样。
您应该按如下方式编辑类声明。
class MainActivity : Activity() {
您还需要添加以下导入语句:
import android.app.Activity
在类声明之后和onCreate
函数之前,向MainActivity
类添加以下所需属性,准备绘制一些图形:
// Here are all the objects(instances)
// of classes that we need to do some drawing
lateinit var myImageView: ImageView
lateinit var myBlankBitmap: Bitmap
lateinit var bobBitmap: Bitmap
lateinit var myCanvas: Canvas
lateinit var myPaint: Paint
在包声明后添加以下导入:
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.widget.ImageView
现在,我们可以初始化onCreate
中的所有实例,如下所示:
// Initialize all the objects ready for drawing
val widthInPixels = 2000
val heightInPixels = 1000
// Create a new Bitmap
myBlankBitmap = Bitmap.createBitmap(widthInPixels,
heightInPixels,
Bitmap.Config.ARGB_8888)
// Initialize Bob
bobBitmap = BitmapFactory.decodeResource(
resources, R.drawable.bob)
// Initialize the Canvas and associate it
// with the Bitmap to draw on
myCanvas = Canvas(myBlankBitmap)
// Initialize the ImageView and the Paint
myImageView = ImageView(this)
myPaint = Paint()
// Draw on the Bitmap
// Wipe the Bitmap with a blue color
myCanvas.drawColor(Color.argb(
255, 0, 0, 255))
接下来,我们添加对我们即将编写的三个函数的调用,并将我们的新绘图设置为应用的视图:
// Draw some bitmaps
drawRotatedBitmaps()
drawEnlargedBitmap()
drawShrunkenBitmap()
// Associate the drawn upon Bitmap
// with the ImageView
myImageView.setImageBitmap(myBlankBitmap)
// Tell Android to set our drawing
// as the view for this app
// via the ImageView
setContentView(myImageView)
现在,增加drawRotatedBitmap
功能,执行位图操作:
fun drawRotatedBitmaps() {
var rotation = 0f
var horizontalPosition = 350
var verticalPosition = 25
val matrix = Matrix()
var rotatedBitmap: Bitmap
rotation = 0f
while (rotation < 360) {
matrix.reset()
matrix.preRotate(rotation)
rotatedBitmap = Bitmap
.createBitmap(bobBitmap,
0, 0, bobBitmap.width - 1,
bobBitmap.height - 1,
matrix, true)
myCanvas.drawBitmap(
rotatedBitmap,
horizontalPosition.toFloat(),
verticalPosition.toFloat(),
myPaint)
horizontalPosition += 120
verticalPosition += 70
rotation += 30f
}
}
前面的代码使用一个循环迭代 360 度,一次 30 度。该值(在每次循环中)在Matrix
实例中用于旋转鲍勃的图像,然后使用drawBitmap
功能将他绘制到屏幕上。
添加最后两个功能,如下:
fun drawEnlargedBitmap() {
bobBitmap = Bitmap
.createScaledBitmap(bobBitmap,
300, 400, false)
myCanvas.drawBitmap(bobBitmap, 25f, 25f, myPaint)
}
fun drawShrunkenBitmap() {
bobBitmap = Bitmap
.createScaledBitmap(bobBitmap,
50, 75, false)
myCanvas.drawBitmap(bobBitmap, 250f, 25f, myPaint)
}
drawEnlargedBitmap
功能使用createScaledBitmap
功能,将位图图形放大到 300×400 像素。drawBitmap
功能然后将其绘制到屏幕上。
drawShrunkenBitmap
函数使用完全相同的技术,除了缩放然后绘制一个 50 x 75 像素的图像。
最后,运行该应用,查看 Bob 的生长、收缩,然后以 30 度的间隔旋转 360 度,如下图截图所示:
我们的绘画曲目中唯一缺少的东西是能够在活动发生时观看所有这些活动。接下来我们将弥补知识上的这一空白。
问 1)我知道怎么画这些,但是为什么我看不到任何移动的东西?
a)要看到事物的移动,你需要能够控制绘图的每个部分何时发生。你需要使用动画技术。这不是微不足道的,但也不是一个坚定的初学者所不能理解的。我们将在下一章研究所需的主题。
在本章中,我们看到了如何绘制自定义形状、文本和位图。既然我们知道了如何绘制和操作基本形状、文本和位图,我们就可以更进一步了。
在下一章中,我们将开始我们的下一个多章节应用,这是一个孩子风格的绘图应用,点击一个按钮就能激活。