Skip to content

Latest commit

 

History

History
663 lines (432 loc) · 31.6 KB

File metadata and controls

663 lines (432 loc) · 31.6 KB

二十、绘制图形

这一整章都是关于安卓Canvas类和一些相关的类,比如PaintColorBitmap。当结合在一起时,这些类在屏幕上绘图时会带来巨大的力量。有时候,安卓应用编程接口提供的默认用户界面并不是我们所需要的。如果我们想制作一个绘图应用,绘制图表,或者制作一个游戏,我们需要控制安卓设备提供的每个像素。

在本章中,我们将涵盖以下主题:

  • 了解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

首先,我们来谈谈BitmapCanvasImageView,正如前面代码中所强调的。

开始使用位图、画布和图像视图进行绘制

由于安卓是为运行所有类型的移动应用而设计的,所以我们不能立即开始输入我们的绘图代码并期望它能工作。我们需要做一点准备(也就是更多的编码)来考虑我们的应用运行的具体设备。诚然,有些准备工作可能会有点违反直觉,但我们会一步一步来完成。

画布和位图

根据你如何使用Canvas类,这个术语可能会有点误导。虽然Canvas类,您将图形绘制到该类,例如绘画画布,但您仍然需要表面来转置画布。

在这种情况下(在我们的前两个演示应用中),表面将来自Bitmap类。

类型

请注意,位图是一种图像类型,安卓有一个Bitmap类。Bitmap类可以用来在屏幕上绘制位图图像,但是,正如我们将看到的,它也有其他用途。当谈到位图图像和Bitmap类时,我会尽量清晰,这样区别就越明显越好。

我们可以这样想这个过程:我们得到一个Canvas对象和一个Bitmap对象,然后将Bitmap对象设置为Canvas对象的一部分进行绘制。

如果你从字面意义上理解“画布”这个词,这有点违反直觉,但是一旦它都设置好了,我们就可以忘记它,专注于我们想要绘制的图形。

类型

Canvas类提供能力进行绘制。它具有绘制形状、文本、线条和图像文件(如其他位图)等所有功能,甚至支持绘制单个像素。

Bitmap类由Canvas类使用,是绘制的表面。您可以将Bitmap实例想象为位于Canvas实例的图片框内。

油漆

除了CanvasBitmap之外,我们将使用Paint类。这就容易理解多了;Paint是用于配置特定属性的类,例如我们将在Bitmap实例上绘制的颜色(在Canvas实例内)。

然而,在我们开始画东西之前,还有另一块拼图。

图像视图和活动

ImageView类是Activity类将用于向用户显示输出的类。第三层抽象的原因是,正如我们在整本书中看到的那样,Activity类需要传递一个ViewsetContentView函数的引用,以便向用户显示一些东西。在整本书中,这是我们在可视化设计器或 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

前面的代码声明了ImageViewBitmapCanvasPaint类型的引用。它们分别被命名为myImageViewmyBlankBitmapmyCanvasmyPaint

初始化对象

接下来,我们需要在使用新对象之前初始化它们:

// 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变量,分别叫做widthInPixelsheightInPixels。当我们编写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 键并点击回车键会快得多。如果弹出选项提示,选择导入类

一旦您对ImageViewBitmapCanvasPaint执行了此操作,所有错误都将消失,相关的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 值;然后,您可以试验阿尔法值。

用于清除绘图表面的值为25500255。这些值表示完全不透明(即纯色)、无红色、无绿色和完全蓝色。这就形成了蓝色。

argb函数的下一次调用是在对setColor的第一次调用中,我们正在为文本设置所需的颜色。255255255255值表示全不透明度、全红色、全绿色和全蓝色。当你把光和这些值结合起来,你就会变成白色。

argb的最后调用是在对setColor的最后调用中,我们在这里设置颜色画圆;2552120762为太阳黄色。

在运行代码之前,我们需要执行的最后一步是添加对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 像素的图形。在下一章中,我们将使用更先进的技术来利用整个屏幕,我们还将了解线程以使图形实时移动:

Explaining Color.argb

如果你对安卓坐标系有更多的了解,它将帮助你理解我们在Canvas绘图功能中使用的坐标结果。

安卓坐标系

正如您将看到的,绘制位图图形是微不足道的。然而,我们用来绘制图形的坐标系需要一个简单的解释。

绘图和绘图

当我们在屏幕上绘制位图图形时,我们传入我们想要将对象绘制到的坐标。给定安卓设备的可用坐标取决于其屏幕的分辨率。

例如,当保持横向时,谷歌像素手机的屏幕分辨率为 1920 像素(横向)乘以 1080 像素(向下)。

这些坐标的编号系统从左上角 0,0 开始,向下向右进行,直到右下角是像素 1919,1079。1,920/1,919 和 1,080/1,079 之间明显的 1 像素差异是因为编号从 0 开始。

因此,当我们在屏幕上绘制位图图形或其他任何东西(如Canvas圆形和矩形)时,我们必须指定一个 xy 坐标。

此外,位图图形(或Canvas形状)当然包括许多像素。那么,给定位图图形的哪个像素绘制在我们将要指定的 xy 屏幕坐标上?

答案是位图图形的左上角像素。请看下一张图表,它应该以谷歌 Pixel 手机为例阐明了屏幕坐标。作为解释安卓坐标绘图系统的图形手段,我将使用一个可爱的飞船图形:

Plotting and drawing

此外,坐标是相对于你所画的。所以,在我们刚刚编码的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);

以下是上一节中的宇宙飞船图形在屏幕上绘制时的样子(仅供我们讨论旋转位图时参考):

Creating bitmap graphics with the Bitmap class

操作位图

然而,我们经常需要绘制旋转或改变状态的位图。使用 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被吸引到屏幕上时的样子:

Inverting a bitmap to face the opposite direction

我们也可以使用旋转矩阵创建上下位图。

上下旋转位图

让我们看看旋转一个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画出来的样子:

Rotating the bitmap to face up and down

您也可以使用相同的技术,但使用不同的值,在参数中以preRotate向下面对位图。让我们继续使用演示应用,看看所有这些东西是如何运作的。

位图操作演示应用

现在我们已经学习了理论,让我们绘制并旋转一些位图。首先,创建一个新项目,并将其称为Bitmap manipulation。选择清空活动选项,所有其他设置如同在整本书中一样。

将鲍勃图形添加到项目中

右键单击,选择复制,从Chapter20/Bitmap Manipulation/drawable文件夹的下载包中复制bob.png图形文件。鲍勃,以bob.png为代表,是一个简单、静态的电子游戏角色。

在 Android Studio 中,在项目浏览器窗口中找到app/res/drawable文件夹,将bob.png图像文件粘贴到其中。下面的截图清楚地显示了这个文件夹的位置和里面的bob.png图片的样子:

Adding the Bob graphic to the project

右键点击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 度,如下图截图所示:

Adding the Bob graphic to the project

我们的绘画曲目中唯一缺少的东西是能够在活动发生时观看所有这些活动。接下来我们将弥补知识上的这一空白。

常见问题

问 1)我知道怎么画这些,但是为什么我看不到任何移动的东西?

a)要看到事物的移动,你需要能够控制绘图的每个部分何时发生。你需要使用动画技术。这不是微不足道的,但也不是一个坚定的初学者所不能理解的。我们将在下一章研究所需的主题。

总结

在本章中,我们看到了如何绘制自定义形状、文本和位图。既然我们知道了如何绘制和操作基本形状、文本和位图,我们就可以更进一步了。

在下一章中,我们将开始我们的下一个多章节应用,这是一个孩子风格的绘图应用,点击一个按钮就能激活。