九、Android 图像动画:使用 XML 结构的基于帧的动画

在本章中,我们将通过使用 XML 标记向应用用户界面屏幕添加动画视觉元素,将我们已经给人留下深刻印象的 Hello World 应用带到新媒体认知的下一个层次。

Android 有两种不同类型的动画,第一种,我们将在本章重点介绍的,是基于帧的光栅动画。

光栅动画使用一系列位图(称为 cels (如果你在 2D 动画行业)或(如果你在电影或数字视频编辑行业)。第二种类型的动画是程序矢量动画,我们将在第 10 章中介绍,因为它完全不同。

在本章中,我们将介绍基于帧的动画理论,以及在从 LDPI 到 XHDPI 的所有四种不同图像像素密度分辨率下实现动画所需的 XML 逻辑结构。这将使我们的 Hello World 应用的 UI 设计达到一个新的水平。

基于帧的动画:概念和数据优化

由于基于光栅或帧的动画是由位图图像组成的,我们在过去两章中学习的关于图形设计和数字成像的许多技术概念在本章中也同样适用,并且每一个都在光栅动画中扮演一个角色,所以在本章中你也可以更多地练习这些数字成像概念。

可以使用索引色或真彩色位图图像来创建光栅动画。这些图像可以有一个 alpha 通道,因此基于帧的动画可以利用透明度,这对我们的特效来说是个好消息。我们可以用动画做同样的合成技巧,就像我们在前两章中对静态图像做的一样。

与我们的静态图像类似,我们还需要为每种屏幕密度提供密度匹配的图像,正如我们的静态图像一样,Android 会处理哪些像素密度用于哪些屏幕,只要我们的动画单元或帧支持所有不同的像素密度级别,就像我们在本书中一直虔诚地做的那样。

静态图像优化和运动图像(动画)优化之间的主要技术差异涉及动画的帧速率。帧速率通常用每秒FPS 来描述,这是一个定义一秒钟内显示多少图像的数字,相当于图像帧在屏幕上相互替换的速度。

更高(更快)的帧速率提供了更平滑的视觉外观,但需要更多的图像数据来创建,因为动画需要有更多的帧,并且每个帧都是 PNG32 图像,需要存储在应用的 drawable-dpi 文件夹中(或者是 PNG8 图像,如果可以这样优化的话)。

你在日常生活中熟悉的新媒体素材的典型帧速率是数字视频,它以 30 FPS 运行;电影胶片,以 24 FPS 运行;或者视频游戏,运行速度为 60 FPS 。值得注意的是运动错觉可以在少至 12 FPS 的时间内实现,我们将在本章稍后的动画数据足迹优化工作流程中使用这一鲜为人知的事实。

因此,我们光栅动画的数据足迹优化过程的一部分不仅涉及我们用于动画的图像帧的色深,还涉及我们将需要用来创建运动幻觉的图像帧的总数。较少的 PNG8 帧比大量的 PNG32 帧制作的动画数据更紧凑。

在本章中,我们将仅使用几帧和索引彩色图像来制作引人注目的真实照片动画,因此您可以看到如何使用很少的数据开销,让几千字节的图像数据在您的应用中创建令人信服的特殊效果。

我们将在本章介绍的另一个基本动画概念叫做循环。正如你可能知道的,动画可以播放一次,或者它们可以无缝循环并永远播放,或者它们可以通过我们的 XML 标记指定的变量循环播放一定次数。

循环有两种:无缝循环,帧循环播放,像这样 0123401234乒乓循环 像这样 012343210

在这一章中,我们将会看到这两种类型的动画循环,并且我们将会看到这两种类型对于不同类型的特效是如何有用的。

用 XML 实现帧动画:动画列表标签

在我们可以用 XML 创建定义动画帧编号及其持续时间(帧速率)的动画标记之前,我们需要做的第一件事是将可绘制素材复制到每个相应的 drawable-dpi 文件夹中,在本例中,这些素材是动画的 celsframes

找到名为:attack virus 96 frame 0attackvirus96frame5 的 PNG8 文件,复制到 /res/drawable-xhdpi 文件夹,最后重命名为attackvirus0.pngattackvirus5.png共六帧动画。请注意,我们在这里仅使用了总共六个帧来实现这个动画病毒细胞效果,并且我们使用了优化的索引颜色 PNG8 文件,因此我们在所有四种分辨率密度下实现该动画的总数据量仅为总数据的 120KB 或每个动画的 30KB 平均值(这又是每帧 5KB 的数据压缩平均值)。

创建一个新的资源类型的 Android XML 文件:Drawable for Frame Animation

一旦我们的源图像动画帧数据就位,我们需要创建引用这些帧的 XML 构造,并且作为单个动画素材名称(anim_virus)被引用。右击任意一个项目 /res/drawable 文件夹,从右键菜单中选择新建image Android XML 文件,打开如图图 9-1 所示的对话框。

9781430257462_Fig09-01.jpg

图 9-1。创建一个新的 Android XML 文件,名为 anim_virus.xml,根元素为<动画列表>的

Hello_World 项目中选择 Drawable 的资源类型,并将文件命名为 anim_virus ,然后在对话框中间的选择框中选择 < animation-list > 的根元素类型。注意在资源类型的下拉菜单中:有几个与动画相关的选项不要选择它们(直到下一章),因为它们用于程序(矢量)动画,而不是基于帧(光栅)的动画,后者只利用可绘制图形(位图图像)。

这里需要注意的另一件事是,因为我们现在在图 9-19-2 中涉及到它,Android 在可绘制素材文件夹中保存帧动画素材和定义,而 Android 在动画素材文件夹中保存程序动画素材和定义。

9781430257462_Fig09-02.jpg

图 9-2。将<项目>标签添加到<动画列表>标签容器中,对病毒细胞图像进行 pong 动画制作

类似地,Android 中每种不同类型的动画也有不同的类和包,所以不要混淆 Android 动画包(包含用于过程或矢量动画的类)和用于基于帧或光栅动画的 AnimationDrawable 类。

如果你想知道,程序动画使用更少的图像和更多的处理能力,因此产生更小的应用大小(更好的压缩),而帧动画使用更多的图像和更少的处理能力,因为 CPU 只是从内存中获取数据并将其放在屏幕上。

最终,帧动画使您可以控制每个像素在做什么以及在哪里做,并且在效果的结果中没有任何差异,但它可能需要应用有更多的数据。正如我们将在下一章中看到的,同时使用这两种技术可以达到一个很好的平衡。

在你点击了新的 Android XML 对话框中的 Finish 按钮之后(图 9-1 ,你现在应该有一个 anim_virus.xml shell 脚本在 Eclipse 中为你打开(图 9-2 )。

这当然是由对话框助手创建的,并且包含了 <动画列表> 标签元素的框架 XML 标记,正如你可能已经猜到的,这是一个父容器,我们将用动画帧 <项目> 标签填充它。

在我们开始通过标签用动画帧填充容器之前,有一个重要的参数需要添加到我们的父标签中,这个参数叫做 android:oneshot 参数。该关键参数定义动画是无缝循环还是简单地播放一次(一个镜头)。因为我们希望我们的病毒永远脉动,将此设置为

添加我们的帧动画标签,指定动画帧

每个 <项> 标签指定一个帧参考和持续时间,格式如下:

<item android:drawable="@drawable/attackvirus0" android:duration="100" />

android:drawablefilename reference 允许 Android 从正确的 drawable 文件夹中获得每个帧的正确分辨率密度版本。

android:duration 参数指定在屏幕上显示该帧的时间长度,使用一个毫秒值,在本例中为 100

您可能想知道这个毫秒持续时间值如何转化为我们之前了解的帧速率。数学计算起来相当简单;由于每秒钟内有一千毫秒的 T2,我们可以用 1000 除以我们的每帧毫秒值,这就得到我们的每秒帧数,或 FPS 值。在这种情况下,我们使用 10 FPS 来获得足够令人信服的振动病毒细胞。

从优化的角度来看,这次我们讨论的是 CPU 处理优化,用于创建效果的帧越少(帧速率越低,或者每帧可以分配的毫秒持续时间越多)越好。从逻辑上讲,20 FPS 的 CPU 工作量是 10 FPS 的两倍。

这是因为随着时间的推移,当我们向应用添加动画素材时,我们会要求 CPU(中央处理器)执行越来越多的任务,因此我们给 CPU 单独执行这些任务的时间越多,CPU 在执行这些任务之间就有越多的时间来执行我们要求它执行的其他任务。

从我们的 attack_virus.xml 文件 引用我们的帧动画定义 XML 文件

接下来,我们需要在现有代码中引用这个新的动画病毒细胞可绘制素材,用于我们的 activity_attack.xml 用户界面屏幕布局 xml 定义。我们观察到我们的 attackvirus ImageButton 用户界面元素当前引用了 attack_virus.xml 多状态 UI 元素定义,因此这是我们新的 anim_virus.xml 文件名必须被引用的地方,以维护按钮的多状态功能。

为此,我们必须找到(并打开)用于 attack_virus.xml 标记的标签,如图图 9-3 所示,并将我们的可绘制引用更改为现在指向新的 anim_virus XML 动画定义可绘制素材。

9781430257462_Fig09-03.jpg

图 9-3。将我们的多态 attack_virus.xml 的正常状态改为引用 anim_virus.xml 文件

记下图 9-3/res/drawable 文件夹下的 anim_virus.xml 文件,以及现在已经就位的新Android:drawable = " @ drawable/anim _ virus "参数。如果你现在使用运行 Android 应用工作流程并进入攻击一个星球 UI 屏幕,你会注意到你必须点击病毒一次来激活(启用)它以使病毒开始活动。

这是 Android 中动画资源的默认行为,并且有一个非常合乎逻辑的原因。通常你希望当你点击一个 UI 元素时触发一个动画,我们也将在本章中设置这些动画类型之一。

因此,在默认的 Android 动画行为中,当你点击 UI 元素时,动画就会触发,并且,如果你将 oneshot 设置为 true ,你就会得到你想要的效果。在下一章中,我们将使用 LaserCannon pulse,向您展示这种类型的 XML 设置。然而,就目前而言,我们必须弄清楚如何让这种攻击病毒始终保持活动状态,即使用户没有首先点击它。

利用 Android state_enabled 参数使我们的病毒在活动屏幕启动时活跃起来

为了让我们的病毒在屏幕启动时有动画效果,我们添加了一个 Android:state _ enabled 参数到我们的默认(正常)按钮状态项目标签,在 attack_virus.xml 文件中指定,如图 9-3 中的所示。这将该按钮状态定义为在该用户界面屏幕加载时已经启用。当你再次作为 Android 应用运行时,当用户界面屏幕出现时,我们的病毒立即活跃起来。

现在,我们已经了解了如何为 ImageButton 用户界面元素实现动画源图像,让我们更进一步,为我们的应用的母星制作一个背景用户界面元素的动画(母星显示在应用的主屏幕上)。这显示了我们的星球力场设置,当它被激活的时候。

我们使用循环动画帧,而不是 pong 动画帧(我们用于动画攻击病毒细胞的来回帧类型的帧访问)来实现这一点,因此您有实现这两种类型的动画帧访问顺序的经验。

XML 中的高级帧动画:使用动画背景

为了实现我们的 Forcefield 动画,我们需要做的第一件事是确保我们的动画帧素材就位,所以让我们将 forcefield120frame0.png、forcefield160frame0.png、forcefield240frame0.png 和 forcefield320frame0.png 分别复制到 LDPI、MDPI、HDPI 和 XHDPI /res/drawable 文件夹中。我们通过 Windows 资源管理器文件实用程序来实现这一点。

一定要把每个文件名末尾编号为 0 到 6 的七帧全部复制到它们合适的分辨率密度资源文件夹中,然后简单的全部重命名:forcefield0.pngforcefield6.png,如图图 9-4 所示。一旦完成这些,我们就为 XML 和 Java 代码做好了准备。

9781430257462_Fig09-04.jpg

图 9-4。将力场动画帧复制并重命名到每个 drawable-dpi 文件夹中,以准备我们的素材

现在,我们可以通过右键单击项目文件夹并选择 New image Android XML File 来获得更多实现帧动画的练习,这次是循环而不是 pong。如果你忘记了这看起来像什么,参考本章开始的图 9-1

将文件命名为 anim_forcefield 并使用资源类型:Drawable项目:Hello_World根元素:< animation-list > 就像我们之前做的一样。

如果你想知道我们的命名惯例,以及为什么我们要复制 Android activity_name 命名惯例,这是因为当文件被 OS 或 Eclipse 排序(按字母顺序排列)时,逻辑文件被分组在一起,所以所有 attack_names 和 anim_names 以有序的方式保持分组在一起。

在 anim_forcefield.xml 文件中添加我们的动画帧标签

您现在应该在 Eclipse 中打开一个 XML 编辑窗格,其中包含新的 anim_forcefield.xml 文件,以及一个骨架 <动画列表> 容器,我们将在其中放置指定动画帧的< item >子标签。

我们要做的第一件事是添加 android:oneshot="false" 参数,这样我们的动画就会永远循环下去。注意, false 是 oneshot 的默认值,所以如果你愿意,你可以完全省略这个参数。因为我们将在本书的不同地方改变循环参数(并学习它们),所以我总是在父容器中编写这个参数,因为这是很好的编程实践。

现在我们准备为我们的动画帧添加七个标签,使用我们之前实现 pong 动画时使用的相同的 android:drawable 和 android:duration 参数。这如图 9-5 中的所示,请注意我们的帧无缝循环,从第 6 帧到第 0 帧,帧速率为 12 FPS (80 毫秒是 1 秒的 12.5 倍)。这是它的 XML 标记:

<animation-list xmlns:android=http://schemas.android.com/apk/res/android
                android:oneshot="false" >
    <item android:drawable="@drawable/forcefield0" android:duration="80" />
    <item android:drawable="@drawable/forcefield1" android:duration="80" />
    <item android:drawable="@drawable/forcefield2" android:duration="80" />
    <item android:drawable="@drawable/forcefield3" android:duration="80" />
    <item android:drawable="@drawable/forcefield4" android:duration="80" />
    <item android:drawable="@drawable/forcefield5" android:duration="80" />
    <item android:drawable="@drawable/forcefield6" android:duration="80" />
</animation-list>

9781430257462_Fig09-05.jpg

图 9-5。将每个力场动画帧的<项>标签添加到父<动画列表>容器标签 T3】

接下来是在同一个用户界面元素容器中同时使用背景图像(或者,在本例中是动画)和前景图像(称为源)的新技术,在本例中是 ImageView 标记。

在单个 UI 元素中合成:同时使用源图像和背景图像

为了完成这个合成,我们将向主屏幕末尾的 ImageView 标签添加几个新参数,这是通过 activity_main.xml 文件中的 XML 定义的。在 Eclipse 中打开这个编辑标签,添加参数Android:background = " @ drawable/anim _ forcefield "来引用您刚刚创建的动画列表 XML,这样您就可以引用您的 force field 动画素材中的所有帧。

我们要添加的第二个参数与背景参数一样重要,因为它允许我们推出 ImageView 容器的边界,因为我们的背景动画素材比我们的星球图像大 25%(大小的 125%)。前景(源)图像资源和背景动画资源都利用阿尔法通道;这是为了让它们无缝地组合在一起。

添加 android:padding="30dp" 参数,将 30 个设备无关像素添加到 ImageView UI 元素容器内的中。由于我们将地球图片推离了 UI 屏幕的左侧,也推离了它上面的文本字段,所以让我们也从这个 ImageView 标签中移除或删除 android:marginTop 和 android:marginLeft 标签。

图 9-6 显示了我们新的 ImageView 标签及其参数,同样数量的参数(我们添加了两个,删除了两个)实现了更高级的特殊效果。如果你想调整行星和它的力场之间的空间大小,你可以改变 android:padding DP 设置,从 24dp(紧配合)到 40dp(间隔很远)。请记住,您可以使用 30dp 或 30dip 作为您的 dip 参数,Android 将接受它。

9781430257462_Fig09-06.jpg

图 9-6。引用 anim_forcefield 作为 activity_main.xml 中 imageEarth ImageView 的背景图像

填充参数在 UI 元素的内部增加空间,而不是像 margin 参数那样在外部增加空间。这可以使用图形布局编辑器很好地可视化,因为 GLE 给你指南(蓝色和绿色)每个选择的用户界面元素相对于其他用户界面元素做什么。例如,在您删除边距参数之前,图形布局编辑器视图窗格显示了靠近行星图像的蓝色选择框和显示边距设置的绿色箭头,将整个容器推离父容器(屏幕边缘)和位于其上的文本元素。现在,注意图 9-7 中的 UI 容器本身已经扩展到适合我们新的背景动画和前景图像。

9781430257462_Fig09-07.jpg

图 9-7。使用 android:padding 预览 imageEarth 周围的 anim_forcefield 背景图片间距

我们可以只使用一个参数设置来改变我们的效果,因为我们使用 alpha 通道来设置一切;这样,无论我们如何设置填充或动画速度,结果都是专业的。

例如,您甚至可以在 Java 代码中将这个填充值制作成动画,或者将行星周围力场的紧密程度与其他物体与行星的接近程度联系起来。

图 9-7 显示了图形布局编辑器中的 30dp 填充设置。要根据您自己的喜好设置该参数值,请在 activity_main.xml 选项卡(查看编辑窗格的底部)和图形布局编辑器选项卡之间来回切换,以查看 DIP 设置对最终用户界面元素容器填充值的结果,在本例中,我们设置该值以影响行星在力场动画中的居中方式。截至撰写本书时,该动画还不能在 GLE 播放。要观看动画,您需要使用作为 Android 应用运行的工作流程,并启动 Nexus S 模拟器。

用 Java 编写我们的 AnimationDrawable 对象来实现我们的力场动画

在我们能够启动模拟器并看到动画之前,我们需要添加一些 Java 代码,如图 9-8 所示,将我们的 ImageView 和 AnimationDrawable 类和对象添加到我们的 MainActivity.java 类中,以便 Android 知道它们在那里。记住,设计者可以用 XML 实现 UI 设计,但是程序员通常必须使用 Java 来实现它们。

9781430257462_Fig09-08.jpg

图 9-8。编写一个 setStartUpScreenAnim()方法来设置我们的 AnimationDrawable 并调用它的 start()方法

的确,Android 使得实现动画 ImageButton UI 元素成为可能,而无需编写任何 Java 代码(我将在每章的开头展示最酷和最容易实现的技巧),并且只使用 XML 标记。然而,对于 ImageView UI 元素,由于它们更多地被设计为保存图像素材而不是用作 UI 元素,我们将需要编写一些 Java 代码,以确保当您的应用启动时动画正在播放。

这对我来说也很好,因为如果可以的话,我会在每一章中介绍一些 Java 编码,让这些概念在你的脑海中保持新鲜。

让我们在我们的 MainActivity.java 类中创建一个名为 setUpScreenAnim( ) 的新 Java 方法,该方法将初始化 ImageView,设置其背景图像,并调用 AnimationDrawable 类来制作我们在此设置的背景动画素材的动画。

在 Eclipse 中找到并打开 MainActivity.java 编辑选项卡,折叠 setStartUpWorldValues()、SetStartUpScreenText()、onCreateOptionsMenu()和 onOptionsItemSelected()方法,以便我们有一些空间来编写新方法。

像我们的 setStartUpScreenText()方法一样,将新方法设为 privatevoid ,并将其命名为 setStartUpScreenAnim。代码应该如下所示:

private void setStartUpScreenAnim() { animation code will go in here }

现在我们有了一个空方法,可以编写 Java 代码语句来实现 ImageView 及其 AnimationDrawable 背景元素。

我们需要做的第一件事是使用以下代码声明、命名和实例化我们的 homePlanet ImageView 用户界面元素:

ImageView homePlanet = (ImageView)findViewById(R.id.imageEarth);

下一行代码(它在图 9-8 中被注释掉了,因为我们不需要它,因为我们在 XML 中定义了一个背景)显示了如何在 Java 中设置你的背景素材。注意,因为我们在 ImageView 标签中使用了 android:background 参数,所以我们不需要在 Java 中重复这样做。然而,如果我们没有这个 XML 背景参数,我们将需要这行代码。因此,出于学习目的,我将它包含在这里:

homePlanet.setBackgroundResource(R.id.anim_forcefield); (commented out in Figure9-8)

下一行代码做了一些繁重的工作,并使用 Android AnimationDrawable 类设置了我们的 forcefield 动画,使用了以下单行代码:

AnimationDrawable forceFieldAnimation = (AnimationDrawable)homePlanet.getBackground();

最后,现在我们的 AnimationDrawable 对象名为 forceFieldAnimation 已经设置好了,使用来保存我们的动画资源。getBackground( ) 方法调用了 homePlanet 的 ImageView 对象,我们可以使用方便的。start( ) 方法使用下面这行非常简单的代码启动动画:

forceFieldAnimation.start();

现在,我们准备在 Nexus S 模拟器中使用作为 Android 应用运行的工作流程来测试我们的应用。一旦我们这样做,我们可以看到我们的力场动画正在我们的应用主屏幕上运行,地球现在使用填充值而不是边距值与屏幕左侧及其上方的文本元素隔开。通过这种方式,我们可以远离其他 UI 元素,并使用相同的标签参数为背景动画效果腾出空间。这都显示在图 9-9 中,尽管你必须自己运行模拟器,才能看到力场动画的运行!

9781430257462_Fig09-09.jpg

图 9-9。在 Nexus S 模拟器中运行我们的 Forcefield 动画 ImageView UI 元素背景

在本节中,我们仅使用 400KB 的总 PNG8 图像资源,实现了所有四种分辨率密度的七帧动画力场效果。这相当于平均每分辨率 100KB 或平均每帧 14KB。

接下来,我们将在我们的 Configure a Planet 用户界面屏幕上实现一个更加令人印象深刻的视觉特效,因此我们在应用的每个主要用户界面屏幕上添加了帧动画(或基于图像的动画)特效。

让我们来看看如何通过内置的 Android 图像过渡特效,同时动画显示图像中的所有像素。

图像过渡是数字成像特效的基础,如变形,以及数字成像交叉渐变过渡

全屏 XML 帧动画:背景图像过渡

这次让我们使用一个不同的工作流程来创建一个图像转换 XML 定义文件,这样您就可以看到在 Eclipse 中有不止一种方法可以做到这一点。右键单击项目的 /res/drawable 文件夹,选择新建image文件菜单命令序列,弹出新建文件对话框(创建一个包含任何数据类型的文件,不仅仅是 XML 数据),如图图 9-10 所示。

9781430257462_Fig09-10.jpg

图 9-10。使用新建文件对话框创建 tran_stars_galaxy XML 文件

请注意,新文件对话框会自动填充我们右键单击以访问新image文件菜单序列的父文件夹,因此我们现在要做的就是指定文件名。将文件命名为 tran_stars_galaxy ,代表从恒星到星系的跃迁。

为图像转换 XML 定义设置标签

当在 Eclipse 中为 tran_stars_galaxy.xml 文件打开空白编辑标签时,键入包含 < item > 标签的 < transition > 父标签,这些标签定义了作为该图像转换 xml 定义一部分的图像可绘制元素。开始的<转换>标签包含 XML 模式地址,就像所有开始的容器标签一样,如下所示:

<transition xmlns:android="http://schemas.android.com/apk/res/android" >

在这个父图像过渡容器中,放置两个 < item > 标签,第一个用于源图像,第二个用于目标图像。<项目>标签使用 android:drawable 参数以通常的方式指向可绘制图像素材,通过对源图像的 @drawable/stars 引用和对目标图像的 @drawable/galaxy 引用。

一旦你的 tran_stars_galaxy.xml 文件看起来像我们在图 9-11 中所写的标记,我们需要做的下一件事是准备我们的现有代码来实现图像转换,打开我们名为 activity_add.xml 的 xml 文件,并使用 android:id 参数将一个 ID 值赋给我们的父 RelativeLayout 布局容器标签。

9781430257462_Fig09-11.jpg

图 9-11。使用<过渡>标签父容器通过<项>标签定义数字图像过渡元素

我们添加它的原因是因为我们需要在 Java 代码中引用并在以后访问这个屏幕布局容器。这是因为这个 RelativeLayout 容器保存了用户界面屏幕的背景图像。由于这是 UI 元素(RelativeLayout 屏幕布局容器的背景图像),我们试图使用 Java 中的 image transition 对象将它渐变为新图像,因此我们必须能够通过 Java 代码中的 findViewById( ) 方法来访问它。

我们在父 RelativeLayout 容器打开标签的最末端添加了Android:id = " @+id/new _ planet _ screen ",如图图 9-12 所示。注意,标记参数的顺序没有区别;我们可以把 tag ID 参数放在前面,也可以把它放在参数列表的最后。

9781430257462_Fig09-12.jpg

图 9-12。使用 android:id 参数命名 RelativeLayout 容器,以便在 Java 中引用

为 Hello_World 升级 AndroidManifest XML 中的应用 API 级别支持

为了在布局容器中使用图像过渡,我们需要做的下一件事是为我们的项目设置 AndroidManifest.xml 文件,以指定 API 级别 16 (Jelly Bean)。这是一项高级功能,需要高级 API 级别的支持才能执行。

为此,在 Eclipse 中打开包含您的 AndroidManifest.xml 文件的 Hello_World Manifest 选项卡。值得注意的是,在图 9-13 中,这是 Eclipse 项目中唯一一个不会简单显示您正在编辑的确切文件名的选项卡。您可以在底部的包资源管理器中看到这一点,其中实际的文件名显示为 AndroidManifest.xml,但是 Eclipse 在编辑窗格选项卡中将其修改为 Hello_World Manifest!请不要让这迷惑你;Eclipse 只是想变得聪明和先进。不要以为可以将 AndroidManifest.xml 文件命名为 Hello_WorldManifest.xml,否则项目将无法编译。记住,Android 操作系统是硬编码的只寻找(并找到)一个名为 AndroidManifest.xml 的引导文件,所以只使用那个文件名。

**9781430257462_Fig09-13.jpg

图 9-13。将我们的 android:minSdkVersion 调整到 API 级别 16,以允许屏幕布局背景过渡

为了指定我们使用的是 API 16 的最低 API 支持级别,以及 API 16 的当前 API 支持级别,我们为 xml 标记顶部附近的 AndroidManifest.xml 文件中的 < uses-sdk > 标记调整了这些标记参数,结果显示在图 9-13 中。

android:minSdkVersion 参数设置为 16 的值,表示Android 4 . 1 . 2Jelly Bean API 16 级支持。保留这个标签中的另一个参数, android:targetSdkVersion 设置为 17 的当前值,或者Android 4 . 2 . 2Jelly Bean Plus API Level 17 支持。我们的货单看起来不错!

您可能想知道这是否会降低您的应用在旧平台和设备上的功能。如果你还记得的话,在第一章中我们安装了一个 API 以及其他所有的东西,这些东西都是在旧 API 级别的 API 上模拟当前 API 级别的支持。我的猜测是,通过图像过渡来混合布局容器背景在任何 API 级别上都不会太难,所以这也可以在 Android 3.x 和 4.0 平台上工作。

这也是一种令人惊叹的应用特性,如果它不能在旧平台上工作(正确实现),它就不会发生;也就是说,背景图像过渡不会发生,但是最终用户不会意识到这一点,因为没有什么会明显出错,除非该用户已经在实际上支持该背景图像过渡的平台上利用了该应用。

在我们的 Java 代码中添加一个 TransitionDrawable 对象来实现图像转换

现在我们准备好了有趣的东西!我们需要在我们的 NewPlanet.java Java 类定义中添加三行关键代码,我在 Activity onCreate()和 setContentView()方法调用之后添加了它们,因为我们正在设置对象并将它们连接在一起。稍后在 marsImage 对象的 onClick()方法中,当用户点击 Mars 时,我们将调用这个效果。

第一行代码设置了我们的 TransitionDrawable 对象,我们将把它命名为 trans ,并且我们将引用: tran_stars_galaxy.xml 文件,该文件保存了我们的< transition >定义标记。Java 代码应该是这样的,如图图 9-14 所示:

final TransitionDrawable trans = (TransitionDrawable)getResources().getDrawable(R.id.trans_stars_galaxy);

9781430257462_Fig09-14.jpg

图 9-14。在我们的 NewPlanet.java 代码中实现我们的 RelativeLayout 和 TransitionDrawable 对象

我们需要使我们的对象成为 final ,因为我们从类中更深层次的 onClick()方法内部引用它,final 关键字防止 trans 对象被编辑,本质上使它成为一个对象常量,这实际上是我们希望这个过渡效果可用并且每次都相同,由调用它的类中的任何方法使用。

接下来,我们需要实例化我们的 RelativeLayout 容器对象,将其命名为 newPlanetScreen ,并将其引用到我们的 activity_add.xml 布局中,在这里对其进行了定义,并在最近赋予了一个 android:idnew_planet_screen

这与我们在 Java 中实例化任何用户界面元素的方式相同,通过将它声明为一个对象,并将其命名,如下所示:

RelativeLayout newPlanetScreen = (RelativeLayout)findViewById(R.id.new_planet_screen);

既然我们已经声明了一个 trans TransitionDrawable 对象和我们的newPlanetScreenrelative layout 对象,接下来我们需要做的就是将它们连接在一起,这样它们就可以作为一个团队一起工作,来创建我们想要实现的背景图像过渡效果。

这是通过将我们最近的newPlanetScreenrelative layout 容器对象的背景元素(参数)设置为我们创建的 TransitionDrawable 对象来执行我们的图像转换来完成的。记住,我们将这个对象命名为 trans ,我们将通过使用一行简单但强大的 Java 代码来完成这个任务,这些代码通过将这两个对象连接在一起(或者更准确地说,引用 newPlanetScreen 对象内部的 trans 对象)。setBackground( ) 方法调用:

newPlanetScreen.setBackground(trans);

现在我们的数字图像特效已经用 XML(在三个 XML 文件中)和 Java 实现了,并且可以用。我们的 planet onClick()方法中的 start()方法调用,其中一个方法我们已经为我们的 imageMars 用户界面对象编写了代码,因此我们现在可以通过添加这一小段代码来测试所有这些新代码:

trans.startTransition(5000);

这行代码通过 trans TransitionDrawable 对象的调用它。start( ) 方法,它启动过渡,并传递给它一个以毫秒为单位的持续时间变量,在这个特定的例子中,我们使用了 5000 毫秒(或 5 秒)来创建当前 stars.png 图像到新 galaxy.png 图像之间的缓慢交叉淡入淡出效果。

所有这些新的 Java 代码都可以在图 9-14 中看到,在类的中间(我在 Java 代码的三个核心行周围留了一些空白),以及在底部,用浅蓝色突出显示的一行。

现在,我们可以在 Package Explorer 窗格中右键单击我们的项目文件夹,并选择 Run As Android Application 命令来启动 Nexus S 模拟器,并查看我们的背景图像转换效果!

当 Nexus S 模拟器启动且应用完成加载时,单击菜单按钮并选择新行星优先菜单选项,并加载新行星活动用户界面屏幕。然后点击火星星球图像,看星星背景慢慢交叉淡化,变成星系背景图像,如图图 9-15 所示。因为到目前为止我们已经在我们的数字图像中使用了 alpha 通道来开发这个应用,最终结果是 100%专业的无缝合成,即使在淡入淡出期间,我们的六颗行星也在我们的新背景图像上。

9781430257462_Fig09-15.jpg

图 9-15。Nexus S 模拟器中显示的 galaxy.png 背景图像过渡

现在我们知道了如何使用 Android 中的 AnimationDrawable 和 TransitionDrawable 类实现位图动画和图像过渡,我们可以在下一章的过程动画中将我们的应用提升到更高的视觉效果艺术水平。

使用程序动画,其中我们使用 XML 参数修改静态图像和运动动画(是的,当它通过其帧进行运动动画时),用于平移(移动)、缩放、旋转、alpha 通道(混合)等,我们可以通过特殊效果实现更大数量级的视觉效果。

所以让我们总结一下我们在这一章中学到的关于基于光栅图像的动画的知识,然后直接进入矢量动画,这样我们就可以使用这些类和技术来合成 WOW!Hello World 应用视觉效果的因素。

摘要

在这一章中,我们利用我们在过去几章中学到的数字成像概念和技术知识,通过 Android 的帧动画功能将这些技术带入第四维时间,并应用它们来进一步增强我们的 Hello World 应用。

首先,我们学习了一些数字图像动画的基本概念,它建立在我们在前两章学习的静态图像概念的基础上。我们了解到,基于图像的动画通常被称为光栅动画帧动画,这种类型的动画由 cels组成,它们随着时间的推移快速显示,以创建运动的幻觉。

我们学习了动画的核心概念帧速率,以及如何计算任何给定动画的 FPS每秒帧数。我们了解到视频游戏使用最快的帧速率 60 FPS ,而数字视频(电视)使用一半的帧速率,或 30 FPS 。电影(电影)使用的帧速率为 24 FPS ,数字媒体使用的帧速率甚至更低,范围从 10 FPS20 FPS ,以节省数据空间。

我们学习了如何通过优化图像的色深(使用优化的索引彩色图像),以及创建我们正在寻找的视觉特效所需的帧数,来优化我们应用的基于帧的动画的数据足迹。

我们为 Hello_World 应用实现了几个动画效果。一个六帧动画在所有四个 Android 分辨率密度目标上仅使用了 120 千字节的图像数据,或者一个紧凑的 30KB 每分辨率屏幕密度。我们的力场动画有七帧,使用了 400KB,但这是一个包含整个星球的更大的效果,仍然平均只有 100KB 的每 Android 分辨率密度的总帧数据。

我们了解了循环的概念和 android:oneshot 参数,该参数控制动画是否无缝循环,或者是否只是播放一次然后停止。我们探索了 pong 循环的概念,其中一个循环在它的组成帧之间来回循环。我们还学习了无缝循环,其中动画的循环被预先设计为无缝循环,因此不需要反转其帧方向。

然后,我们开始在我们的 Hello World Android 应用中实现基于帧的动画,使用 <动画列表> 父标签及其子标签 <项目> ,它们定义了动画帧的可绘制素材以及每个帧的持续时间。

我们学习了 Android 的 AnimationDrawable 类,以及如何通过在 Android 应用活动的 Java 代码中声明的 AnimationDrawable 对象来实现基于图像的(可绘制的)动画。我们学会了如何称呼。我们的 AnimationDrawable 对象上的 start( ) 方法。

我们仅使用 XML 标记代码,在我们的“攻击一个星球”活动用户界面屏幕上生动地展示了我们的攻击病毒图标,并仅使用 XML 标记和参数以及零 Java 代码,将这种脉动病毒实现为 pong 循环动画用户界面元素。

我们通过用基于 XML 的帧动画定义替换 ImageButton UI 元素的源图像来实现这一点,然后我们使用一个 android:state_enabled 参数来在活动屏幕完成加载后自动生成病毒动画。

然后我们升级到更高级的 UI 技术,使用我们的背景图像板作为用户界面元素来保存我们的动画帧,并将它们与 UI 元素的前景(源)图像合成。

我们在应用主屏幕上实现了这种 UI 内合成技术,通过使用 ImageView 标签背景参数来保存无缝循环的 7 帧力场动画,我们再次使用父标签和子标签在 XML 文件中定义了该动画,从而在地球周围添加力场。

然后我们研究了 AndroidTransitionDrawable类,以及它在我们的 Android 应用中实现数字图像过渡的能力。我们完全从头开始创建了一个 XML 文件,并实现了一个 <过渡> 标签父容器,以及两个子 <项目> 标签,它们定义了我们想要在其间过渡的图像。

因为我们想变得更巧妙(阅读:高级)并在 RelativeLayout 用户界面屏幕布局容器元素的背景参数(图像容器)中使用图像过渡,我们需要在我们的 AndroidManifest.xml 文件中将我们应用的最低 API 支持升级到 Android API 级别 16 和目标 API 级别 17。然后,在我们的MainActivity.java类中,我们仅使用四行 Java 代码将所有东西连接在一起。

在下一章中,我们将通过学习程序性矢量动画来建立我们在本章中学习的帧动画知识,这些动画可以与帧动画结构结合使用,或者与简单的静态图像结合使用,甚至与非图像 UI 元素结合使用,例如 TextViews。**