安卓应用开发学习手册-三-

171 阅读1小时+

安卓应用开发学习手册(三)

原文:Learn Android App Development

协议:CC BY-NC-SA 4.0

十、Android 矢量动画:通过 XML 结构的程序动画

在这第二章中,我们将学习 Android 中的“其他”类型的动画,这种类型使用代码,而不是像素,来产生它的魔力。这种基于代码的动画通常被称为程序动画,涉及到范围枢轴点等内容。

业内使用了几个程序动画术语。如果你听到术语补间动画 ,它指的是一种创建动画的程序方式。这是因为补间实际上是插值,或者划分你想要你的动画跨越的帧数,在开始和结束值之间,或者范围,用于动画的计算。

我们将在下一节讨论插值和其他程序动画概念的数学问题,比如枢轴点。另一个你可能听说过的程序动画术语是矢量动画。矢量是一条射线或直线,但一般概念是动画是通过使用数学构造而不是使用栅格(像素)集合来创建的。

矢量和栅格是两种完全不同的方法;栅格是数据密集型,而矢量是数据密集型。这是因为文本(数学)可以很好地压缩,而像素(和帧)的数组就不行。矢量使用更多的 CPU 资源(在进行计算时),而栅格使用更多的存储或带宽资源(传输数据)。这是因为一个数学向量需要被渲染到显示屏上,这意味着数学被转化为图形元素,通常是运动图形,或 2D 动画。

因此,Adobe Illustrator(或 InkScape)是一个矢量成像软件包,而 Adobe Photoshop(或 GIMP 2)是一个光栅成像软件包。

程序动画概念:旋转、缩放和平移

让我们从学习矢量图像和动画的一些概念开始。首先,有两种主要类型的矢量平台, 2D二维(平面)矢量图形,如我们在 Illustrator 或 InkScape 中发现的,以及 3D三维(体积)矢量图形,如我们在 Blender 3D 等 3D 建模软件中发现的。

注意我们将在本章的这一节介绍的概念适用于 2D 和 3D 成像和动画。两者都使用向量;2D 的 2D 矢量,3D 的 3D 矢量,2D 和 3D 都涉及到核心概念平移(移动)旋转缩放。在 2D,这些概念涉及 XY 轴,在 3D 中,这些概念涉及 XYZ 轴。

2D 动画中有一个 Z 概念,但它不是 Z 轴,而是 Z 顺序。 Z 顺序在 2D 更像是数字成像中的图层,它涉及到每个 2D(平面)图层处于什么图层顺序,是在其他 2D 图层的前面还是后面。z 顺序是对 2D 合成中的层进行排序的数字,并且定义了给定 2D 层的前面和后面。实时更改 Z 顺序可以创建翻书特效。

2D 中的平移涉及到沿 X 轴和 Y 轴的移动,它是 2D 动画中可以完成的三个变换中最基本的。平移由移动的起点、移动的(以像素或百分比表示)和移动的方向(沿着 XY 轴,或两者的某种相对组合)来定义。

2D 中的旋转是指绕给定的支点旋转,由该旋转的度数、方向**(正或负)、旋转的支点(中心)位置定义。因为一整圈有 360 度,所以旋转数学专门涉及这个 360 的数字,就像 FPS 计算涉及数字 1000(一秒中的毫秒数)。**

2D 的比例涉及给定形状的大小,由相对于该形状当前大小的十进制数定义。例如, 0.5 的比例将是当前形状大小的一半,而 2.0 的比例将是当前形状大小的两倍**。像平移一样,缩放也有 X 和 Y 分量,如果值相同,缩放可以说是均匀缩放,如果不相同,缩放可以说是非均匀缩放。****

有趣的是,你也可以在你的缩放操作中定义一个枢轴点**,这允许倾斜缩放,其中你的缩放操作会受到枢轴点位置的影响。对于不规则的形状,这可以更精确地控制缩放操作产生的形状扭曲效果。假设有时被缩放的 2D 形状是位图图像,使用不在图像中心点的枢轴点放置可以获得一些非常有趣的结果。

实现旋转动画:攻击炸弹 UI 图标

让我们开始实现程序动画,在我们的攻击星球用户界面屏幕上制作一个静态图像的动画,使它看起来像一个动画图标。我们将旋转我们的 bomb ImageButton UI 元素,这样它就和我们的 virus ImageButton 一起被动画化了,我们在前一章中已经将 virus ImageButton 动画化了。因为我们为炸弹图像创建了一个 alpha 通道,这给了我们一个与空间图像背景无缝的 2D 效果。

右键单击您的项目资源文件夹,然后选择新建imageAndroid XML 文件菜单选项序列。在项目中选择补间动画的一个资源类型:设置 Hello_World 然后将文件命名为 anim_rot_bomb ,动画旋转炸弹图像的简称。接下来选择根元素旋转>最后点击完成按钮,如图图 10-1 所示。****

9781430257462_Fig10-01.jpg

图 10-1。用于补间动画(旋转)的新 Android XML 文件

这将打开一个带有 < rotate > 标签的空白 XML 文件,我们使用参数 填充该文件,以实现我们希望的旋转过程。

首先删除 < /rotate > 结束标签,因为这不会是一个父(容器)标签。将 <旋转> 开始标记拆分成一行 <旋转,然后在下一行添加一个结束结束标记符号 / > ,如图图 10-2 所示。

9781430257462_Fig10-02.jpg

图 10-2。通过 Eclipse 中的 android: parameter helper 对话框添加旋转动画参数

完成旋转标签

图 10-2 中还显示了我们输入 android: 的小技巧,以获得一个参数助手对话框,其中有一个可在 < rotate > 标签中使用的参数列表。在使用标记之前,您应该总是利用这种技术来调用和研究可用的参数,我们将在这个示例中尝试使用尽可能多的这些参数,向您展示它们都将做什么。

我们需要添加的第一件事是一个引用 http://schemas.android.com/apk/res/android URL 的 xmlns:android 参数,这样我们添加的任何参数都可以通过位于 android 网站上的当前规范进行验证。在添加这个初始的 XML 命名模式(XMLNS)引用之前,您添加的任何参数都将被标记为红色 X 错误,因为它不能被验证为正确的参数。这是因为附加在每个参数前的 android:等同于这里定义的 URL。

在旋转动画中定义最重要的事情是旋转参数本身,以度为单位,使用 android:fromDegreesandroid:toDegrees 参数。因为我们想要攻击炸弹图标的完整圆形无缝动画,我们将在这些设置中使用 0 到 360 度来获得炸弹图像的完整无缝旋转。

我们添加的下两个设置使用 X 和 Y 坐标建立了这个旋转的中心点,或枢轴点。在 Android 中,你的枢轴点是使用一个百分比建立的,从图像左上角的 0%100% 进入 2D 图像。通过设定这个百分比,我们的图像有多少像素并不重要,因此这适应了我们需要支持目前市场上许多类型的 Android 设备的各种分辨率图像。

因为我们希望图像围绕其中心点旋转,所以我们将使用设置为 50%android:pivotX 参数,以及设置为 50%android:pivotY 参数。如果图像(和 alpha)没有完全位于像素中心,可能会有一些抖动。请注意,与返回到 GIMP 并计算炸弹图像上方和侧面的边框像素相比,这在这个 XML 标记中更容易解决(即,通过使用 49%和 51%的设置来稍微对角移动枢轴)。

接下来我们需要做的是通过使用一个 android:interpolator 参数,用一个在 Android OS 内部资源 R. 路径中定义的插值器常数,为我们的 2D 动画设置一个插值方法

目前,Android OS 支持 13 插值器常量。每个插值器常量访问 Android 中的一个插值器类子类。

每个插值器子类都将数学曲线应用于动画帧速率,调整每个动画帧之间的过渡速度,以实现更复杂和逼真的运动效果。

如果您想详细研究这些插值器方法,Android 开发者网站上有一整页专门介绍这些方法,网址为:

http://developer.android.com/reference/android/view/animation/Interpolator.html

这里我们需要使用的插值器是一个线性插值器,它给我们一个很好的均匀移动沿着我们动画的所有部分。我们将在本章使用其他插值常量,所以你可以看到他们做什么,当他们应用到你的 2D 动画设置时看起来如何。如果您想在一个地方看到所有的 R .插补器常数,请访问:

http://developer.android.com/reference/android/R.interpolator.html

为了应用插值器常量,我们将使用 @android (操作系统)以及 :anim (动画资源)和 /linear_interpolator (常量的路径)从 Android 操作系统资源(R) 桶中引用它,如下所示:

android:interpolator="@android:anim/linear_interpolator"

接下来我们需要为动画定义 重复计数。这是通过一个 android:repeatCount 参数来完成的,该参数要么接受一个整数值(完成值的循环数),要么接受一个预定义的无限常数。

infinite 常量仅在存在需要永久动画的 UI 元素(如用户界面按钮或 UI 设计元素)时使用。

因为我们的攻击炸弹 ImageButton 图像图标属于这一类,所以我们将使用一个**Android:repeat count = " infinite "**参数来实现这个最终结果。

最后,我们需要定义动画的循环持续时间,也就是程序动画的循环发生的时间。

动画循环持续时间使用 android:duration 参数定义,动画的每个循环的确切时间(动画速度)以毫秒为单位定义。让我们从快速值 2000 毫秒开始,或每旋转 180 度一秒,稍后,如果这太快,我们可以将其值增加到 8000 毫秒或更多。

现在我们已经实现了 <旋转> 标签参数,我们需要这些参数来定义我们的炸弹旋转 2D 动画(使用大约一半的可用参数,如图图 10-2 所示),接下来我们可以用 Java 实现动画。XML 标记及其参数应该包含以下标记:

<?xml version="1.0" encoding="utf-8"?>
  <rotate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:interpolator="@android:anim/linear_interpolator"
    android:repeatCount="infinite"
    android:duration="2000"
  />

图 10-3 显示了在 Eclipse 中完成的 anim_rot_bomb.xml 文件。

9781430257462_Fig10-03.jpg

图 10-3。为 Eclipse 中的 anim_rot_bomb.xml 文件配置< rotate >标签的旋转定义参数

为旋转添加 Java 代码

我们的动画触发器的 Java 代码逻辑上定义在 AttackPlanet.java 文件中,因为该类控制攻击功能的活动屏幕。单击 Eclipse 编辑窗格右上角的数字,下拉打开文件菜单,然后选择:AttackPlanet.java。

在 Java 代码中,我们需要做的第一件事是在我们的.setContentView()方法调用下添加一行空格,这样我们就可以使用 Android Animation类声明一个Animation对象。将这个Animation对象命名为rotateBomb ,并像这样引用anim_rot_bomb XML:

Animation rotateBomb = AnimationUtils.loadAnimation(this, R.anim.anim_rot_bomb);

注意,声明一个动画和加载一个过程化的 XML 定义有一点不同,在等号的左边声明和命名动画对象,并调用**。loadAnimation( )** 方法通过从 AnimationUtils 类中取出点标记法。AnimationUtils 类使用其 loadAnimation 方法在其第二个参数中引用当前上下文( this )和 anim_rot_bomb.xml 中的动画 XML 定义。

当您键入这一行代码时,Eclipse red 会为您需要定义导入语句的类加下划线,如果您愿意,您可以单击这些并选择让 IDE 为您做这件事的链接。

现在我们已经设置了一个动画对象来为我们执行这个动画,我们所要做的就是将它连接到我们的 bombButton UI 对象并启动动画,这也是我们在一行紧凑的 Java 代码中完成的。

bombButton.startAnimation(rotateBomb);

这让我们的bombButton ImageButton对象调用它的startAnimation 方法,并将它传递给我们刚刚在它上面的代码行中创建的rotateBomb Animation对象。正如你在图 10-4 中看到的,实现一个程序化的动画只需要两行非常密集的 Java 代码,因为所有定义我们想要动画做什么的繁重工作都被卸载到 XML 中了。这使得设计人员可以专注于 UI 和动画设计,而 Java 编码人员则不必如此。相当天才的东西!

9781430257462_Fig10-04.jpg

图 10-4。添加一个名为 rotateBomb 的动画对象,并通过。start animation()

接下来,让我们变得更复杂一点,在我们的攻击病毒图标 ImageButton UI 元素中,将一些程序性(矢量)动画与我们现有的基于帧的(光栅)动画相结合。通过这种方式,您可以看到如何使用矢量和光栅动画世界的最佳组合,使用相对少量的 Java 和 XML 代码以及半打数字图像素材(帧)来获得高级的最终结果。

实现标量动画:脉冲攻击病毒 UI 图标

我们开始创建另一个程序动画 XML 文件,就像我们在图 10-1 中所做的一样,只是这次我们为我们的 XML 文件选择了 <缩放> 根元素,而不是<旋转>元素。这是因为我们将使我们的攻击病毒脉冲或伸缩,除了其目前基于帧的病毒运动。让我们把这个 XML 文件命名为 anim_scale_virus

现在我们再次将标签分成 <比例/ > ,并删除</比例>结束标签,以及添加我们的 xmlns:android 参数,以通过通常的 URL 在线引用 DTD (文档类型定义)。

完成刻度标签

现在我们使用 android: 工作流程来打开 Eclipse 助手对话框,列出所有的 < scale > 元素标签参数,这样我们就可以仔细研究这个标签可以(并将)为我们做什么。这都显示在图 10-5 中。

9781430257462_Fig10-05.jpg

图 10-5。创建我们的 anim_scale_virus.xml 并使用 android: parameter helper 对话框和< scale >参数

我们将在标签中设置的第一个和主要参数是 android:fromXScaleandroid:toXScale 参数。因为我们想让我们的病毒脉动,我们将把它从 100%(其默认或当前大小)降低到 75%,然后再降回来。因此,我们需要将 fromXScale 参数设置为 1.0 (或者以十进制表示的 100%),将 toXScale 参数设置为 0.75 (或者以十进制表示的 75%)。

因为我们想在 X 轴和 Y 轴上均匀地缩放我们的病毒,我们用完全相同的数值设置我们的 ?? Android:fromy scale 和 ?? Android:toYScale 参数。

正如我在本章早些时候提到的,通过设置枢轴点 X 和 Y 值(我们在这里设置这些值是为了让您熟悉这个参数),可以使用方向倾斜来缩放比例,但是因为我们希望病毒在其中心均匀弯曲,所以我们使用 X 和 Y 枢轴参数中的 50%值来使我们的枢轴点居中。

现在是使用 android:interpolator 设置动画插值器的时候了,我们将在这里使用一个 accelerate_decelerate 插值器,以获得更多的花哨,并给我们的病毒的伸缩运动赋予一点特性。

接下来,我们使用设置为 3000 毫秒或 3 秒的 android:duration 参数来设置我们的缩放动画的单个循环的长度。我们需要一个平滑缓慢的缩放,所以这应该是一个很好的起始值,如果需要,我们可以在以后调整(增加)以获得更真实的效果。

现在我们所要做的就是设置我们的重复参数,我们将准备在我们的 Java 代码中实现缩放动画定义。有两个重复参数,我们将用于这个比例动画, android:repeatCount ,我们以前见过,我们将再次设置为 infinite ,以及 android:repeatMode ,这对我们来说是新的,我们将设置为 reverse

RepeatMode 在 Android 中有两个常量,我们在这里使用的反向常量,它导致了 pong 动画效果,并使我们的病毒伸缩,重启常量,它创建了一个无缝循环,是默认值或设置。因此,如果您将 android:repeatMode 参数去掉或未声明,您的动画将无缝循环,前提是您已经将 android:repeatCount 参数设置为 infinite。您的 XML 标记应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
   <scale
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:fromXScale="1.0"
      android:toXScale="0.75"
      android:fromYScale="1.0"
      android:toYScale="0.75"
      android:pivotX="50%"
      android:pivotY="50%"
      android:interpolator="@android:anim/accelerate_decelerate_interpolator"
      android:duration="3000"
      android:repeatCount="infinite"
      android:repeatMode="reverse"
   />

图 10-6 显示了完成的标签。

9781430257462_Fig10-06.jpg

图 10-6。为 Eclipse 中的 anim_scale_virus.xml 文件配置< scale >标签的缩放定义参数

添加用于缩放的 Java 代码

现在是时候在我们的 AttackPlanet Java 代码中实现 scale 动画对象了。在 infectButton ImageButton 对象声明下添加一行空格,并复制它上面的两行代码,Animation rotateBomb 和 bombButton.startAnimation()并将它们粘贴到该空格中。

接下来将动画名称更改为 scaleVirus ,并设置 infectButton startAnimation()方法来调用这个 scaleVirus 动画对象,使用下面两行 Java 代码:

Animation scaleVirus = AnimationUtils.loadAnimation(this, R.anim.anim_scale_virus);
infectButton.startAnimation(scaleVirus);

实现 scaleVirus 的 Java 代码如图 10-7 所示。

9781430257462_Fig10-07.jpg

图 10-7。添加一个名为 scaleVirus 的动画对象,通过。startAnimation()

最后,我们将使用作为 Android 应用运行工作流程来查看我们的攻击病毒 ImageButton 的新级别动画细节,它现在在大小上可以伸缩,也可以制作帧动画。尝试插值器设置并改变病毒的弯曲方式,因此您开始熟悉 Android 中的 13 个不同的插值器常数,以及它们在动画中的运动曲线上提供的不同效果。

接下来,我们将为我们的士兵设置阿尔法通道的动画,这样我们就可以把他传送到星球表面来入侵星球。

实现阿尔法通道动画:把我传送到一个星球上

接下来,让我们使用补间动画 XML 对话框中显示的 < alpha > 根元素将我们的士兵部队传送到一个星球表面,如图图 10-1 所示。右击你的 Hello_World 项目文件夹,选择新建 image ** Android XML 文件对话框,在从资源类型:下拉菜单中选择补间动画并设置 Hello_World 项目,然后选择< ** alpha >根元素后,将新的 XML 文件命名为:anim _ alpha _ invasive

完成阿尔法标签

一旦设置好一切,点击 Finish 按钮,您将在 Eclipse 中获得打开的 XML 文件,并且您可以设置您的< alpha >标签。就像我们之前对<旋转>和<缩放>标签所做的一样,让我们通过将标签拆分为 < alpha/ > 组件,并添加您的 xmlns:android 参数和 URL 来设置标签以获取我们的参数,如图图 10-8 所示。然后,要查看< alpha >标签为我们提供了哪些参数,键入 android: 并弹出参数帮助器对话框。

9781430257462_Fig10-08.jpg

图 10-8。用< alpha >参数创建我们的 anim _ alpha _ invasive . XML 和 android: parameter helper 对话框

我们要添加的第一个 alpha 参数是 android:fromAlpha 参数,它取 1 (100%,或不透明)和 0 (0%,或完全透明)之间的一个整数值。因为我们想让士兵的可见性从不透明变为透明,我们将初始值设为 1.0

接下来,我们将添加 android:toAlpha 参数,该参数指定我们想要动画到的 Alpha 值。我们将把它设置为 0,这表示完全透明,就像 A 通道中的**# aarggbb**设置为 0 一样。

接下来,我们需要指定一种帧运动插值的方法,使用现在熟悉的 android:interpolator 参数,我们也将该参数设置为 accelerate_decelerate 设置,以实现逼真的淡出传送器效果。如果您愿意,您可以尝试其他 12 种运动曲线常量设置,以便更好地熟悉它们。

接下来,让我们将 android:duration 参数设置为 4000 毫秒(或 4 秒),给我们一个不错的缓慢淡出效果。由于我们不希望士兵不断淡入淡出,我们将尝试一个新参数,即 android:startOffset 参数,设置为 5000 毫秒,在每个动画周期中添加 5 秒钟的延迟,以便士兵保持可见(实心),时间比他透明(传送到星球)的时间长。您可以根据个人喜好设置这两个参数,以微调效果定时。

接下来,我们设置我们的 alpha 通道动画重复参数,将 android:repeatCount 再次设置为 infinite ,将 android:repeatMode 设置为 reverse ,这样士兵淡入(从星球返回)与淡出的方式非常相似,如下面的标记所示:

<?xml version="1.0" encoding="utf-8"?>
  <alpha
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="1.0"
    android:toAlpha="0.0"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:duration="4000"
    android:startOffset="5000"
    android:repeatCount="infinite"
    android:repeatMode="reverse"
  />

这也显示在 Eclipse IDE 代码编辑器的图 10-9 中。

9781430257462_Fig10-09.jpg

图 10-9。在 Eclipse 中为 anim _ alpha _ invasive . XML 文件配置< alpha >标签的 alpha fade 定义参数

为 Alpha 动画添加 Java 代码

现在是时候添加 Java 代码了,该代码创建所需的alpha invasive动画对象,并通过. startAnimation 方法将其连接到 invadeButton。这是通过以下代码行完成的:

Animation alphaInvade = AnimationUtils.loadAnimation(this, R.anim.anim_alpha_invade);

正如我们之前看到的,这一行密集的 Java 代码声明了一个动画对象,将其命名为alpha invasive,然后通过调用来加载我们的anim _ alpha _ invasiveXML动画定义。使用点符号从 AnimationUtils 对象访问的 loadAnimation( ) 方法。

现在我们要做的就是通过和将 alphaInvade 动画对象连接到 invadeButton UI 对象中。startAnimation( ) 方法,现在使用下面一行简单但功能强大的 Java 编程代码就完成了:

invadeButton.startAnimation(alphaInvade);

请注意,您也可以通过在两行代码上使用复制和粘贴操作来完成所有这些编码,这两行代码是我们已经为另一个攻击图标 UI 元素和上面的动画对象调用编写的。

既然我们所有的 XML 标记都已经被吸收到我们的 Java 编码中,如图图 10-10 所示,是时候使用我们的运行为 Android 应用的工作进程了,看看我们的攻击士兵消失在太空中又回来了。

9781430257462_Fig10-10.jpg

图 10-10。添加名为 alphaInvade 的动画对象,并将其分配给 invadeButton via。startAnimation()

虽然这本身很酷,但我并不满意,因为没有光束部分,什么是传送光束效果?因此,我们需要将这种效果提升到一个新的水平,这意味着通过将位图(帧或光栅)和程序(矢量或基于代码)动画结合在一起,创建一个更强大的效果,就像我们对攻击病毒所做的那样。

结合位图和程序动画

为了制作一个令人信服的动画(其中重复或循环的部分不容易被观众注意到),我们需要使用大约 12 个帧位图,我通过 beam64frame11.png 命名为 beam64frame0.png,并优化为 PNG8 索引颜色,这样所有 48 分辨率 DPI 版本的帧总共只有 190KB,或者平均每帧不到 4KB。

我们在动画的两侧留了一个 alpha 通道,这样光束动画可以在士兵的后面播放(最好在 ImageButton UI 元素的背景层内),而不会通过士兵图像的 alpha 通道(透明区域)显示,士兵图像将保存在前景层(android:src)内。这样,当我们对士兵的图像进行 alpha 淡出时,这种光束动画特效会显示出来,这既是士兵淡出过程的一部分,也是士兵图像消失后(当士兵图像不在时)它自己的效果。

我们需要做的第一件事是将我们所有的 44 个光束帧动画素材复制到它们各自的/res/drawable-dpi/ project 文件夹中,如图图 10-11 所示。

9781430257462_Fig10-11.jpg

图 10-11。将我们的 transporter 特效动画帧复制到我们的 drawable-dpi 文件夹并重命名

现在您应该对这个工作过程很熟悉了,所以让我们再多练习一下,将 96 像素的光束帧复制到/drawable-xhdpi 中,将 80 像素的光束帧复制到/drawable-hdpi 中,将 64 像素的光束帧复制到/drawable-mdpi 中,将 48 像素的光束帧复制到/drawable-ldpi 中。

接下来,我们将把这些文件重命名为beam0.pngbeam11.png,这样我们在 XML 中引用时就有了一个更简单的文件名。为此,我们删除了 48 个文件(每个分辨率密度文件夹中有 12 个)中每个文件的分辨率指示器编号和字框。

接下来,我们需要右键单击项目资源的 drawable 文件夹,并使用我们的 New > Android XML File helper 对话框工作流程,如前面章节中的图 10-1 以及第九章图 9-1 所示。一旦在 Eclipse 环境之外添加了这些文件,不要忘记使用 F5 或 Refresh 工作流程,以便您的项目可以“看到”它们。

将您的资源类型:设置为 Drawable ,然后选择 <动画列表>根元素:,最后将文件命名为 anim_effect_beam ,就像它将要成为的那样,一个动画效果命名为 beam,点击完成

接下来,将这个传送器光束效果的帧添加到为我们创建的 XML 文件中。

在 XML 中配置动画

我们希望效果是无缝的,所以我们将使用 pong 动画效果,并在第 0 帧和第 11 帧之间来回反弹,因此第 1 帧到第 10 帧将在 XML 代码中使用两次。

请注意,我们没有复制第 0 帧或第 11 帧,因为这将导致动画暂停,并在无缝动画循环期间通过播放暂停向观众泄露其帧边界。我们想利用一个相当慢的帧速率****8 FPS来获得一个缓慢的闪烁效果,由于 8 除以 1000 共 125 次,我们的 android:duration 值将是 125 毫秒,在 XML 中表示为 android:duration="125"

这里显示了一个标签的 XML 标记。XML 标记的所有 22 个标记行将遵循如下相同的格式:

<item android:drawable="@drawable/beam0" android:duration="125" />

配置我们的光束动画帧访问和顺序的 XML 代码如图 10-12 所示。我们从波束 0 开始,经过波束 11,然后回到波束 1,每个波束使用 8 FPS 设置。这个设置给了我们一个很好的,均匀的,无缝的传送器光束乒乓动画特效。

9781430257462_Fig10-12.jpg

图 10-12。在 pong 配置中添加传送器光束特效帧到 anim_effect_beam.xml

接下来,我们需要引用 anim_effect_beam.xml 文件,通过我们的 attack invasive ImageButton 的参数 android:background 中其名称的第一部分,这样它就可以在 UI 元素中的前景源图像后面制作动画。因为我们已经精心制作了我们的阿尔法通道,背景效果只有在前景图像淡出时才可见,在由<阿尔法>创建的淡入淡出期间也是如此。这创造了一个我们正在寻找的传输效果,通过结合 Android 中这两种关键类型的动画,以及最少量的数字图像素材。

记住,前景源图像实际上是一个多态图像 XML 定义文件,我们将其命名为attack _ invasive . XML,在 android:src 参数中引用。这表明我们正在变得非常复杂,在我们的 UI 元素背景板中使用基于 XML 的帧动画合成特效,在我们的 UI 元素前景板中使用基于 XML 的多态图像按钮定义,如图图 10-13 所示。以下是图中所示的 invadeButton 的 XML 标记:

<ImageButton
  android:id="@+id/invadeButton"
  android:background="@drawable/anim_effect_beam"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:contentDescription="@string/content_desc_invade"
  android:layout_margin="8dp"
  android:src="@drawable/attack_invade" />

9781430257462_Fig10-13.jpg

图 10-13。将 anim_effect_beam 引用插入到 ImageButton 的 android:background 元素中

如果由于某种原因,这种使用单个 UI 元素的特效设置对于 Android 来说太复杂,或者不被支持,或者由于某种原因不工作,我们将使用位于同一屏幕区域的两个用户界面元素来设置这种特效,以向您展示 Z 顺序的概念。

让我们来看看我们使用 Run As Android Application 工作流程的效果。一旦应用启动,点击菜单按钮,并选择攻击一个星球的活动屏幕,然后观察入侵图标。

请注意,士兵像以前一样渐隐为黑色,但背景动画不可见,因此要么是不支持,要么更有可能是 alpha 渐隐应用于整个用户界面元素,而不仅仅是其前景图像组件。

在淡入淡出后实现背景

接下来我们应该尝试使用 android:background 参数,它是< alpha >标签参数的一部分,如图图 10-8 所示。这个参数应该允许我们在我们的渐变后面实现一个背景,所以将这个参数添加到我们在图 10-9 中显示的参数中,并将其设置为引用 anim_effect_beam,并从图 10-13 中显示的 ImageButton 标签参数中移除 android:background 参数。

再次使用运行 Android 应用的工作流程,并在攻击星球活动屏幕中再次测试效果。因为背景效果在士兵淡出后不再保留,所以我们需要通过一个单独的 UI 元素来实现它,一个 ImageView,它就是为了这个目的用来保存图像的。

在 invadeButton ImageButton 标签之前添加一个 ImageView 标签,并将其设置为 invadeEffect 的一个 android:id ,并将其 android:background 参数设置为 anim_effect_beam 来引用我们的帧动画特效。确保将 android:layout_widthandroid:layout_height 参数设置为 wrap content 并将Android:layout _ margin left设置为 8dp 进行对齐。

8dp 左边距将你的背景图像推到 invadeButton UI 元素后面的位置,因此效果与士兵一致。确保包含一个Android:content description参数集来引用**@ string/content _ desc _ invasive**常量,否则 Eclipse 会给你一个警告。

一旦您在前两个 ImageButton 之间添加了这个 ImageView 标记,我们就必须在 invadeButton 中更改并添加几个参数,因为您可能还记得,LinearLayout 是按行或列排列的,因此,这个 ImageView 会将我们的 ImageButton UI 元素向下移动一个等级,除非我们为此进行参数更改。

这些 UI 参数更改的另一个选项是将整个屏幕布局容器重新编码为一个 RelativeLayout 容器,正如您将在下一节中看到的,这使得 Z 排序 alpha 通道兼容的 UI 元素更加容易。现在,我们用两种方法中比较容易的一种。

我们需要调整的最重要的事情是入侵图标 ImageButton 的像素间距,这样它就可以覆盖入侵效果 ImageView。我们这样做是为了让我们的 transporter 效应在我们的多态 ImageButton 后面发挥作用。

删除在我们的按钮周围放置 8dp 像素间距的Android:layout _ margin = " 8dp ",并添加一个**Android:layout _ margin left = " 8dp "**将 ImageButton 从屏幕一侧推出,使其与其他 ImageButton 图标对齐。

接下来添加一个 android:layout_marginTop 参数,设置为 54,没错,就是五十四如图图 10-14 所示。这将把我们的入侵图标 ImageButton 向上推到我们的 invadeEffect ImageView 的顶部,它包含了我们的传送光束特效动画。下面是此时这个< ImageButton >标记的 XML 标记的样子:

<ImageButton
  android:id="@+id/invadeButton"
  android:background="#00000000"
  android:layout_marginTop="-54dp"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:src="@drawable/attack_invade"
  android:contentDescription="@string/content_desc_invade"
  android:layout_marginLeft="8dp" />

9781430257462_Fig10-14.jpg

图 10-14。向我们的 activity_attack.xml 添加 ImageView UI 元素,以保持我们的 transporter beam 效果

新的 XML 标记的最终 Eclipse 编辑窗格内容如图 10-14 所示。

要将其余的 ImageButton 图标向上拉,并放回原位,请从 invadeButton 正下方的 infectButton ImageButton 标记中删除 android:layout_margin="8dp ",并添加一个Android:layout _ margin top = " 18dp ",以向下分隔其他图标,以及一个Android:layout _ margin left = " 8dp "

为动画添加 Java 代码

现在是时候在 Java 代码中实现 ImageView 对象和 AnimationDrawable 对象了,这样我们就可以实现 transporter 效果了。

声明一个名为 invadeEffect 的 ImageView 对象,通过使用 findViewById( ) 方法,使用下面一行 Java 代码,将它设置为我们的 XML 文件中定义的 invadeEffect UI 标签,如图图 10-15 所示:

ImageView invadeEffect = (ImageView)findViewById(R.id.invadeEffect);

9781430257462_Fig10-15.jpg

图 10-15。为 AttackPlanet.java 添加入侵效果 ImageView 和运输效果 AnimationDrawable

然后使用声明一个名为 transporterEffectAnimationDrawable 对象。getbackground( ) 方法使用下面一行 Java 代码:

AnimationDrawable transporterEffect = (AnimationDrawable) invadeEffect.getBackground();

接下来,调用**。使用下面一行 Java 代码为transporter effectanimation drawable 对象提供 start( )** 方法:

transporterEffect.start();

现在,我们已经准备好看到我们的应用中实现的最终效果,通过使用作为 Android 应用运行的工作流程。启动 Nexus S 模拟器,点击菜单按钮,选择攻击一个星球菜单选项,进入我们的活动界面。

正如你所看到的,当我们的士兵现在淡出时,通过我们的程序动画,有一个传送器光束帧动画被合并到这个特殊效果中,使它成为一个更加真实的传送器光束空间旅行效果。

请注意,我们只用了 190KB 的总素材、几行 Java 代码和几十行 XML 标记就完成了这项工作。如果我们必须单独通过数字视频或帧动画来实现这种效果,那么要达到类似的质量,需要十倍多的数据占用空间。

实现复杂动画:XML 参数分组

接下来我们要做的是通过在一个单独的动画集中一起使用旋转、缩放和阿尔法标签来创建一个复杂动画。这是使用 < set > 标签完成的,您可能已经猜到了,因为您已经在之前的几个屏幕截图中看到过这个< set >根元素选项。

一个动画集就像一个,所以这些<集>标签允许你将动画类型分组在一起,如果需要,甚至可以通过在其他集内嵌套集来对它们进行分组,从而创建越来越复杂的动画或特殊效果的子集。

我们将围绕我们的星球实现一个简单得多的力场动画,仅使用三帧 2D 图像动画,然后旋转、缩放和淡化这些光栅动画数据,以产生一个更复杂、数据更紧凑的星球力场效果。

为此,我们必须首先将 ring320frame0.png 到 ring320frame2.png 的文件(以及其他分辨率密度版本)复制并重命名到 drawable-dpi 文件夹中,并使用图 10-11 所示的常用工作流程将它们重命名为 ring0.png 到 ring2.png。复制完所有 12 个文件后,右击 drawable 文件夹,选择新建> Android XML 文件对话框,创建一个 Drawable XML 容器,带有 < animation-list > 根元素,命名为 anim_plasma_ring ,最后点击完成按钮。不要忘记利用 F5 或刷新工作流程,以便 Eclipse 可以看到您的可绘制素材。

添加三个引用 ring0、ring1 和 ring2 素材的 < item > 标签,并为每个 166 毫秒设置一个持续时间,这将每秒钟为你的力场环设置两次动画(6 乘以 166 等于 1000)。您添加的每个<项目>标记应该具有以下 XML 标记格式:

<item android:drawable="@drawable/ring0" android:duration="166" />

最终的 XML 标记显示在图 10-16 中的 Eclipse XML 编辑窗格中。

9781430257462_Fig10-16.jpg

图 10-16。创建 anim_plasma_ring 帧动画 XML 定义,用于我们的复杂动画

然后进入您的 activity_main.xml 文件,将您的 ImageView 标签 android:background 参数更改为 reference @ drawable/anim _ plasma _ ring,如以下 xml 标记所示:

<ImageView
  android:id="@+id/imageEarth"
  android:src="@drawable/earth"
  android:padding="24dp"
  android:background="@drawable/anim_plasma_ring"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_below="@+id/textView8"
  android:contentDescription="@string/content_desc_invade" />

这显示在图 10-17 中,一旦你修改了你的 XML 标记,加入了新的 android:background 参数,引用了你新的 anim_plasma_ring drawable,我们将准备好实现我们的动画集,它将在动画制作时进一步旋转你的背景,并在地球上缩放它,同时整个动画会稍微淡入淡出。

9781430257462_Fig10-17.jpg

图 10-17。引用我们 activity_main.xml 文件 ImageView 中的新 anim_plasma_ring 帧动画

现在我们可以右击可绘制文件夹,选择我们新的image Android XML 文件菜单序列,创建一个补间动画 XML 资源类型,命名为 anim_set_ring ,然后选择 < set > 根元素,点击 Finish 按钮,如图图 10-18 所示。

9781430257462_Fig10-18.jpg

图 10-18。使用新的 Android XML 文件对话框创建一个 anim_set_ring < set >复杂动画结构

配置设置标签

接下来,进入空的动画文件,键入 < 角色,如图图 10-19 所示。这将打开标签选择器对话框,然后我们可以选择 <旋转> 标签将其包含在我们的动画<集合>中,因为我们将首先设置我们的旋转动画参数。顺序并不重要,因为<集合>用于将动画组合在一起,以便它们在并行中处理,或者彼此同时处理,而不是按顺序,这将被称为串行动画(或一个接一个)。

9781430257462_Fig10-19.jpg

图 10-19。使用通过<键访问的助手对话框,向我们的< set >父标签添加一个< rotate >子标签

确保将 xmlns:android 参数添加到您的< set >标签中,以引用http://schemas.android.com/apk/res/android URL,这样我们放在标签中的所有这些标签和参数都会生效,并且 不会在 Eclipse XML 编辑窗格中抛出 Red-X 错误。

让我们将我们的旋转 android:fromDegrees 参数设置为 0(零),将我们的 android:toDegrees 参数设置为 360 ,这样我们就得到一个无缝的环旋转。

接下来,让我们将我们的 android:pivotXandroid:pivotY 参数设置为 50% ,以动画环形元素的中心为中心进行旋转。我们将使用一个很好的均匀线性插值,因为我们正在循环动画,以及 10,000 毫秒的缓慢持续时间,或 10 秒的总旋转。

对于我们的重复参数,我们将使用**Android:repeat count = " infinite "Android:repeat mode = " restart "**参数,以便我们可以实现一个无缝循环,永远持续下去。

我们可以在稍后的 Java 代码中使用 visibility 参数来打开和关闭这个力场效果,所以我们希望我们的力场永远保持动画效果,只要所有这些动画设置参数都是相关的。父<集>标签内的<旋转>标签的 XML 标记应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
  <rotate android:fromDegrees="0"
          android:toDegrees="360"
          android:pivotX="50%"
          android:pivotY="50%"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="10000"
          android:repeatCount="infinite"
          android:repeatMode="restart"  />
</set>

你可以看到我们完成的标签参数如图图 10-20 所示。

9781430257462_Fig10-20.jpg

图 10-20。使用通过<键访问的助手对话框,向我们的<集合>父标签添加一个<比例>子标签

正如您在图 10-20 中看到的,我们再次利用 < 键调出标签选择器对话框,我们现在选择一个 <缩放> 标签添加到我们的缩放参数中,以添加图像缩放这个复杂的动画集。

添加图像缩放比例

我们将从最重要的 android:fromXScale="1.0" 或全比例(原始位图图形元素像素尺寸)开始,然后缩小到一个 android:toXScale="0.75" 或原始大小的四分之三比例。

因为我们想要一个统一的(纵横比保持不变)缩放,我们将对 android:fromYScaleandroid:toYScale 参数使用相同的参数。正如我们在本书前面所学的,通过在 X 轴和 Y 轴或维度(H & W 边)上等量缩放图像或动画帧,可以保持纵横比,减少空间失真。

由于我们希望均匀地缩放力场环,我们将 android:pivotXandroid:pivotY 参数设置为 50%的值,这将缩放原点直接放在我们正在缩小的位图图像(或在本例中,基于帧的光栅动画)的中心,然后使用我们将要添加的接下来的四个参数再次返回。

因为我们希望平滑均匀地缩放,所以我们希望通过Android:interpolator = " @ Android:anim/linear _ interpolator "参数添加一个线性插值器。为了获得缓慢的缩放效果,我们将我们的 android:duration 参数设置为10000 毫秒 (10 秒),以获得更真实的效果。

最后,我们设置我们的重复参数来定义我们的缩放将如何随着时间的推移而执行。我们希望效果永远持续下去,所以我们将 android:repeatCount 参数设置为 infinite 。因为我们希望缩放效果在每个星球周围缓慢地进出,所以我们将 android:repeatMode 设置为 reverse ,这样缩放会自动反转。

到目前为止,XML 标记应该看起来像下面显示的标签和参数:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
  <rotate android:fromDegrees="0"
          android:toDegrees="360"
          android:pivotX="50%"
          android:pivotY="50%"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="10000"
          android:repeatCount="infinite"
          android:repeatMode="restart"  />
  <scale  android:fromXScale="1.0"
          android:toXScale="0.8"
          android:fromYScale="1.0"
          android:toYScale="0.8"
          android:pivotX="50%"
          android:pivotY="50%"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="10000"
          android:repeatCount="infinite"
          android:repeatMode="reverse"  />
</set>

最终的刻度标签如图 10-21 所示。

9781430257462_Fig10-21.jpg

图 10-21。使用通过<键访问的助手对话框,将< alpha >子标签添加到我们的< set >父标签

最后,我们使用 < 键添加我们的 < alpha > 程序动画标签,如图图 10-21 所示,以及我们完成的< scale >标签参数。

添加 Alpha 程序动画

标签有最少的总参数,因为它只是控制一个渐变效果,在这种情况下,从 100%不透明到 50%透明或一半透明,以显示一些恒星或星系背景图像。既然力场通常是透明的,为什么要打破常规呢?

首先使用 android:fromAlpha 参数,通过使用 1.0 的值将我们的源图像(或本例中的动画)透明度级别设置为 100%。接下来使用 android:toAlpha 设置值为 0.5 或 50%透明,为我们的力场环动画获得一个真实的半透明效果。

接下来让我们设置我们的 android:interpolator 参数为 linear ,为我们的渐变(混合)alpha 值提供一个平滑的线性插值。这是一个参数,您可以使用插值器常量来尝试获得更动态的透明度变化效果。

因为我们希望将这个效果的持续时间与其他两个缩放和旋转效果相匹配,所以我们将使用一个 android:duration 参数,并将其设置为 10000 毫秒。同样,由于渐变效果不需要与旋转和缩放效果同步,您也可以使用该数值,以获得更真实的力场脉冲效果。

最后,我们设置我们的重复参数,将 android:repeatCount 参数设置为 infinite ,将 android:repeatMode 参数设置为 reverse ,这样,这个淡出在动画周期结束后转变为淡入。XML 标记应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
  <rotate android:fromDegrees="0"
          android:toDegrees="360"
          android:pivotX="50%"
          android:pivotY="50%"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="10000"
          android:repeatCount="infinite"
          android:repeatMode="restart"  />
  <scale  android:fromXScale="1.0"
          android:toXScale="0.8"
          android:fromYScale="1.0"
          android:toYScale="0.8"
          android:pivotX="50%"
          android:pivotY="50%"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="10000"
          android:repeatCount="infinite"
          android:repeatMode="reverse"  />
  <alpha  android:fromAlpha="1.0"
          android:toAlpha="0.5"
          android:interpolator="@android:anim/linear_interpolator"
          android:duration="10000"
          android:repeatCount="infinite"
          android:repeatMode="reverse"  />
</set>

Eclipse 中的 XML 编辑窗格应该看起来像图 10-22 所示的屏幕。

9781430257462_Fig10-22.jpg

图 10-22。完成我们的<集合>父标签,包含旋转、缩放、阿尔法的复杂动画参数

接下来,我们需要通过当前的 XML RelativeLayout 容器将这个新的 forcefield 动画实现到我们的主屏幕用户界面屏幕中。我们使用一个设置为 planetEffectandroid:id 参数,给这个动画用户界面元素一个自己的 ImageView 标签。

我们以这种方式设置它,因为如果我们将这个缩放动画应用到我们的原始 imageEarth ImageView,并将 anim_plasma_ring.xml 引用为其背景组件,整个 UI 元素将被旋转、缩放和混合,在这种情况下,我们希望行星图像保持不变。

事实上,我们在本书的前一部分看到了同样的考虑,我们需要分离我们的程序动画效果来处理它自己的 UI 元素容器,原因大致相同。

接下来,包含必需的 android:layout_heightandroid:layout_width 参数,这两个参数都被设置为 wrap_content ,以及android:content description参数,这也是 Android 对于任何图像相关标签所必需的。

接下来,我们将添加我们的相对定位标签, android:layout_below ,我们将引用我们的 imageEarth ImageView 引用的 @+id/textView8 ,因为我们希望这个 planetEffect ImageView 位于 imageEarth ImageView 占据的确切屏幕位置。还要确保您的 android:src 参数引用了包含我们的< set >复杂程序动画定义的**@ drawable/anim _ plasma _ ring**XML 文件。XML 标记如下所示:

<ImageView
         android:id="@+id/planetEffect"
         android:src="@drawable/anim_plasma_ring"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_below="@+id/textView8"
         android:contentDescription="@string/content_desc_earth"
/>

最终的 XML 如图 10-23 所示。

9781430257462_Fig10-23.jpg

图 10-23。添加带有 planetEffect and 和 anim_plasma_ring 源引用的 ImageView XML 标记

现在,让我们调整我们的 imageEarth ImageView 标记参数,以确保行星图像与 planetEffect ImageView UI 元素中包含的程序动画对齐并合成。将您的 android:padding 设置为 24dp ,并使用您的Android:layout _ margin left参数设置为 1dp 来微调效果中行星图像的居中。使用设置为**# 0000000**的 android:background 参数,确保 ImageView 是透明的,如以下 XML 标记所示:

<ImageView
         android:id="@+id/imageEarth"
         android:src="@drawable/earth"
         android:background="#00000000"
         android:padding="24dp"         android:layout_marginLeft="1dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_below="@+id/textView8"
         android:contentDescription="@string/content_desc_earth"
/>

结果如图 10-23 所示,我们将准备好编写 Java 代码来实现这些 ImageView UI 元素,以创建一个仅使用 200KB 数字图像数据的 planet forcefield 效果。

为动画添加 Java 代码

现在,我们要实现这个新的力场动画场景所要做的就是改变我们的 Java 代码来实现保存我们力场环动画的新 ImageView,并删除保存旧力场动画的 ImageView 对象,如图图 10-24 所示。

9781430257462_Fig10-24.jpg

图 10-24。添加 effectPlanet ImageView,将其设置为通过引用 animSetRing 动画对象。startAnimation( )

正如你在图 10-24 的旧 Java 代码中看到的,我使用了双斜线注释功能,以消除编译器对这些代码行的考虑,但也保留给我们自己使用(这个功能的一个方便的用法,你可以考虑自己使用)作为学习参考,这样你就可以比较 Java 代码前后的情况。

第一行代码本质上和之前一样;声明 effectPlanet ImageView 对象,然后使用 findViewById( ) 方法将其设置为 XML 定义。Java 代码应该如下所示:

Imageview effectPlanet = (ImageView)findViewById(R.id.planetEffect);

剩下的我们只用两行代码就完成了,一行声明了程序动画对象,另一行将它连接到 effectPlanet ImageView 并启动动画运行。

为了声明一个程序动画对象,我们将使用 Android Animation 类,实例化一个动画对象,并在使用加载 XML 动画定义的同一行代码中命名它。loadAnimation( ) 方法,通过使用点标记法从 AnimationUtils 对象调用该方法。这可以在一行 Java 代码中完成,如下所示:

Animation animSetRing = AnimationUtils.loadAnimation(this,R.anim.anim_set_ring);

这是对 Android 操作系统和编译器的暗示:我想创建一个名为 animSetRing 的动画对象,并在当前上下文中,使用 AnimationUtils 类加载我在 anim_set_ring.xml 文件中定义的动画数据。一旦完成,我们开始动画。

。startAnimation( ) 方法是从 effectPlanet ImageView 对象调用的,我们在前面设置了两行 Java 代码,该方法被传递给一个动画对象,这个动画对象也是我们刚刚创建的,使用了一小段代码:

effectPlanet.startAnimation(animSetRing);

现在是时候看看我们新的行星力场在起作用了。右键单击项目文件夹,使用 Run As Android Application 工作流程在 Nexus S 模拟器中启动 Hello World Android 应用。

一旦应用运行,你可以在主屏幕的底部看到行星地球图像视图,以及新的力场动画用户界面元素,如图图 10-25 所示。forcefield 动画用户界面元素现在可以在三个不同的图像合成帧之间制作动画,同时还可以旋转、缩放和淡入淡出,使用我们编写的程序动画 XML 标记来控制该用户界面元素的许多属性。

9781430257462_Fig10-25.jpg

图 10-25。运行我们的新主屏幕和特效

接下来,我们将为最后一个攻击星球的图像按钮图标实现程序动画:致命但美丽的激光加农炮。

实现运动动画:XML 参数

最后,为了确保本章中使用的动手操作示例涵盖了 Android 中用于程序动画的所有根元素,我们现在将在攻击星球活动用户界面中为我们的 LaserCannon ImageButton 图标创建一个逼真的发射反冲脉冲

让我们右键单击我们的 drawable 文件夹,选择 New image ** Android XML 文件**,创建一个新的补间动画 XML 容器,名为 anim_trans_laser ,然后选择一个 translate 选项,这样它就包含了一个 < translate > 根元素。

由于 XML 文件只有一个标签,请遵循以下工作流程:删除结束标签,然后将开始标签转换为 <翻译/ > ,为添加配置翻译动画操作的参数做准备。

如果你愿意,你可以输入 android: 来查看标签的所有 15 个可能的参数选项,如图图 10-26 所示。

9781430257462_Fig10-26.jpg

图 10-26。添加一个<平移>根元素到我们的动画 _ 平移 _ 激光并调用参数助手对话框

配置翻译标签

我们最重要的参数是那些告诉我们的动画如何在屏幕上移动对象的参数:参数 android:fromXDeltaandroid:toXDelta 。这些 Delta 参数采用百分比值,因为我们只是要为我们的激光炮模拟一个短暂的脉冲逆火,我们将这两个参数设置为从 0%(定义它们当前的位置)到 10%(沿负 X 轴向后一小段距离)。

因为我们希望我们的运动是沿着一条对角线,所以我们还会将 Y 轴参数设置为 0%和 10%,这样运动量就会相等,而且是完美的对角线(45 度,就像我们的激光炮一样)。

您可以使用这四个值来调整效果的大小和方向,并感受一下参数是如何操作的。

接下来,我们要设置我们的插值器,对于脉冲激光炮来说,反冲是一个重要的属性。幸运的是,对于这种效果应用,有一个完美的反弹插值器,所以让我们添加我们的Android:interpolator = " @ Android:anim/bounce _ interpolator ",以便我们可以将这种逼真的运动曲线效果放置到位。

因为这个动画的特点是一个短而快的运动能量爆发,我们将使用一个只有 80 毫秒的 android:duration 参数,这样运动就快速而锐利。另一方面,我们不希望激光加农炮经常发射,所以我们将使用 android:startOffset 参数设置为 8000 毫秒,这样激光加农炮就不会过于频繁地发出脉冲,最终在视觉上干扰我们的最终用户。

最后,我们需要设置我们的重复参数,所以让我们设置 android:repeatCountinfiniteandroid:repeatModerestart ,这样激光炮每次都会以相同的方式发出反冲脉冲。标签的 XML 标记应该如下所示:

<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0%"
    android:toXDelta="-10%"
    android:fromYDelta="0%"
    android:toYDelta="10%"
    android:interpolator="@android:anim/bounce_interpolator"
    android:duration="80"
    android:startOffset="8000"
    android:repeatCount="infinite"
    android:repeatMode="restart"
/>

最终标签如图 10-27 所示。

9781430257462_Fig10-27.jpg

图 10-27。将翻译动画参数添加到我们的 XML 文件中,使激光炮脉冲发射

最后,我们需要进入我们的 AttackPlanet.java 活动类,并添加 Java 代码来启动所有这些(没有双关语)。

为翻译添加 Java 代码

现在大家应该都很熟悉了,毕竟熟能生巧,所以让我们首先实例化我们的动画对象并将其命名为 translateLaser,然后通过下面一行 Java 代码使用 AnimationUtils 类 loadAnimation()方法从名为 anim_trans_laser.xml 的 XML 文件加载我们的动画,如图图 10-28 所示:

Animation translateLaser = AnimationUtils.loadAnimation(this,R.anim.anim_trans_laser);

9781430257462_Fig10-28.jpg

图 10-28。编码 translateLaser 动画对象并使用。startAnimation()方法将其连接到 laserButton

然后我们可以调用**。startAnimation( )** 方法,使用下面一行 Java 代码将 laserButton ImageButton 对象的 translateLaser 关闭:

laserButton.startAnimation(translateLaser);

一旦这一切就绪(见图 10-28 ),我们就可以使用我们的作为 Android 应用运行工作流程,启动我们的 Nexus S 模拟器,看看我们的激光加农炮脉冲!

一旦应用启动,点击菜单按钮,选择攻击一个星球菜单选项,进入你的用户界面屏幕,观察激光加农炮反冲,从它的激光发射序列反冲。请记住,由于 startOffset 参数的原因,您必须等待 8 秒钟,第一个平移动画才会“启动!”现在,我们的《攻击星球》UI 屏幕已经完全动画化,可以供最终用户使用了。

摘要

在本章中,我们学习了如何在 Android 中实现程序动画。我们了解到这种类型的动画使用代码和算法,而不是位图,就像基于帧的动画一样,我们还了解了矢量动画的主要概念和组件,以及矢量图像、旋转平移缩放。我们还看了一下如何制作 alpha 通道值的动画。

然后,我们继续(没有双关语)在当前 Hello World 应用中为静态和动画位图图像添加程序动画,添加比例以使我们的病毒脉冲更真实,旋转我们的炸弹图像以使其动画化,alpha 通道混合到我们的入侵士兵以实现令人印象深刻的传输光束效果,并转换到我们的激光炮以使其反冲,就像它射出了一个激光脉冲一样。

我们还研究了如何创建更复杂的动画集,通过使用 < set > 标签将各种类型的程序动画标签分组,以形成更复杂的动画定义,其中所有这些组件都由 Android 同时并行处理。

在本书的下一部分中,我们将了解并在 Hello_World 应用中实现更复杂的新媒体元素,如数字音频和数字视频。在下一章,我们将学习数字视频概念、数字视频编辑和数字视频压缩。****

十一、视频介绍:概念和优化

在这一章中,我们将为提供一些基础知识,帮助你了解数字视频的工作原理,以及如何在你的 Android 应用中处理数字视频。

本章包括许多高级概念,这些概念将建立在你在第七章和第八章中学到的所有关于数字成像概念和技术的知识之上。

这种新媒体素材更难理解和优化,因为数字视频本质上是静态数字图像的移动集合。因为数字视频总是在移动,所以它引入了第四维度,或者说时间。这一点,加上当今强大而复杂的数字视频编码和解码算法所带来的复杂性,使得数字视频在本质上比数字成像更难学习,甚至比程序动画更难学习。

优化数字视频也很复杂,主要是因为数字视频对视频中的所有帧进行压缩,这需要更高级的数学知识,因此需要更高级的知识来了解压缩过程中到底发生了什么,才能有效地实施这些压缩算法。

我们将在如何应用于为您的 Android 应用最终用户创建最佳用户体验的背景下审视所有这些。

这意味着不仅要了解数字视频编辑的基本原理及其数据占用优化,还要了解哪些数字视频格式最适合用于 Android,以及在何种分辨率下使用它们。这些主题是如此先进,我们将利用两个章节来有效地彻底涵盖一切,这样你就可以很好地处理数字视频,以及如何针对 Android 进行优化。

数字视频的基础:像素,帧 ,FPS ,编解码器

您在第七章中了解到的所有适用于您的数字影像的概念(像素、分辨率、纵横比、颜色深度、alpha 通道、图层、混合和合成)同样适用于数字视频,并且当您处理数字视频时,它们对您来说同样重要,甚至更重要。

你在第九章中学到的一些关于基于帧的动画的概念也适用于数字视频。这是因为数字视频和光栅动画一样,是基于帧的内容,可以随着时间的推移快速显示。由于这个原因,帧率的概念,表示为每秒帧数,或 FPS,在您的数字视频数据足迹优化工作过程中也非常重要。

数字视频文件是用一种叫做数字视频编解码器的软件创建的,这是 ?? 公司的缩写。DV 编解码器是一个复杂算法的集合,既可以对装满数字视频帧的容器(文件)进行编码,也可以进行解码,以便最终用户可以存储、传输和查看。请注意,您的最终用户必须能够访问对视频帧进行编码的同一编解码器才能对其进行解码;这通常是通过操作系统(Android)或浏览器(Chrome 或 Firefox)完成的。

因为编码任何给定数字视频数据文件的相同编解码器(算法)也必须用于解码该数字视频数据,所以这证明了每个数字视频编解码器都具有编码组件或编码侧,以及解码组件或解码侧,其可以解密其编码器所写的内容。

因此,MPEG4 H.264 编码的数字视频文件只能使用 MPEG4 H.264 编解码器读取(解码),这是您的 Android 操作系统的一部分。类似地,由 VP8 编解码器编码的数字视频帧只能使用 VP8 编解码器播放(解码),Android 操作系统也支持 VP8 编解码器。

数字视频文件有不同类型的文件扩展名,告诉开发者和 Android OS 该文件中的数字视频数据是什么类型,以及使用哪种类型的编解码器(解码器)来读取(解码)该类型的视频数据。我们之前提到的 MPEG4 H.264 文件具有 .mp4 文件扩展名,因此如果我们使用这种特定的视频编解码器来编码视频数据,我们的火星旅行视频将被称为mars.mp4

不同类型的数字视频编解码器具有不同的功能和性能规格。因此,我们想要查看的关键是任何给定编解码器提供的质量与文件大小的比率。一些数字视频编解码器(如 MPEG4 的 H.263 编解码器)质量较低,适用于低分辨率视频会议等应用,而其他编解码器(如 MPEG4 的 H.264 和 VP8)则更适合于高质量的内容,如电影和电视节目。在写这本书的时候,MPEG4 的 H.268 编解码器也已经发布,但是还没有被采用。为什么?因为:H.268 编解码器必须存在才能解码 H.268 内容!

重要的数字视频属性:标清、高清、流媒体和比特率

所以,现在我们知道视频编解码器将帧中的像素转化为数学;分辨率在哪里起作用,视频是如何存储和访问的?

存储视频:分辨率

高清普及之前使用的原始视频分辨率被称为 SD ,或 标清数字视频。在美国,SD 视频最初在高度上使用 480 行分辨率,因此 4:3 纵横比为 VGA 分辨率或 640 乘 480,宽屏纵横比 SD 视频为 720 乘 480。在欧洲,标清视频在高度上使用 576 行分辨率,因此欧盟的 4:3 标清视频将为 768×576,而欧洲的宽屏幕宽高比标清视频将为 1024×576。

最近高清 数字视频已经变得流行,并且使用宽屏 16:9 宽高比。高清分辨率有两种,第一种是 1280 乘 720 像素,我称之为伪高清分辨率,第二种,也是现在比较常见的高清分辨率,是 1920 乘 1080 像素,视频行业称之为真高清

有趣的是,所有这些分辨率都非常接近 Android 消费电子设备上常见的屏幕尺寸。有 640×480 VGA 屏幕的入门级安卓手机,也有 800×480 WVGA 屏幕的主流安卓手机,接近 720×480 宽 SD 标准分辨率。还有 1024×600 的入门级(更小的外形)Android 平板电脑,接近欧洲范围内 1024×576 的标清分辨率。

较新的 Android 高清手机是 1280×720,或伪高清,最新的 Android 平板电脑是真高清 1920×1080。这非常方便,因为我们可以对视频内容使用广播分辨率标准,并且仍然可以达到大多数流行的 Android 屏幕分辨率。

访问数字视频数据:捕获和流式传输

那么,Android 设备首先是如何访问数字视频数据的呢?这可以通过两种方式之一来实现。数字视频数据(文件)可以被 捕获在您的应用本身中,在这种情况下,它是您的资源文件夹中的新媒体素材,就像您的图像和动画数据一样。

第二种访问数字视频的方式是通过一种叫做“??”的概念,从你的安卓设备和应用之外的外部视频服务器“??”解码(播放)数字视频文件。

流式传输数字视频数据的好处是它可以大大减少应用的数据占用量。这是因为您不必在您的中包含所有繁重的新媒体数字视频数据。APK 档案。

流式数字视频的缺点是,如果您的用户连接(或视频服务器)中断,您的视频文件可能无法始终呈现给最终用户播放和欣赏;因此,视频内容的可靠性和可用性是另一个需要考虑的因素。

流式数字视频的核心概念之一是数字视频数据的比特率。比特率是在压缩过程中定义的,因此我们将在本章的后续章节中更详细地讨论这个问题,但让我们在这里定义它,因为它是一个重要的视频概念。

比特率本质上定义了你的数字视频数据将被压缩到什么程度。比特率是在数字视频压缩过程中定义的,这就是为什么我将在本章关于数据占用优化的章节中更详细地介绍它。

比特率较低(数量较少)的数字视频文件将对数据进行更多的压缩,这将导致质量水平较低,但在更多的消费电子设备上播放会更流畅。

这是因为比特率是每秒比特BPS 的度量,可以有效地处理或传输。随着计算机处理器越来越快,它每秒可以处理更多的位,同样,随着数据带宽连接越来越快,它每秒也可以轻松地发送或接收更多的位。

正如你所看到的,位/秒不仅对流式数字视频内容很重要,因为它适合带宽,而且一旦它到达 Android 设备,它还会影响哪些内容可以足够快地处理(解码),以允许 Android 设备内的处理器顺利播放。因此,比特率之所以重要,有两个原因,一是因为流媒体视频,二是因为它只在一个方面(解码器处理速度)对 Android 应用中的捕获(嵌入)视频文件重要。

因此,在 Android 应用中嵌入了受控嵌入的视频,视频资源的比特率越低,就有越多的 Android 设备可以顺利解码该视频资源(不会丢帧)。

原因很明显,每秒处理的数字视频数据位数越少,处理器的处理负荷就越轻。这不仅为视频播放带来了卓越的性能,也为整个 Android 应用带来了卓越的性能,同时也为 Android 设备内部的其他一切带来了卓越的性能。

因此,我们针对数字视频的数据占用优化非常重要,以较低的比特率获得良好的视频图像质量成为我们的最终目标,这也是我们希望使用最佳编解码器的原因。

在视频行业中,每秒一比特被写为:比特/秒,它通常包括一个大小修饰符,如前面的kbit/秒。这个 k 将表示千比特每秒,这意味着每秒几千比特。大多数视频压缩比特率设置在256 kbit/s(256 kbps)和 768 kbit/s 之间,尽管我们将尝试为我们的 Android 应用优化我们的数字视频数据到更低的范围,在 192 kbit/s 和 384 kbit/s 之间,但仍然可以获得出色的图像质量。如果我们能做到这一点,我们的数字视频将播放流畅,在所有不同类型的 Android 消费电子设备上看起来都很棒。这就是本章非常重要的原因。

数字视频格式:支持 Android 中的数字视频编解码器

Android 中支持的三种主要的数字视频数据格式: MPEG4 H.263 ,是质量最低(性能最差)的编解码器,可以有 .3gp.mp4 文件扩展名; MPEG4 H.264 AVC ,可以有 .3gp.mp4 文件扩展名;还有 VP8 ,可以有一个**。webm** 或 an 。mkv 文件扩展名,在 Android 2.3.3(及以后版本)中支持捕获播放,在 Android 4.0(及以后版本)中支持视频播放。

第一个编解码器 MPEG4 H.263 主要用于视频会议,因此我们将更多地关注更高质量、更常见和更广泛的编解码器,MPEG4 H.264 AVC 和 VP8。 AVC 代表:高级视频编码

这两种流行的编解码器在 HTML5 中都得到了很好的支持,因此在所有流行的浏览器中都得到了支持。MPEG4 H.264 AVC 在所有版本的 Android 中都支持回放,并且在 Android 3.0(和更高版本)中支持使用数码相机硬件对视频进行编码,如果 Android 设备包括一个的话。

VP8,也称为 WebM ,包含在 Android 2.3.3 中,用于播放受控数字视频文件,包含在 Android 4.0 和更高版本中,用于播放流式数字视频文件。当前版本的 Android 不支持将相机数据编码为 VP8 格式,但也许该功能将成为 Android 5.0 的一部分,这应该很有意思。

因为我们专注于 Hello World 应用的数字视频解码,并且我们正在项目资源文件夹中优化和使用捕获或嵌入的数字视频文件,所以本章我们将专注于 MPEG4 H.264 AVC 和 VP8 的优化,因为这些是目前我们可用的优秀编解码器。

这两种编解码器中最常见的数字视频编解码器是 MPEG4 H.264 AVC 编解码器,因此市场上的大多数数字视频编辑软件,如 Final Cut Pro 或 After Effects,都可以将数字视频编码成这种格式。这是因为目前互联网上几乎每个人都在使用 HTML5 制作数字视频,因此大多数流行的数字视频编辑包中都包含了 H.264 编解码器支持。

我将使用英国伦敦 EditShare 的开源 Lightworks 数字视频编辑软件包。几年前,EditShare 慷慨地将他们以前的六位数非线性数字视频编辑软件包开源,新版本 11 应该会在本书发布时发布,他们刚刚在我最近下载的版本 11.1 中添加了 H.264 支持。我们还将使用流行的 Sorenson Squeeze Pro 软件来了解数字视频编码,这是互联网 2.0 视频编码的行业标准。

VP8 编解码器支持稍微更新一些,目前在 Lightworks 中没有,所以我将在本书中使用流行的 Sorenson Squeeze 视频编码软件包,以便您可以看到一个支持 Android 中当前支持的所有格式的编码包。

数字视频优化:跨设备和分辨率播放

既然我们已经决定了 Android 操作系统中支持的哪些数字视频编解码器最适合我们在 Hello World 应用中的使用,我们现在需要确定我们将为不同密度级别的 Android 设备屏幕支持哪些分辨率。

我们需要选择三到四个关键分辨率,这些分辨率分布得足够远,以完美地适应各种屏幕尺寸,从入门级的 320 x 480 预付费 El-cheap 手机,一直到支持真正的 HD 1920 x 1080 或更高分辨率的新型大型平板电脑和 ITV。

如果我们要以 1920×1080 的原生 XXHDPI 分辨率支持真高清,那么我们应该有四种不同的分辨率(和比特率),其中之一就是那个。在这种情况下,我会使用 VP8 ,因为视频的每一帧都有 1920(宽)乘以 1080(高)乘以 3 (R,G,B)或 6220800 个像素值要压缩。如果我们想要一个快速的, 30 FPS 的帧速率,我们需要将该值乘以 30,这样我们每秒要压缩的像素数据总量为 186,624,000 像素。

符合逻辑的下一个支持的分辨率级别是伪高清1280×720XHDPI分辨率级别,用于高清智能手机、大多数平板电脑和一些 ITV。如果你只打算使用三个目标分辨率,这一个将成为最高级别,并且可以放大以适应 1920×1080 的显示器,并且看起来仍然相对不错,因为你在编解码器设置方面做得很好。如果你知道你在做什么,使用 VP8 这实际上是很可能的,它可以以低比特率产生惊人的质量水平和巨大的数据足迹。

合乎逻辑地支持的下一个目标分辨率水平也是一种流行的广播分辨率,美国的宽 SD 格式,或者 ?? HDPI ?? 屏幕的 720×480 像素。到目前为止,我们的四个目标分辨率中有三个也是非常常见的目标视频广播分辨率,因此您在为 Android 应用支持准备数字视频文件的工作过程中,也可以方便地为其他媒体和目的提供优化的数字视频素材,一切都不费吹灰之力。这一目标分辨率将适用于 800 x480 和 854 x480 屏幕分辨率的 Android 设备,主要是中级智能手机和迷你平板电脑。

符合逻辑的最终目标分辨率是**四分之一高清,**为 MDPI 屏幕的 480×270 像素。这符合原始智能手机 480 x320 的分辨率,因为仍有廉价的预付费智能手机在生产中使用这种分辨率。

请注意,480 乘 270 正好是高清的四分之一(因此 QHD 或四分之一高清),将分辨率缩小 1/2 或 1/4 可获得最佳质量结果。只要我们在视频压缩优化方面做得很好,我们应该能够将 MPDI 的比特率提高到 192 kbps。那么我们将为 HDPI 设定 384 kbps 的比特率,为 XHDPI 设定 768 kbps 的比特率。

数字视频创作:创造我们的火星表面飞越

在我们真正能够进入视频压缩软件包之前,比如 Sorenson Squeeze Pro ,我们需要创建一个我们星球表面的飞越。由于火星最近很受欢迎,我们将使用名为布莱斯 7 专业版的流行地形编辑软件包来创建 900 帧视频内容。我们将以每秒 15 帧的速度创建该视频内容,让我们有一分钟的时间飞越红色星球表面,我们可以用它来学习一些数字视频优化的概念。

如果你还没有,去 DAZ 3D 网站下载 Bryce 7.1 或谷歌“Bryce 7 Download ”,然后去下载该软件的网站。安装软件并启动,你会看到如图图 11-1 所示的 Bryce 7 Pro 启动画面。

9781430257462_Fig11-01.jpg

图 11-1 。来自 DAZ 的 Bryce 7 Pro 是地形生成软件

转到参考资料中的 Bryce 文件夹。zip 文件,并在那里打开 Mars.br7 文件。这是一个简单的表面地形,用类似火星的材质绘制,有一些薄薄的橙色云,地平线上的薄雾,和一个黄色的太阳。我在地表附近放置了一个摄像机,显示了地面、天空和太阳,并制作了一个直线摄像机的动画来模拟在行星表面快速飞行。

生成未压缩的帧

在下一章中,我们将通过 Java 代码使用一些更高级的媒体播放器控件,我们可以调整我们的播放速度。现在,我们将在一个。AVI 文件格式压缩可以阅读练习视频压缩。

  1. Once Bryce has launched, use the File image Open menu sequence, and locate the book resources folder and the Bryce sub-folder, and then open the Mars.br7 file, as shown in Figure 11-2. Notice the 3D data is only 77 KB.

    9781430257462_Fig11-02.jpg

    图 11-2 。使用 Bryce 7.1 中的文件image打开菜单序列打开 Mars 文件

  2. 一旦这个文件被打开,你会看到一个平坦的陆地地形网格和地平线;在你渲染这个 3D 场景之前,你不会看到这个将会生成的图像。如果您想要渲染 3D 火星场景的第一帧,您可以在左侧 UI 面板中单击较大的(最低居中的)绿色球体。

  3. 接下来,我们使用文件 image **进行动画设置。。。**菜单序列,用于设置我们的 3D 动画参数,包括当前开始时间或帧数、持续时间或结束时间或帧数、我们使用 FPS 的帧速率、我们的回放参数和帧显示类型(帧数或 SMPTE 时间)。

  4. Set the Current time to 0:00:00.00 Frame Number 0 and the Duration time to 0:01:00.00 Frame Number 900 and set the FPS to 15 as shown in Figure 11-3.

    9781430257462_Fig11-03.jpg

    图 11-3 。设置动画参数

    注意注意布莱斯用术语描述 Pong 动画:钟摆动画!

  5. 我猜钟摆动画是对 Pong 动画描述的巧妙改进,但我们打算将我们的动画设置为播放一次,并控制 Android 中的任何其他播放参数。此外,将显示参数设置为帧数,这样一旦您实际渲染了这个火星动画,您将能够看到哪个帧当前正在 Bryce 中渲染。首先,我们必须配置我们的渲染引擎。

配置渲染引擎

接下来让我们设置我们的渲染选项。这些控制 3D 特征,决定每一帧视频渲染需要多长时间,如图 11-4 所示。

9781430257462_Fig11-04.jpg

图 11-4 。为火星表面飞越动画设置我们的火星 3D 动画渲染选项

  1. 通过单击渲染球体旁边的向下箭头,打开一个菜单,并选择渲染选项来访问渲染选项对话框。
  2. 我们的火星动画在每个分辨率级别的渲染时间(或帧渲染速度)的主要决定因素是使用质量模式设置来设置的。我们对每一帧进行的反走样越多,渲染的时间就越长。为了获得最高质量的视频和合理的渲染时间,使用对话框左上角显示的常规设置。 *** 我们在每一帧上做了一个完整的渲染,用纹理渲染 ??,这样我们的火星表面看起来就像红色的岩石。我们还优化了统一场景最小优化来节省每帧渲染时间。* 我们关闭所有的后处理选项关闭,因为我们不需要伽马校正,我们不希望额外的噪声通过 48 位抖动引入每一帧,因为那个选项会使我们的文件变大,正如本书前面所学的。* 我们使用透视投影(一个普通的相机镜头)和无遮蔽,因为我们没有使用任何阿尔法通道。最后,我们将关闭光学灯光设置下所有“昂贵”的计算密集型选项,因为这些对于这个简单的行星表面飞越来说是不需要的,并且大多涉及有水的场景,而且火星上没有太多的水。* 对话框右下角的最后一部分控制着反走样设置,以及用于在我们正在创建的 3D 动画视频的每一帧上执行反走样功能的算法的类型。算法从上到下变得越来越复杂(和耗时),但提供了更好的结果,代价是处理周期。* 最后, AA 半径定义了每个像素周围需要抗锯齿的像素数量。把这一套留给: 1.0AA 射线决定渲染引擎用于反走样的射线数量或射线跟踪计算的数量。较高的 AA 光线值会产生较长的渲染时间。最后,将 AA 公差设置为 15 ,将算法设置为算法,因为其他算法会使用更多的处理时间并增加渲染时间。**

**渲染动画

现在,我们准备渲染我们的火星表面飞越 3D 动画,所以下拉文件 image 渲染动画菜单序列,输入动画的持续时间,并指定输出参数和文件位置。参考图 11-5 中的了解我们将要经历的序列。

9781430257462_Fig11-05.jpg

图 11-5 。渲染动画对话框和编辑按钮(中间)和设置按钮(右边)的子对话框

  1. 选择整个持续时间选项,显示我们在动画设置对话框中输入的信息,如图 11-3 所示。
  2. 接下来点击编辑按钮,并选择我们的视频数据的全帧(未压缩)设置,并将其存储在 AVI(音频视频交错)容器。这意味着我们不必使用带编号的文件,正如你可以想象的那样,900 帧的文件会有点乏味。
  3. 接下来单击 Set 按钮,并在 Documents 下的用户文件夹中为 Mars.avi 文件指定一个文件位置。创建一个名为 Android 的文件夹(如果还没有的话),然后在其中创建一个 Hello_World 子文件夹,用于这个特定应用的正在进行的数据文件。
  4. 如果您设置了网络,并希望在网络上的所有机器上进行渲染,则需要在所有工作站上安装 Bryce 7.1 Pro。一旦完成,你可以使用对话框底部的配置按钮来配置你的渲染农场。然后,你所要做的就是选择渲染网络单选按钮来启用这个功能。
  5. 一旦这个对话框中的一切都配置好了,如图图 11-5 所示,你可以点击对话框右下角的复选标记,900 帧火星表面动画视频的渲染就开始了。在六核 64 位工作站上,分辨率为 1080×1920,耗时 8 小时。

创建解析文件

工作流程的下一步是为 270×480(MDPI)、480×854(HDPI)和 720×1280(XHDPI)Android 设备分辨率密度创建其他三个分辨率源文件。这是在文档设置对话框中完成的,正如你已经知道的,双击绿色 Bryce 渲染球体可以进入该对话框,如图 11-4 左侧所示。

  1. For each of our target DPI resolutions, we need to enter our desired target resolution into a Document Resolution field in this Document Setup (see Figure 11-6).

    9781430257462_Fig11-06.jpg

    图 11-6 。在 Bryce 中设置文档分辨率、纵横比、抗锯齿和报告渲染时间选项

  2. 确保您的文档宽高比为 9:16 宽屏(在这种情况下,我们的应用在纵向模式下使用,或使用垂直方向),并使用上面显示的精确 Android 设备屏幕分辨率。

  3. 如果您的分辨率差了一个像素,取消选中“约束比例”选项(这将使您的纵横比保持锁定),并将像素更改一个像素,然后重新选中相同的“约束比例”单选按钮,以便在完成更改后再次打开它。如你所知,当我们全屏播放视频时,我们试图逐个像素地达到这些主流(常见)Android 设备屏幕分辨率,以获得最佳质量,而不需要 Android 为我们缩放数据,这可能会引入伪像。Android 目前不擅长的一件事是缩放图像和视频。

  4. 当您每次设置一个目标分辨率时,单击对话框底部的复选标记,如图 11-6 所示,您会注意到 Bryce 7 将视口缩放到该分辨率的像素数。

接下来,重复图 11-3 到 11-5 所示的工作过程,渲染三个较小的分辨率,并分别将文件命名为 Mars270.aviMars480.aviMars720.avi

请注意,在图 11-5 中, Mars.avi 高清数字视频文件(1080×1920 分辨率)已经渲染完成,我们正在该屏幕截图右侧的对话框中为我们的 Mars270.avi 文件设置文件名。还要注意,我们选择了一个选项来报告渲染时间,这样我们就可以从数学的角度来看渲染引擎在做什么。这在图 11-7 中显示,我把它包括在内,这样我们就能明白为什么我们的 3D 渲染过程在我们生成的 900 帧数字视频中的每一帧上花费了这么多时间。

9781430257462_Fig11-07.jpg

图 11-7 。渲染报告信息

我在 480 乘 854 渲染时生成了这个截图,因为我们知道 480 乘 854 是 409,920,所以我们已经知道第一个数字是如何计算的。第二个数字是抗锯齿的像素数量,我们已经知道这意味着图像中有 149,699 个像素在两个像素之间存在剧烈的颜色变化(这简单地等同于图像中不同对象或颜色之间的边缘)。

渲染引擎投射出 146 万条光线来定义该图像中每个像素的颜色值,即平均每个像素 3.55 条光线。如果你想要确切的数字,3.55 乘以 409,920 等于 1,455,216。

因为我们关闭了阴影,所以渲染引擎没有投射阴影光线;你可以想象,这为我们节省了一些渲染时间!在投射的 146 万条光线中,有 125 万条击中了什么东西,206,376 条没有击中任何东西,因此没有用于创建像素数据值。

数字视频压缩:关键概念和技术

现在我们有了一个 3D 渲染的源星球(火星)飞越动画数据,我们可以用它来创建数字视频文件,我们可以在下一章中使用,在那里我们将学习如何实现这个数字视频内容,以及如何通过 Android VideoView 和 MediaPlayer 类在我们的 Android Hello World 应用中控制它。

在我们这样做之前,我们必须优化我们在上一节中渲染的这些文件,因为它们的文件大小从 400MB 到 4GB 不等,并且我们的。APK 文件只能是 50MB,所以我们必须了解数字视频编解码器的设置和数字视频压缩的概念,设置和技术。

用于优化数字视频的最佳软件叫做 Sorenson Squeeze。如果你曾经为 Adobe Flash 平台开发过,你可能会对这个软件包很熟悉,现在它已经是第九版了。

我们要做的第一件事就是推出 Sorenson Squeeze Pro ,版本 8.5.1。当你这样做时,你会看到一个如图图 11-8 所示的启动屏幕。

9781430257462_Fig11-08.jpg

图 11-8 。启动 Sorenson Squeeze Pro 来压缩和优化我们的 MPEG4 和 WebM 视频文件

这个视频压缩软件工具有七个主要区域,如上面的图 11-8 所示,包括输入选项、编解码器预设、效果过滤器、发布平台、通知、序列器窗口(右下角)和视频编辑窗口(右上角)。

导入视频

我们需要做的第一件事是导入我们的数字视频 AVI 全帧未压缩格式文件。

  1. This is done by clicking the icon on the upper right labeled Import File. This opens a file navigation dialog window, shown in Figure 11-9, which allows us to navigate through the hard disk drive, and find the 3D Mars.avi source digital video files that we created earlier in Bryce 7.1 Pro.

    9781430257462_Fig11-09.jpg

    图 11-9 。使用导入文件对话框定位我们的 Mars.avi 源文件

  2. 找到你的 My Documents 文件夹(应该在你的 Users 文件夹下)和它下面的 Android 文件夹,里面包含了你的 Android 相关工作素材。找到您之前为您的 Hello_World 应用新媒体资源创建的子文件夹,选择 Mars.avi 文件并单击打开按钮。一旦你这样做了,你将在 Sorenson Squeeze 的视频编辑部分看到火星视频的第一帧(见图 11-11 )。

  3. Locate the + icon on the left at the bottom of the Presets Pane, and click it to open a new presets dialog, so that we can develop a codec that is Android specific, as shown in Figure 11-10. Type Android 1080x1920 15 FPS in the Name: and Desc: fields, to label our custom settings, which will be saved for future use with other digital video. The Format Constraints are set to None, and the Stream Type is set to Non-Streaming as we are optimizing to a .MP4 file. The best Codec to use for H.264 is MainConcept.

    9781430257462_Fig11-10.jpg

    图 11-10 。创建 1080×1920 Android 视频压缩设置,预设为 15 FPS 和 1 Mbps 数据速率

  4. 我们使用 MainConcept H.264 编解码器的原因是因为它是最先进的,并且具有一种多遍方法,该方法使视频数据多次通过,以尽可能实现最佳的质量与文件大小之比。这需要更多的时间,但我在这里假设您至少有一个四核工作站(如果不是八核的话),并且质量是您最终关心的问题。一旦选择了多遍,设置帧速率:15 FPS ,并设置目标数据速率1024 Kbps (也是 1 Mbps)。

  5. 选择约束最大数据速率,将最大数据速率设置为 1024 Kbps 的 150% ,即 1536 Kbps。这为我们的数据突发留出了一些回旋空间,如果在任何给定的帧上需要多一点空间的话。

  6. 帧尺寸设置为 1920 年的1080以防止缩放,保持纵横比,让我们确保编解码器每 40 帧查看关键帧。最后,选择场景变化时的自动关键帧,并选择一个平均关键帧频率 50 作为该压缩设置预设的开始。

  7. Once everything is set click the OK button shown in Figure 11-10 and you will be returned to the main Squeeze Pro program screen where you can now click the Apply button in the top left panel to apply these settings (see Figure 11-11).

    9781430257462_Fig11-11.jpg

    图 11-11 。将 Android 1080×1920 15 FPS 视频压缩预设应用到导入的 Mars.avi 文件

  8. 最后,我们可以点击挤压它!软件屏幕右下方的按钮,开始我们的视频压缩过程。

当您的视频数据正在压缩时,我们将稍微讨论一下什么是关键帧,以及它们在整个视频压缩过程中的确切作用。

关键帧是您的动画数据的一帧,编解码器会查看该帧以存储视频在该确切帧的完整图像,这也是它被称为“关键帧”的原因

编解码器为您的视频节省空间(减少数据足迹)的方式是不必保存数字视频中的每一帧,在这种情况下,它是一个飞越火星表面的 3D 动画。

这是通过当今新媒体算法中一些最复杂的数学来完成的,并且肯定超出了一本学习 Android 书的范围。它本质上是进入第四维(时间)并查看它编码的每个关键帧之后的帧,以查看下一帧相对于它刚刚采样的关键帧发生了什么变化。

然后,编解码器只对帧与帧之间发生变化的数据进行编码,这可以在许多视频场景中节省大量编码数据。这方面的一个很好的例子就是正在说话的头部视频,例如教师或政治家。如果这个人保持冷静,静止,固定在一个地方,那么只有他们的嘴部(说话)动作会随着画面的变化而变化。

在这种情况下,因为包含嘴的像素区域只占整个视频帧像素的很小一部分,编解码器可能会逐帧编码这些像素,而不是整个图像。在这种情况下,视频帧的许多像素会随着时间的推移而冻结,这就是编解码器可以减少数据占用量的原因,这就是编解码器所关注的游戏名称。

因此,一个非常安静(零头部运动)的说话人编码得非常好,只要他们背后的背景不是太嘈杂或者没有编解码器需要解决的大量快节奏运动。因此,在一个繁忙的新闻编辑室中,一个正在说话的头的编码效果不如在一个具有纯色、均匀照明背景的特殊设置中的一个正在说话的头。

编解码器不能 很好地转码(跨帧编码)的东西是噪声,就像静态图像编解码器一样,以及全帧运动。全帧移动是指视频帧中的每个像素改变其在视频的每个帧中的位置。这样的例子包括非常快速的相机摇摄,例如在赛车的拍摄中使用的;镜头拉近或拉远,例如用于拍摄自然纪录片的镜头;以及像动作片和火星飞越中所用的摄像机飞越。

现在,您的数字视频应该已经完成编码,您将在软件屏幕右下角的 Squeeze Pro 工作区底部看到一个可播放的视频图标,如图图 11-12 所示。

9781430257462_Fig11-12.jpg

图 11-12 。完成视频压缩(仅屏幕截图的底部区域)显示 MP4 视频准备播放

压缩视频

接下来,我们需要压缩我们的 MDPI 分辨率 Android 屏幕的视频素材:

  1. Use File image Save to save your Squeeze Pro environment as it sits now. Next use File image New to set out a new blank canvas, so we can use the Import File button to bring in our Mars270.avi raw video data file to compress into H.264 MPEG-4 data, as shown in Figure 11-13.

    9781430257462_Fig11-13.jpg

    图 11-13 。使用 192 Kbps 多通道 H.264 在 Squeeze 中以 15FPS 压缩 270×480 数字视频源

  2. Follow the same work process that we did for the 1080 version and click the + button in the Presets Panel and set up a preset for our 270 by 480 resolution video that uses a Target Data Rate of 192 Kbps and a Max Data Rate of 256 Kbps, or a 133% burst data rate ceiling. Keep KeyFrames every 30 frames (30 total per 900 frames), and be sure to lock in the resolution by specifying it, and then using Maintain Aspect Ratio, or Same as Source, as shown in Figure 11-14.

    9781430257462_Fig11-14.jpg

    图 11-14 。为我们的 270×480 视频文件输出设置编解码器设置

  3. 请确保使用顶部的“名称”和“Desc”字段来标记您的新 MDPI 编解码器设置;我使用了 Android 270×480 15 FPS 的标签,因为它很短并且具有描述性,非常适合预置面板。一旦一切都配置好了,点击 OK 按钮,然后在预置面板中选择新的编解码器定义,点击 Apply 按钮将其应用到你的 Mars 项目设置窗口。现在,你可以点击**挤压它了!**按钮,压缩你的全帧未压缩。AVI 转换成 MP4 文件。

创建的 MPEG-4 文件只有 1.3 兆字节,所以我们用这些设置获得了惊人的压缩,因为我们的源(原始)数据在。AVI 文件接近 342 兆字节。正如我们之前所了解的,这为我们提供了 263:1 的压缩率,它将 Android 设备必须处理的数据量从一分钟几百兆字节减少到一分钟内处理不超过一兆字节,这是 Android 设备处理器应该能够处理的。

值得注意的是,优化视频绝不是一次性的过程,如果您发现视频可以在所有设备上流畅播放,您可以通过指定更高的数据速率来提高质量。例如,256 Kbps和 384 Kbps 上限的将是您尝试的下一个设置,更多的关键帧,比如说,每 20 帧使用关键帧,而不是 30 帧(这相当于采样和存储 45 个关键帧,而不是总共 900 帧中的 30 个)。文件大小会增加,视频数据的视觉质量也会提高,并且将有更多的视频数据供处理器解码和显示。

接下来,我们需要压缩我们的 HDPI 分辨率 Android 屏幕视频素材:

  1. Use File image Save to save your Squeeze Pro environment as it sits now. Next use File image New to set out a new blank canvas, so we can use the Import File button to bring in our Mars480.avi raw video data file to compress into H.264 MPEG-4 data, as shown in Figure 11-15.

    9781430257462_Fig11-15.jpg

    图 11-15 。使用 384 Kbps 多通道 H.264 在 Squeeze 中以 15FPS 压缩 480×854 数字视频源

  2. Follow the same work process that we did for the 1080 version, and click the + button in the Presets Panel, and set up a preset for our 480 by 854 resolution video that uses the Target Data Rate of 384 Kbps and a Max Data Rate of 512 Kbps, or a 133% burst data rate ceiling. Keep KeyFrames every 30 frames (a total of 30 per 900 frames), and be sure you lock in the resolution, by either specifying it and using Maintain Aspect Ratio, or by using the Same as Source option instead, as shown in Figure 11-16.

    9781430257462_Fig11-16.jpg

    图 11-16 。为 480×854 视频文件输出设置编解码器设置

  3. Finally, we need to compress our XHDPI target resolution Android screen’s video asset, so again, use File image Save to save your previous Squeeze Pro environment. Next use File image New to set out a new blank canvas, so we can use the Import File button to bring in our Mars720.avi raw video data file to compress into an H.264 MPEG-4 MP4 data file, as shown in Figure 11-17.

    9781430257462_Fig11-17.jpg

    图 11-17 。通过 768 Kbps 多通道 H.264 在 Squeeze 中以 15 FPS 压缩 720×1280 数字视频源

遵循我们为 1080 版本所做的相同工作流程,并单击预设面板中的+按钮,为您的 720 x 1280 分辨率视频设置一个预设,该预设使用 192 Kbps目标数据速率256 Kbps最大数据速率,或 133%突发数据速率上限。每隔 30 帧保留关键帧(每 900 帧总共 30 帧),并确保通过在对话框 UI 中指定并使用保持宽高比或使用与源相同选项来锁定分辨率,如图图 11-18 所示。

9781430257462_Fig11-18.jpg

图 11-18 。为 720×1280 视频文件输出设置编解码器设置

接下来我们需要将我们创建的 MPEG-4 H.264 视频素材放入适当的 Hello_World 项目资源文件夹,或者对于已经优化的 raw 视频数据文件,放入 Hello_World/res/raw 中,不需要 Android 进一步优化。这是我们在本书前面提到的一个概念,我们将在本书的下一部分详细讨论。一旦完成,我们的视频数据就可以使用了。

在 Android 中使用数字视频资源:资源的原始文件夹

现在我们已经创建了四个分辨率密度(DPI)目标分辨率视频文件,我们可以将它们复制到相应的资源子文件夹中,然后我们可以通过 XML 标记和 Java 代码在第十二章中访问它们。

  1. Open your OS file manager utility, for Windows 7 or Windows 8 it’s Windows Explorer, and go to your C:/Users/YourName/workspace/Hello_World/res resource folder and right-click it to get a context-sensitive menu, shown in Figure 11-19.

    9781430257462_Fig11-19.jpg

    图 11-19 。在 Hello_World resources (res)文件夹下新建一个名为/raw 的 image文件夹来存放视频文件

  2. At the bottom of this menu, select the New image Folder sub-menu sequence to create a new folder under the res (resource) folder and name it raw. Once created, the folder shows up under your menu sub-folder (see Figure 11-20).

    9781430257462_Fig11-20.jpg

    图 11-20 。拖放四个 DPI 分辨率的版本的火星表面飞越到/raw

    注意正如我在本书前面提到的,这个原始资源子文件夹在 Android 中用于包含已经由开发人员优化的新媒体素材,并且不需要 Android 操作系统的任何进一步优化或其他干预。本质上来说,/raw 文件夹中的新媒体素材是我们告诉 Android 存储在。APK 文件,并在应用代码中按原样访问和使用它们,无需任何进一步的修改。

  3. 创建/raw 文件夹并显示在文件管理实用程序的左窗格中后,选择四个文件中的第一个,然后按住 Shift 键并选择该组中的最后一个文件。这用于分组选择整组文件,然后你可以将所有四个文件拖放到 /res/raw 文件夹中,如图图 11-17 所示。

  4. Next, we need to go into the /res/raw folder, and use the file manager to rename all the files from the detailed names assigned by Squeeze Pro to use simpler filenames, as we’ve been doing previously in this book. This process is shown in Figure 11-21 showing our new lower-case names and the raw folder.

    9781430257462_Fig11-21.jpg

    图 11-21 。将火星视频的四个分辨率版本重命名为:mars270、mars480、mars720、mars1080

    注意正如我们过去所做的,我们将遵循 Android 的小写字母和数字素材命名约定,使用行星名称(在本例中为火星)和数字视频的水平分辨率(以像素为单位)作为我们的视频文件命名约定。

  5. Now let’s launch our Eclipse ADT IDE, and make sure that we did everything correctly and that our digital video assets are in place and ready for use in our next chapter covering how to code Java and XML mark-up to implement digital video in our Android application. As you can see in Figure 11-22, we now have our /raw folder in our Android Hello_World project resource folder and it has four MPEG-4 files inside it that we can use to play video in our Travel to Planet Activity screen. If you already had Eclipse running, be sure and use the F5 (or Refresh) work process.

    9781430257462_Fig11-22.jpg

    图 11-22 。查看项目的/res/raw/文件夹中的四种分辨率版本的数字视频 MPEG-4 文件

接下来,我们将简单介绍一下 Android VideoView 类,我们将在第十二章中使用该类来保存和播放我们的数字视频素材,然后我们将了解将数字视频素材编码到 Hello_World Android 应用中所需的基础知识。

在 UI 设计中播放数字视频:Android 的 VideoView 类

Android 设计了一个用户界面小部件,使我们的应用中的数字视频播放变得相当简单。

它被称为 VideoView 类,它使用 XML 中的 < VideoView > 标签来通过各种参数实现其 UI 属性,正如我们在本书前面的章节中用 Java 和 XML 实现 TravelPlanet 活动类时已经看到的那样。

Android VideoView 类是 SurfaceView 类的子类,后者本身是 View 类的子类,我们知道,后者是 Java Object 类的子类。

VideoView 类实现了 MediaPlayerControl 接口,这样我们就可以访问与控制数字视频播放相关的方法。

这个接口是由Android . widget . media controller包提供的,所以它的完整路径是Android . widget . media controller . media player control,这样我们就能够在 Java 代码中播放暂停我们的视频,如果我们愿意的话。

事实上,在 XML 和 Java 中实现 VideoView 和 MediaPlayer 功能是我们下一章关于 Android 中数字视频的内容,现在我们已经有了基础知识,知道我们到底在谈论什么数字视频概念、编解码器、功能和回放。

摘要

在这一章中,我们为理解数字视频新媒体素材打下了坚实的基础,这样我们就可以在 Android 应用中实现它们。

我们了解了像素、它们所在的视频帧以及将它们压缩和解压缩为可用文件的编解码器如何协同工作,以使大量移动数字图像数据更易于管理和使用。

然后,我们了解了数字视频领域的一些重要概念和标准,例如标准分辨率标清视频及其原生分辨率,以及更新的高清视频及其两种常见分辨率规范。我们了解到 Android 中的数字视频可以被捕获,或者保存在一个资源文件夹中,并包含在我们的应用中。APK 文件,或者它可以是流视频,并通过视频服务器远程提供。

我们讨论了比特率和每秒位数的重要概念,以及这种测量方法如何用于数字视频的数据占用优化过程。我们还了解到,比特率不仅决定了什么类型的带宽速度环境可以容纳数字视频,还决定了什么类型的 Android 设备数据处理(CPU)功能可以在视频流实际通过该带宽时对其进行解码。

我们发现即使机器人内部有视频文件。APK 容器,比特率和质量水平是获得清晰视频的关键组成部分,播放流畅,没有丢帧,以试图跟上数字视频文件所需的帧速率。

接下来,我们了解了 Android 支持的数字视频编解码器和格式,如 MPEG4 H.263 和 H.264 编解码器,以及 VP8,以及这些编解码器在 Android 操作系统内部和外部的支持级别有何不同,以及它们是 HTML5 规范的一部分。

我们还研究了我们需要在所有 Android 消费电子设备上支持的最佳数字视频分辨率,发现美国和欧洲 SD 和 HD 数字视频的主要广播分辨率与当前大多数 Android 设备的屏幕分辨率非常匹配。

我们学习了如何使用 Bryce 7 Pro,并创建了基本的火星表面动画。我们练习了使用 3D 地形生成软件为一些关键的 Android 设备分辨率实现数字视频文件素材生成。我们还学习了一些关于 3D 渲染的基本概念。

接下来,我们讨论了数字视频优化中的关键概念,并了解了除分辨率之外影响视频压缩的其他因素,如平移和缩放,以及将噪声引入帧图像数据的因素,如之前的压缩伪像。

我们通过在 Sorenson Squeeze Pro 内的 MainConcept H.264 编解码器中实际使用这些设置来了解关键帧和比特率,从而将我们的数字视频从原始、未压缩的原始数据状态优化为高度压缩的数字视频素材,以便在我们的 Hello_World 应用中使用。

我们通过优化四个目标分辨率密度级别的视频素材来实践这一压缩流程,从中等分辨率(MDPI) 480 像素屏幕、高分辨率(HDPI) 800 像素屏幕、超高分辨率(XHDPI)伪高清 1280 像素屏幕,一直到超高分辨率(XXHDPI)真高清 1920 像素屏幕密度。

然后,我们将这四个优化的数字视频素材复制到 resource 文件夹中,复制到我们创建的/raw 子文件夹中,以便在 Hello_World 项目文件夹中保存这些数字视频素材。我们重命名了这些文件,以便在代码中更容易访问,然后进入 Eclipse ADT,以确保一切就绪,我们可以开始在应用中编码、实现和测试这些数字视频素材。

为了了解如何在我们的 Hello World Android 应用中实现数字视频,我们学习了用户界面小部件 VideoView,这是我们在本书前面的 TravelPlanet 活动屏幕用户界面中最初实现的。

我们学习了 Android VideoView 类的层次结构,以及 XML 中相应的标签元素。我们了解到 VideoView 实际上是一个连接到 MediaPlayer 类的 SurfaceView 容器,接下来我们将学习如何在我们的 TravelPlanet.java 活动类中实现它。

在下一章中,我们将深入研究使用 Android VideoView 和 MediaPlayer 类及其方法实现数字视频所需的 XML 和 Java 编码,从而进一步了解数字视频新媒体元素。我们还将构建 Java 代码来控制我们的 VideoView XML UI 定义,并最终控制用户体验。**

十二、Android 中的数字视频:使用VideoView

在本章中,我们将利用在前一章中学到的数字视频概念、技术和格式的新知识,在我们自己的 Hello_World Android 应用中实际实现数字视频素材。

我们将在 TravelPlanet.java 活动子类中添加新的 Java 代码,以实例化 MediaController 对象,这样我们就可以在 VideoView 用户界面元素中播放视频,该元素是我们在本书前面的 activity_travel.xml 屏幕布局定义中设置的。

我们将介绍如何播放与源视频具有不同纵横比的视频,以及如何创建和添加视频传输控制用户界面元素。

使用 Android MediaController 类播放视频

因为我们已经在 FrameLayout 容器内的 activity_travel.xml 文件中添加并配置了 VideoView 用户界面元素,所以让我们打开我们在本书前面创建的 TravelPlanet.java Activity 类,并添加代码来实际播放我们的视频,现在我们已经将文件放在正确的资源文件夹/res/raw 中,可以开始播放了。

我们需要做的第一件事是为播放视频设置一个存储视频文件的路径,以便 Android MediaController 类可以找到它们,并最终播放它们。

这是通过统一资源标识符(**【URI】)**来完成的。URIs 在 Android 开发中如此重要,以至于有一个专门的类来定义和使用它们,叫做 Uri ,就像俄语名字一样。

Uri 在android.net包中,因为 URIs 经常被用来通过互联网访问数据,通常是通过我们都习惯使用的那个 HTTP:// 名字。你很快就会看到,要访问 Android 的资源文件夹中的东西,我们将使用 android.resource:// 路径访问定义。

Uri 类 是 java.lang.Object 类的子类,因此 java 中的 Uri 对象路径通过 android.net.Uri 向上通过 java.lang.Object。如果您对 android 开发者网站参考部分感兴趣,可以在以下 URL 找到有关 Uri 类的更多信息:

[`developer.android.com/reference/android/net/Uri.html`](http://developer.android.com/reference/android/net/Uri.html)

因此,让我们为我们的数字视频文件定义我们的 URI,使用 Uri 类的 parse( ) 方法,以及 getpackageName( ) 方法,它是 Android Context 类的一部分,该类也是从 java.lang.Object 子类化而来

在 TravelPlanet 活动中创建 Java videoUri 对象

让我们开始添加代码到我们的 TravelPlanet.java Activity 类,通过添加我们的第一行 Java 代码来设置将用于引用我们的视频数据的 URI。

让我们将定义视频 URI 的第一行代码放在活动的**setContentView(r . layout . Activity _ Travel)**方法调用的下面,该方法调用引用了我们对行星用户界面屏幕布局定义的访问。

设置我们的 Uri 对象的 Java 代码行的结构如下:

Uri videoUri = Uri.parse("android.resource://"+getpackageName()+"/"+R.raw.mars270);

这段相当密集的代码定义了我们的 Uri 对象,将其命名为 videoUri ,并将其设置为我们的【mars270.mp4】文件的 Uri,该文件位于我们的 /res/raw 文件夹中。

这是通过 Uri 类来实现的。parse( ) 方法,用于构造 Android 操作系统可以理解的有效 URI 对象,从而实现。

这里概述的 Uri.parse( ) 方法被传递了一个连接(通过 Java 语言+操作符完成),该连接是位于 android.resource:// 中的文件的位置与我们的 Hello_World 包名称和上下文的连接(通过 getpackageName( ) 方法获得),以及一个路径级别定义符,也称为正斜杠字符,最后是我们的资源标识符 R.raw.mars270,,这是我们的文件名(第一部分

你可以在图 12-1 中看到这一行代码,它就在我们已经在活动中编写的定义视频视图的代码行之前。

9781430257462_Fig12-01.jpg

图 12-1。将 Java 代码添加到我们的 TravelPlanet.java 活动中,为我们的视频视图实现 MediaController

因为我们已经定义了我们的 travelVideo VideoView 对象,并且引用了我们的 travelVideoView XML 用户界面元素定义,所以我们可以使用**。setVideoURI( )** 方法,通过下面一行代码将我们新创建的 videoUri 对象连接到我们的 travelVideo VideoView 对象:

travelVideo.setVideoURI(videoUri);

这相当简单,我们通过videoUri 对象传递给 travelVideo VideoView 对象。setVideoURI( ) 方法,从而定义哪个数字视频数据文件用于我们的 VideoView,以及这个数字视频文件在我们的 Android 项目中的位置。

接下来,我们需要设置一个 Android MediaController 对象来完成数字视频文件的播放,所以接下来让我们开始吧。

创建 MediaController 对象来播放我们的数字视频

为了在 Android 中播放我们的数字视频文件,我们将利用 Android MediaController 类,它是 Androidframe layout类的子类。

请注意,我们使用 FrameLayout 来包含我们的 VideoView UI 元素(小部件),并且记住 FrameLayout 类是 ViewGroup 类的子类,View group 类本身又是 View 类的子类,View 类又是 java.lang.Object master 类的子类。

为了创建一个 MediaController 对象,让我们使用我们常用的对象实例化 Java 代码结构和 new 关键字,使用下面一行 Java 代码创建一个 videoMediaController (命名)MediaController 对象:

MediaController videoMediaController = new MediaController(this);

接下来我们要做的是告诉我们的 MediaController 对象我们希望它在哪里播放我们的视频。这是通过完成的。setAnchorView( ) 方法,定义哪个 Android 视图对象,在本例中,我们的 VideoView 对象名为 travelVideo ,我们希望将此 MediaController 对象的功能锚定到该对象。这是通过下面一行 Java 代码实现的:

videoMediaController.setAnchorView(travelVideo);

现在我们已经告诉我们的 MediaController 对象我们希望它在哪里播放我们的数字视频数据,但是我们仍然需要告诉我们的 travelVideo VideoView 对象访问哪个 MediaController 对象来播放数字视频数据。这是通过。setMediaController()方法,使用以下代码从 travelVideo VideoView 对象调用该方法:

travelVideo.setMediaController(videoMediaController);

既然我们的 travelVideo VideoView 对象和 videoMediaController 对象已经连接在一起,我们所要做的就是调用 travelVideo VideoView 对象的一些关键方法来控制数字视频数据的位置(Z 顺序)、焦点和回放,这样我们就完成了在 Android 应用中实现基本的视频回放。

在我们的 travelVideo 视频视图中控制视频播放

既然我们的 VideoView 对象和 MediaController 对象相互接口,我们可以调用 travelVideo 对象的一些关键方法,以确保我们的数字视频数据显示在屏幕层堆栈的前面(顶部),具有焦点以便可以播放,并开始播放。

我们需要调用的第一个方法是**,通过使用 travelVideo 对象的点符号。bringToFront( )** 法。这确保了我们的 VideoView 位于任何其他用户界面元素之上,这些元素可能与 VideoView 一起在其布局容器中定义。完成此任务的 Java 代码如下所示:

travelVideo.bringToFront();

在开始视频回放之前,我们需要做的下一件事是确保视频视图具有焦点,这是我们在本书前面提到的概念。这是通过。requestFocus()方法,它通过请求焦点来确保 VideoView 获得焦点。这样,即使 VideoView 当前拥有焦点,调用该方法也能确保我们拥有焦点。这是通过下面一行 Java 代码完成的:

travelVideo.requestFocus();

最后,我们准备开始播放数字视频数据,使用。start()方法,您可能已经猜到了。这是通过以下代码行完成的,该代码行通过点标记法调用了 travelVideo 对象:

travelVideo.start();

现在是时候启动我们的 Nexus S 模拟器,并在 Android 软件模拟器中测试我们的数字视频文件播放。请注意,在实际的 Android 硬件设备上测试数字视频文件回放总是更好;然而,对于这本书来说,这是不可行的,因为有成千上万不同的制造商和型号,你们任何一个读者都可能有。

因为我们拥有的唯一基线是最新的 Eclipse ADT 及其各种软件模拟器,所以我们必须在测试中使用该解决方案。

在 Eclipse ADT 的 Nexus S 模拟器中测试视频回放

让我们启动我们的 Nexus S 模拟器,当你的 Hello World 主屏幕出现时,点击右边模拟器中的菜单按钮,然后选择前往行星活动屏幕,这样我们就可以看到我们的数字视频文件回放,结果如图图 12-2 所示。

9781430257462_Fig12-02.jpg

图 12-2。在 Nexus S 模拟器中播放 Mars 数字视频,并测试 onTouch()事件是否仍然有效

正如你所看到的,我们的数字视频保留了 16:9 的宽高比,即使在 5:3 宽高比的模拟器屏幕上,当我们点击视频屏幕时,我们会返回到我们的应用主屏幕,并显示回家祝酒信息。

因此,我们现在知道,我们现有的来自前几章的 Java 代码仍然可以工作。你可能注意到了,屏幕右侧有一个白条;这是因为布局容器的背景颜色未配置,默认为#FFFFFF 或白色值。您已经知道如何通过 FrameLayout 标记中的 android:background="#000000 "参数将它更改为黑色值。

您可能想知道:如果我想缩放这些数字视频数据以适合整个显示屏,会怎么样?这可能是一个选项,取决于它是什么类型的数据,以及主题对不均匀缩放或纵横比解锁的缩放的敏感程度。

我们正在使用的火星表面数据可以在用户没有察觉到主题材质变化的情况下进行缩放,而全屏人脸或说话的头部可能会看起来失真(肥胖或锥形)。

在 Android 中有一种方法可以实现这个最终结果,就是将 VideoView 用户界面元素放在 RelativeLayout 容器中,而不是使用 Framelayout 容器,这样可以保持纵横比。

用 RelativeLayout 容器而不是 FrameLayout 容器可以做到这一点的原因是,这两个不同的布局容器支持完全不同的 android:layout 参数。

使用相对布局缩放视频视图以适合屏幕

进入你的 activity_travel.xml 文件,将 FrameLayout 容器标签改为 RelativeLayout 容器标签,如图图 12-3 所示。

9781430257462_Fig12-03.jpg

图 12-3。更改 activity_travel.xml 屏幕布局,使用 Relativelatout 容器和 layout_alignParent 标签

在 RelativeLayout 容器中实现这一点的关键是使用 android:layout 参数,这些参数将数字视频文件的四个不同边与父容器对齐,在本例中是相对布局。我已经将这四个参数添加到我们的 VideoView 标签中,就在 android:id 参数之后,如图图 12-3 所示。

诀窍是将所有四个Android:layout _ align parent参数设置为值 true ,如以下 XML 标记代码所示:

android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"

这将拉伸 VideoView 容器的每一侧,以匹配 RelativeLayout 容器的两侧。因为在 android:layout_width 和 android:layout_height 参数中,RelativeLayout 标签被设置为 match_parent ,因此,无论 android 操作系统试图保持(锁定)数字视频宽高比的正确倾向如何,参数设置都会强制 VideoView 填充屏幕。结果如图图 12-4 所示。

9781430257462_Fig12-04.jpg

图 12-4。在 Nexus S 中测试我们的 relative layout layout _ align parent 标签

接下来,我们需要在 Nexus S 模拟器中测试 Travel to a Planet 活动,以确保一切正常。数字视频全屏播放,触摸屏幕会将我们的最终用户带回到主屏幕,就像我们在本书前面对该功能进行编码一样。

然而,有一个重要的问题对于 Android 新手来说可能不是很明显,那就是触摸视频应该会启动媒体控制器传输。视频传输是一组用于播放、暂停或浏览数字视频内容的基本用户控件。

目前,我们的 onTouch()事件处理阻止了我们的最终用户访问这个媒体控制器传输 UI 元素,因此,我们需要向我们的 UI 添加一个按钮 UI 元素,并将其与 onClick()事件处理程序连接起来,以释放触摸屏。这样,Android 的 MediaController 可以在屏幕被触摸时显示其传输实用程序。

修改我们的界面以支持媒体控制器传输

因此,我们需要做的第一件事是删除 onTouch( ) Java 代码,以确保我们可以访问 MediaController 传输控件。简单的方法是使用 Java 中的双正斜杠代码注释功能,临时注释掉这个代码块,第一行代码如下所示:

// travelVideo.setOnTouchListener(new View.OnTouchListener() {

这显示在图 12-5 的中,以及其他注释代码。

9781430257462_Fig12-05.jpg

图 12-5。删除干扰 MediaController 传输 UI 元素的 onTouch()事件处理

正如您所看到的,Eclipse 会立即在不再需要的三个 import 语句旁边显示警告标志,因为这段代码的注释已经将它从 Eclipse Java 编译器的“视图”中删除了。

因为 Eclipse 中的警告不会阻止我们在 Nexus S 模拟器中运行我们的应用,所以让我们使用熟悉的 Run As Android 应用工作流程,看看我们是否可以在触摸我们的视频屏幕时显示媒体控制器传输。

正如你在图 12-6 中看到的,当我们在模拟器中触摸屏幕时,我们现在可以访问视频传输控件。然而,我们现在没有办法回到我们的主屏幕,所以我们需要添加一个用户界面元素来实现这一点。幸运的是,我们只是在一个 Android 模拟器中处于测试模式,这个模拟器的右上角有一个红色的 X 关闭图标,我们可以用它来退出!

9781430257462_Fig12-06.jpg

图 12-6。点击或触摸屏幕即可调出媒体控制器传输

接下来,让我们在屏幕右上角的视频顶部添加一个按钮用户界面元素,它允许我们的用户退出 Travel to Planet 活动屏幕并返回到我们的 Hello World 应用主屏幕。

添加一个按钮 UI 元素让我们返回主屏幕

在 Eclipse 中找到并打开你的 activity_travel.xml 编辑标签,在 < VideoView > 标签下添加一行空格,然后按 < 键调用添加新标签助手对话框。找到按钮标签,如图图 12-7 所示,双击将标签添加到< RelativeLayout >容器中。现在我们准备添加参数来配置我们的按钮标签。

9781430257462_Fig12-07.jpg

图 12-7。向 activity_travel.xml 屏幕布局定义文件添加一个按钮用户界面元素标签

首先,我们需要给我们的按钮 UI 元素一个 ID,以便我们可以在 Java 代码中引用它,所以使用 android:id 参数,并通过 android:id="@+id/travelButton "参数标记将按钮命名为 travelButton。

接下来找到您的 strings.xml 文件,为名为 travel_button_caption 的按钮添加一个 < string > 常量,值为 Return to Home Screen ,如下所示:

<string name="travel_button_caption">Return to Home Screen</string>

使用 android:text 参数来引用这个字符串常量,同时确保添加必需的 android:layout_widthandroid:layout_height 参数,这两个参数都设置为 wrap_content 常量值。

因为我们希望按钮在屏幕的右上角,不要挡住我们的视频内容,所以让我们使用Android:alignParentTop = " true ",以及**Android:alignParentRight = " true "**参数来实现这个最终结果。

最后,让我们将我们的按钮背景颜色设置为白色,并通过为 android:background 参数使用八个字符(AARRGGBB)的十六进制值为其赋予半透明的 alpha 值,如下所示:

android:background="99FFFFFF"

到目前为止,我们的按钮标记的 XML 标记应该如下所示:

<Button android:id="@+id/travelButton"
        android:text="@string/travel_button_caption"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:alignParentTop="true"
        android:alignParentRight="true"
        android:background="#99FFFFFF" />

整个 RelativeLayout XML 容器标记如图 12-8 所示。

9781430257462_Fig12-08.jpg

图 12-8。配置我们的按钮标签参数,在屏幕右上方放置一个透明的 UI 按钮

现在是时候使用 Nexus S 模拟器再次测试我们的应用了,看看我们是否有一个媒体控制器传输 UI 元素,以及一个退出按钮 UI 元素,用于我们到行星活动屏幕的旅行。

调用作为 Android 应用运行菜单序列并启动 Nexus S 仿真器。当您的应用启动时,单击菜单按钮并选择“旅行到星球”选项,然后转到该活动屏幕。

正如你在图 12-6 中看到的(这里不需要复制截图)视频播放,点击或触摸屏幕会调出传输控件,但是按钮 UI 元素没有覆盖在视频的右上角,所以有些不对劲。

我做的第一件事,看看我是否能纠正这个问题,就是在按钮标签中键入 android: ,看看我是否能找到一个 z 顺序参数。

z-order 参数会将按钮带到层顺序的顶部,以便按钮显示在视频的顶部,就像我们希望的那样。唉,目前还没有这样的参数,所以我们必须想出另一种方法,通过一些巧妙的标签参数工作来接近我们正在寻找的 UI 结果,很可能是在和标签中。

故障排除 我们的 VideoView 用户界面设计

让我们看看我们的代码,看看可以做些什么来让我们的顶部安装按钮在我们的数字视频素材的顶部可见。看起来 android:layout_alignParentTop 参数正在将 VideoView UI 元素拉至屏幕顶部,并覆盖在按钮 UI 元素上。

因为没有可用的 android:z-order 参数,所以我尝试的第一件事是在 RelativeLayout 中的 VideoView 标签之前剪切并粘贴按钮标签,以查看布局容器中的顺序是否影响 z-order。

当我在模拟器中测试它时,这不仅没有解决问题,还产生了一个运行时错误。然后,我恢复了之前的标签顺序,并寻找解决这个问题的另一种方法。如果我不能将这个按钮 UI 元素放在 VideoView 的顶部,我接下来会尝试将它直接放在 VideoView UI 元素的顶部,并与之连接。

我尝试的下一件事是Android:layout _ alignParentTop参数从 VideoView 标签移动到 Button 标签,然后将 android:layout_below 参数添加到 VideoView,在我删除 alignParentTop 参数的地方,因此在< VideoView > :" 中查找以下参数

android:layout_below="@+id/travelButton"

这都显示在图 12-9 中。

9781430257462_Fig12-09.jpg

图 12-9。添加按钮标签参数,将按钮元素放置在屏幕顶部,并将其置于 VideoView 的中央

然后,我再次使用我的运行 Android 应用工作流程来启动 Nexus S 模拟器,看看这是否解决了问题,正如你在图 12-10 的左侧看到的,它确实解决了问题。然而,现在我们的背景颜色和透明度设置与我们 UI 的外观和感觉不匹配。

9781430257462_Fig12-10.jpg

图 12-10。在 VideoView UI 元素的基础上,测试和改进我们的“星球之旅”UI 屏幕按钮元素

因此,让我们将我们的 android:background 参数更改为黑色或 #000000 ,以便它与应用顶部使用的其他颜色相匹配。

接下来,我们需要建立一个与我们的火星大气相匹配的文本颜色,并为我们提供与黑色背景颜色的高对比度,所以让我们使用之前在 android:textColor 参数中使用的橙色值#FFDDBB,如图 12-9 所示。

在进行这些更改时,一定要使用位于 Eclipse 中 XML 编辑器底部的图形化布局编辑器标签,这样您就可以了解这些标签对您的用户界面设计有什么影响。举例来说,你会注意到,我们的文本,现在我们实际上可以看到,实际上太小了,需要变大。

所以,让我们添加一个 android:textSize="28sp" 参数,并确保我们的文本是漂亮和大的。请记住,文本(字体)使用标准像素(SP)作为它们在 XML 标签中的表示,而不是像其他图形或用户界面元素那样使用密度像素(DP)。现在,我们已经准备好使用我们的作为 Android 应用运行工作流程,并在 Nexus 模拟器中检查我们的新用户界面屏幕。结果如图 12-10 右侧所示,如你所见,我们的文本现在可读了,设计也匹配了。

接下来,我们需要将 Java 代码添加到我们的 TravelPlanet.java 活动类中,这样我们就可以实例化新的按钮用户界面元素,从而使它起作用,这样我们的用户就可以单击它返回到主屏幕。

在 Java 代码中增加了返回地球的按钮

让我们在我们的 travelVideo.start( ) 方法调用下添加一行新代码,并实例化我们的按钮用户界面元素。我们将其命名为 travelButton ,并使用下面的代码行将其引用到我们的 XML 参数:

Button travelButton = (Button)findViewById(R.id.travelButton);

接下来,我们将修改 TravelPlanet.java 活动中被注释掉的代码,将其从附加到 VideoView UI 元素的 onTouch()事件处理场景更改为附加到 Button UI 元素的 onClick()事件处理场景。注释掉的代码显示在图 12-5 中,而所有修改后的代码显示在图 12-11 中。

9781430257462_Fig12-11.jpg

图 12-11。添加我们的 travelButton Java 代码来实例化一个按钮 UI 元素并附加 onClick()处理程序

因此,使用以下代码,将您的**travel video . setontouchlistener()方法更改为travel button . setonclicklistener()**方法调用:

travelButton.setOnClickListener(new View.OnClickListener() {new code goes in here}

我们需要做的下一件事是删除剩余的双斜线注释字符,这样我们就可以修改 OnClickListener( ) 方法的内部,将 onTouch( ) 方法及其内容改为 onClick()方法。新的 onClick( ) 代码应该如下所示:

@Override
public void onClick(View v) {
         Toast.makeToast(TravelPlanet.this, "Going Home", Toast.LENGTH_SHORT).show();
         finish();
}

请注意,图 12-5 截图中显示的三个警告突出显示中的两个已经消失,现在我们已经在新的 onClick( ) Java 代码中使用了视图吐司类对象。因为 MotionEvent 类与我们已经移除的 onTouch()事件处理一起使用,所以您可以继续删除这个 import 语句,您的 Java 代码现在将没有错误和警告,并准备好在您的 Nexus 模拟器中进行测试。使用 Run As Android Application 菜单序列在 Nexus S 模拟器中运行你的 Hello_World 应用,并测试功能,如图图 12-12 所示。

9781430257462_Fig12-12.jpg

图 12-12。测试我们的按钮用户界面元素和媒体控制器传输

我现在能看到的关于“去行星旅行”活动用户界面的唯一问题是用户体验 UX 设计的问题。

问题是,我们在屏幕顶部添加的按钮 UI 元素看起来像是屏幕的标题,而不是可以单击的按钮。这对于你来说似乎很小,但是对于一些认为屏幕顶部是一个标题而不是一个按钮的用户来说,这种假设会把他们困在这个屏幕上,没有办法回到主屏幕。所以,让我们解决这最后一个 UX 问题,我们将在这里完成!

微调我们的星球旅行用户体验设计

为了突出顶部居中的按钮是一个按钮而不是标题,我们需要在它周围放置一个边框,这样它看起来更像一个按钮。我做的第一件事是在我的按钮标签中键入 android: ,并在助手对话框的可用参数列表中查找 android:border 参数。令人惊讶的是,没有这样的选择,所以我不得不变得有创意,想出其他的东西来达到同样的视觉效果。XML 标记代码如图图 12-13 所示。

9781430257462_Fig12-13.jpg

图 12-13。使用带有按钮边距参数的 RelativeLayout 背景创建一个按钮边框效果

我所做的是为父 RelativeLayout 容器设置一个**Android:background = " # FFDDBB "标签参数。因为我们的 VideoView UI 元素充满了屏幕,所以我可以有效地使用背景色(我匹配了 android:textColor 参数值)在按钮周围创建一个漂亮的边框,还可以使用Android:Layout _ margin = " 4dp "**参数将按钮 UI 元素的黑色背景拉得足够大,让这个相对布局背景色显示出来,并成为我的按钮的边框!

现在让我们使用作为 Android 应用运行的工作流程,看看我们的按钮现在看起来和行为是否更像一个按钮,而不是屏幕标题。

正如你在图 12-14 中看到的,活动用户界面屏幕顶部的按钮现在看起来更像是一个按钮,而不是一个屏幕标题,点击按钮会让用户返回到主屏幕,点击数字视频会显示一个 Android Media Controller 传输控制栏,所以两者现在都工作得很好。

9781430257462_Fig12-14.jpg

图 12-14。测试旅行到星球 UI 屏幕和带边距边框的按钮

在第十三章中,我们将了解视频的另一部分,即音频,以及如何为您的 Android 应用创建、操作、压缩、编码、优化和处理音频。在我们学习了关于音频的基本概念和技术后,我们将开始把它整合到我们的应用中。

摘要

在涵盖数字视频的这一章中,我们仔细研究了如何在 Hello World Android 应用 XML 和 Java 代码以及用户界面和用户体验设计中融入我们在上一章中了解到的数字视频概念和素材。

我们学习了 Android MediaController 类,它用于在我们的 Android 应用中播放数字视频文件。我们学习了如何使用 Uri 类和 Uri.parse( ) 方法以及 getPackageName( ) 方法创建一个统一资源标识符对象,或 URI 对象。

然后,我们为 TravelPlanet.java 活动类编写 Java 代码,通过实例化名为视频媒体控制器的新对象来实现媒体控制器类,然后通过使用将媒体控制器对象锚定到 travelVideo 视频视图对象。setAnchorView( ) 方法。

然后我们通过调用完成了 MediaController 到 VideoView 的连接。setMediaController( ) 方法使用 videoMediaController 对象引用关闭我们的 travelView VideoView 对象。完成后,我们就可以使用 travelView 对象设置数字视频播放了。

我们调用了几个视图类方法来设置我们的数字视频回放,首先使用将视图放到最前面。bringToFront( ) 方法,然后通过调用来确保它有焦点。requestFocus( ) 方法。然后我们开始使用播放我们的 MPEG4 数字视频文件。start( ) 方法,并在 Nexus S 仿真器中测试了我们的程序逻辑。

接下来,我们进行了一些故障排除,以了解为什么我们没有在 VideoView UI 元素的底部获得 MediaController 传输条。

我们需要用一个按钮 UI 元素替换我们的 onTouch()事件处理,并实现 onClick()事件处理,以便我们的 VideoView 可以捕获 onTouch()事件并显示 MediaController 传输控制小部件。

最后,我们对用户界面设计做了一些微调,添加了一些影响按钮颜色的参数,增加了对比度,并与应用的外观和感觉相匹配。我们还在按钮 UI 对象周围添加了一个细边框,这样它就不会以屏幕标题的形式出现在最终用户面前,这可能是用户体验设计的一个错误。

在下一章,我们将了解 Android 操作系统支持的另一种主要新媒体类型:数字音频。我们将学习数字音频背后的核心数字音频概念、设计原则和制作技术,以及用于在我们的 Android 应用中实现数字音频的文件格式和编解码器。我们将了解开源音频编辑软件包 Audacity,以及如何使用它来创建用于 Hello World Android 应用的音频素材。

十三、音频简介:概念和优化

在这一章中,我们将为您提供理解数字音频工作原理所需的基础知识,并让您能够在 Android 应用中胜任数字音频工作。

数字音频是更难理解和优化的新媒体素材之一,因为数字音频本质上是数字采样声波的集合,而且,它们是肉眼不可见的。因此,我们不得不依靠我们的耳朵而不是我们的眼睛,我们大多数人都不习惯使用我们的耳朵来制作,除非我们是音频工程师,作曲家,音乐家或歌剧歌手!

因为数字音频数据是实时回放的,所以它像数字视频一样,在等式中引入了第四维度,即时间。这一点,再加上当今功能强大但复杂的数字音频编辑软件以及编码和解码算法所带来的复杂性,使得数字音频像数字视频一样难以理解。

我们将在如何应用于为您的 Android 应用最终用户创建最佳用户体验的背景下审视所有这些。

这意味着不仅要了解数字音频编辑的基本原理及其数据占用优化,还要了解哪些数字音频格式最适合用于 Android,以及使用哪些采样率。这些主题是高级的,所以我们将利用三个章节来有效地彻底涵盖一切,以便您能够很好地处理数字音频,以及如何针对 Android 进行优化。

模拟音频的基础:声波和空气

正如你们这些立体声发烧友已经知道的,声音是由声波在空气中脉动产生的,这就是为什么超低音扬声器是巨大的 12 英寸到 24 英寸的锥体,通过磁脉冲快速推出,将主要声波扔向摇滚音乐会上的 10 万名观众。

在数字音频出现之前,模拟音频是主要的消费电子行业。事实上,今天仍然如此,声波由复杂的模拟电子设备控制,包括电容器、电阻器、振荡器、晶体、电子管、电路板、扬声器锥体、心形麦克风和类似的模拟技术。正如我所说的,数字音频是复杂的,这种复杂性的一部分来自于需要将模拟音频和数字音频技术联系起来。

正如声音是由各种尺寸的扬声器锥体产生的,扬声器锥体本质上只是由一种或另一种材质制成的薄膜,通过脉冲或振动产生声波,因此我们的耳朵也可以接收并听到这些声波,通过接收空气或振动的脉冲并将它们转化为我们大脑可以处理的数据。

声波产生不同的音调,这取决于声波的频率。宽的或不常见的(长)波产生较低的(低音)音调,而更频繁的(短)波长产生较高的(高音)音调。有趣的是,不同频率的光会产生不同的颜色,所以模拟声音(音频或音乐)和模拟光(颜色或摄影)之间有着非常密切的联系,这种联系一直延续到数字领域。

声波的音量将根据该声波的振幅或高度(或大小)来预测。因此,频率是沿着 2D 的 X 轴的波的间隔有多近,振幅是沿着 Y 轴测量的波有多高。

声波本身可以有不同的形状,以承载不同的声音效果;基线类型的波被称为正弦波,我们在高中数学中学习过正弦、余弦和正切数学函数。

精通音频合成的人都知道,在声音设计中还有其他类型的核心声波,例如看起来像锯子边缘的 saw wave (因此得名),或者使用直角整形的 pulse wave (产生即时的开和关声音或脉冲)。

甚至随机波形,如噪音,也被用于声音设计,以获得尖锐的声音效果。您可能已经从本书中有关数据占用优化的内容中猜到,声波中出现的混乱或噪声越多,编解码器就越难压缩,特定声音的数字音频文件也就越大。接下来,我们将进一步了解这些模拟音频声波如何通过称为采样的过程转化为数字音频数据,这是声音设计和音乐合成的核心工具。

数字音频的基础:采样、采样分辨率和采样频率

将模拟音频声波转化为数字音频数据的过程称为采样。如果你在音乐行业,你可能听说过一种键盘或机架安装设备,它被称为采样器

采样是一个过程,它将音频波(通常是复杂的正弦波)分割成段并以数字格式(0 和 1)存储它们的形状和任何其他波形属性(即噪声)。

音频声波的这些数字片段被称为样本,因为它们在任何给定的时间点对声波进行采样。样本的精度取决于定义每个波切片所用的数据量,与数字成像一样,这种精度被称为分辨率,或采样分辨率,通常定义为 8 位、16 位或 24 位。

在数字成像(和数字视频)中,分辨率用颜色的数量来量化,而在数字音频中,分辨率用多少位数据来定义每个音频样本来量化。

正如您可能已经猜到的那样,更高的采样分辨率,或者再现给定声波样本所需的更多数据,将产生更高的音频回放分辨率,从而产生更高的声音质量。这就是为什么 16 位(也称为: CD 质量)音频听起来远远好于 8 位音频的原因,就像 24 位彩色图像比 8 位彩色图像看起来更好一样。

在数字音频领域,我们现在有 24 位数据采样,在数字音频领域称为: HD 数字音频。高清数字广播电台使用 24 位样本分辨率,因此每个音频样本或声波片段包含 16,777,216 个样本分辨率单位。一些较新的 Android 设备也将支持高清音频,如你在广告中看到的那些智能手机,具有超高质量(高清)音频,这意味着它们具有 24 位音频硬件支持。

除了数字音频采样分辨率,我们还有数字音频采样频率、或在一秒钟内以该特定分辨率采集多少样本。在数字成像中,采样频率类似于我们使用的图像中的像素数。

采样频率也可以称为采样率,您可能熟悉 CD 音质音频,它被定义为使用 16 位采样分辨率和 44.1 kHz 采样率,它需要 44 个100 个样本,每个样本将包含 16 位数据,即每个样本中的总数据量为 65,536 个单位。

让我们计算一下,看看有多少数据样本用于提供一秒钟的原始(未压缩)数字音频数据。这是通过将 65,536 个单位乘以 44,100 个样本来实现的,以获得用于表示一秒 CD 质量音频的 2890137600 个样本的数据值。

因此,要计算出音频文件中的原始数据,你需要将采样比特率的十进制值乘以采样频率,再乘以音频片段中的秒数。请放心,这将是一个很大的数字,但放松;音频编解码器在优化这些数据方面非常出色,数据占用空间非常小,质量损失非常小(听得见)。

因此,我们在数字成像和数字视频中的完全相同的权衡也存在于数字音频中。我们包含的数据越多,得到的结果就越(高)质量,但代价是数据占用空间更大。在视觉媒体中,这是使用色深、像素和(数字视频)帧来定义的,而在听觉媒体中,它是通过采样分辨率结合采样率来定义的。

数字音频行业常见的采样速率包括:22 kHz、32 kHz、44.1 kHz、48 kHz、96 kHz、192 kHz 以及最近的 384 kHz。较低的采样率,例如 22 kHz 或 32 kHz,将足以对基于语音的数字音频进行采样,例如电影对话或电子书的旁白轨道。

较高的采样速率更适合音乐和其他需要高动态范围(高保真度)的声音效果,这样可以再现出出色的“高保真”音质。

一些声音效果,例如我们将在 Hello World 应用中使用的,可以使用较低的 32 kHz 采样速率,只要使用的采样分辨率是 16 位质量。

关键数字音频属性:CD 音频、HD 音频、音频流和音频比特率

正如我们已经提到的,卓越音频质量的行业基准被称为 **CD 音频标准,**被定义为在 44.1 kHz 数据采样频率下的 16 位数据采样分辨率。这是 20 世纪用来制作音频 CD-r om 的材质,至今仍在使用。

还有一个更近的 高清音频标准****24 位数据采样,采样频率为 48 kHz (甚至 96 kHz),目前用于高清收音机和高清音频兼容的 Android 设备,如高保真智能手机。

如果您打算在 Android 应用中使用高清音频,您需要确保您的目标最终用户将拥有高清音频标准兼容硬件,这将需要利用这一更高水平的音频保真度。

就像您的数字视频数据一样,您的数字音频数据既可以在您的应用中被捕获(/ raw 文件夹中的数据文件),也可以从远程数字音频文件流数据服务器中流出。

流式数字音频数据的好处是,它可以减少应用的数据占用,就像流式数字视频数据一样。许多相同的概念同样适用于音频和视频。

流式音频节省了数据空间,因为您不必在中包含所有繁重的新媒体数字音频数据。APK 文件,所以如果你正计划编码一个自动点唱机应用,你可能要考虑你的数字音频数据流。否则,请尝试优化您的数字音频数据,以便您可以将它包含在。APK 档案。这样,当应用的用户需要它时,它总是可用的。

流式数字音频的缺点是,如果您的用户连接(或音频服务器)中断,您的音频文件可能无法始终供最终用户播放和收听!您的数字音频数据的可靠性和可用性是流式音频相对于专属数字音频数据的另一方面需要考虑的关键因素。

就像数字视频一样,数字音频流的一个主要概念是数字音频数据的比特率。

正如我们之前所了解的,比特率是在压缩过程中定义的,我们将在本章后面的章节中详细讨论这一点,因为它与数字音频有关。不过,让我们在这里重温一下,只是为了确保你理解它,因为它是一个基本概念。

比特率定义了您的数字音频数据将被压缩到什么程度。比特率将在数字音频文件压缩过程中定义,这就是为什么我将在本章后面的数字音频数据占用优化部分详细介绍它。

比特率较低(数量较少)的数字音频文件将对数据进行更多的压缩,这将导致较低的质量水平,但在更多的消费电子设备上播放更流畅。

如你所知,比特率是每秒比特数(BPS)的量度,可以被有效地处理或传输。随着计算机处理器变得更快,它可以处理更多的 BPS 同样,随着数据带宽连接变得更快,它可以更舒适地每秒发送或接收更多的比特。

因此,重申一下,位/秒不仅对流式数字音频内容很重要,因为它将适合带宽,而且一旦它到达您的 Android 设备,位速率会影响哪些内容可以足够快地处理(解码),以允许 Android 设备内的处理器顺利播放。

出于这个原因,比特率之所以重要,有两个原因:一是因为流音频,二是因为捕获(嵌入)数字音频文件(作为捕获文件保存在 Android 应用资源/raw 文件夹中)的一个原因(解码 CPU 的处理速度)。

因此,在 Android 应用中使用捕获或嵌入的音频,音频资源的比特率越低,越多的 Android 设备可以平滑地解码该音频资源(而不会丢失音频样本)。接下来,我们需要了解 Android 支持的数字音频文件格式和编解码器,并了解使用哪些格式和何时使用它们。

数字音频格式:支持 Android 中流行的数字音频编解码器

Android 中的数字音频编解码器远远多于数字视频编解码器(MPEG-4 和 VP8)。Android 支持 .MP3 (MPEG-3)文件,Wave (PCM 或脉码调制)。WAV 文件、 .MP4 (或. M4A) MPEG-4 音频、OGG Vorbis ( )。OGG 的音频文件,马特罗斯卡**。MKS** 音频文件,FLAC ( )。FLAC )音频文件,甚至 MIDI(。MID,。MXMF 和。XMF)文件,从技术上来说,这根本不是真正的数字音频数据。让我们先把 MIDI 去掉,因为我们不会在 Hello World 应用中使用它。

MIDI 代表代表乐器数据接口 ,它是数字音频和计算机最早合作的方式之一,可以追溯到 20 世纪 80 年代。第一台具有 MIDI 端口的计算机是 Atari ST-1040,它允许你将键盘合成器,如雅马哈 DX-7,插入该 MIDI 端口,并使用称为 MIDI 音序器的音频软件将 MIDI 数据播放和记录到计算机中。

MIDI 文件不包含样本数据,也就是说,它不包含音频,只包含演奏数据。MIDI 跟踪键盘上的哪些键被按下、何时被按下、按键持续时间、按键被按下的力度(称为:后触摸)以及类似的性能特征。当通过合成器回放 MIDI 文件时,它会复制播放器的性能,即使播放器不再播放那个轨道

在音乐制作行话中,曲目是歌曲创作或表演的一个片段或一部分。在录音棚里,不同的乐器和声乐表演被保存在不同的音轨上,这样在后期制作中,工程师可以更精确地对它们进行“混音”。

这在 MIDI 序列软件中的使用方式是,您可以弹奏一个乐器轨道,将其录制为 MIDI 数据,当您弹奏它旁边的另一个乐器轨道时,序列器会为您回放它。这使得数字歌曲作者能够通过使用计算机来组合复杂的安排,而不是一个充满音乐家的工作室。

Android 支持 MIDI 文件的回放,但没有实现 MIDI 类,所以为 Android 编写 MIDI 音序器并不是一件容易的事情,尽管代码论坛上有人在谈论它。由于这个原因,它超出了本书的范围,我在这里提到它只是为了让你了解数字音频的历史和范围,因为 MIDI 在数字音频的发展中发挥了重要作用。

Android 支持的最常见的格式是 MP3 数字音频文件格式。由于 Napster 等音乐下载网站,我们大多数人都熟悉 MP3 文件,我们大多数人收集这种格式的歌曲,以便在流行的 MP3 播放器上使用,并通过 CD-ROM 和 DVD-ROM 收集音乐。

MP3 数字音频文件格式如此受欢迎的原因是因为它具有良好的压缩比和质量比,并且因为播放它所需的编解码器几乎可以在任何地方找到,甚至在 Android 操作系统中。只要你通过使用最佳的编码工作过程获得最佳的质量水平,MP3 将是 Android 应用中可以接受的格式。

值得注意的是,MP3 是一种有损音频文件格式,就像 JPEG 用于成像一样,其中一些音频数据(以及质量)在压缩过程中被丢弃,并且以后无法恢复。因此,如果您要使用 MP3 音频,请确保保存原始的未压缩音频数据文件。

Android 确实有一个无损音频压缩编解码器,叫做: FLAC ,代表 免费无损音频编解码器 。FLAC 是一个开源音频编解码器,由于软件解码器的免费性质,它的支持几乎和 MP3 一样广泛。因此,使用 PNG32 和 FLAC,在您的 Android 应用中使用完全无损的新媒体素材是可能的。

FLAC 也非常快(编码非常紧密),支持 HD (24 位)音频,使用它没有专利问题。如果您需要在合理的数据占用范围内获得高质量的音频,这是一个很好的音频编解码器。

FLAC 支持大范围的样本分辨率,从每个样本 4 位到每个样本 32 位。它还支持非常宽的采样频率范围,从 1Hz 到 655350Hz (65 kHz),增量为 1Hz,因此非常灵活。从音频回放硬件的角度来看,我建议使用 16 位采样分辨率和 44.1 kHz 或 48 kHz 采样频率。

Android 3.1 和更高版本支持 FLAC,因此如果您的最终用户使用现代 Android 设备,您应该能够安全地使用 FLAC 编解码器。

Android 支持的另一个开源数字音频编解码器是 Vorbis 编解码器,一个来自Xiph.Org 基金会有损音频编解码器。Vorbis 编解码器数据通常保存在中。OGG 数据文件容器,因而 Vorbis 通常被称为 Ogg Vorbis 数字音频数据格式。

Ogg Vorbis 支持从 8 kHz192 kHz 的采样速率,以及到 255 的数字音频离散通道(正如我们现在所知,这代表 8 位音频通道)。所有版本的 Android 都支持 Vorbis。

Vorbis 正在迅速接近 HE-AAC 和 WMA(Windows Media Audio)Professional 的质量,并且在质量上优于 MP3、AAC-LC 和 WMA。这是一种有损格式,因此 FLAC 仍然比 Ogg Vorbis 具有更高的质量水平,因为它包含所有原始的数字音频样本数据。

Android 支持最流行的 MPEG4 AAC ,或高级音频编码 ,编解码器,包括 AAC-LCHE-AAC ,和 AAC-ELD 。这些都可以包含在 MPEG4 容器(. 3gp、. mp4、. m4a)中,并且可以在所有版本的 Android 中回放,只有 AAC-ELD 除外,它只在 Android 4.1 以后才受支持。 ELD 代表增强型低延迟,该编解码器旨在用于实时双向通信应用,如数字对讲机。

最简单的 AAC 编解码器是 AAC-LC 或低复杂度编解码器,这是使用最广泛的编解码器,应该足以满足大多数应用。与 MP3 相比,AAC-LC 应该以更低的数据占用量产生更高质量的结果。

下一个最复杂的 AAC 编解码器是 HE-AAC 或高效 AAC 编解码器。该编解码器支持 8 kHz 至 48 kHz 的采样速率,以及立体声和杜比 5.1 声道编码。Android 支持解码该编解码器的 v1 和 v2 级别,并且还在 4.1 版(Jelly Bean)之后的 Android 设备中以 HE-AAC v1 编解码器编码音频。

对于通常以不同于音乐的声波类型为特征的编码语音,还有两种其他 AMR自适应多速率 音频编解码器,它们对于编码语音或不需要高质量再现的短脉冲音效(如炸弹爆炸音效)等非常有效。

Android 中有一个 AMR-WB ,或自适应多速率宽带标准,它支持从 6.6 到 23.85 kbps 的 9 个离散设置,音频比特率以 16 kHz 采样,这对于语音来说是一个高采样率。例如,如果您正在创建一个交互式电子书应用,这将是用于旁白轨道的编解码器。

Android 中还有一个 AMR-NB自适应多速率窄带标准,它支持从 4.75 到 12.2 kbps 的 8 种离散设置,以 8 kHz 采样音频比特率,这是一个足够的采样率,如果进入编解码器的数据是高质量的,或者由于其噪声性质(炸弹爆炸)导致的音频样本不需要高质量。

最后,我们有 PCM脉码调制 音频,俗称或**。WAV** 音频格式。你们中的许多人都熟悉这种格式,因为它是用于 Windows 操作系统的原始音频格式。

PCM 音频通常也用于 CD-ROM 和 DVD-ROM 内容,以及电话应用。这是因为 PCM Wave audio 是一种未压缩的数字音频格式,它没有应用于其数据流的计算密集型压缩算法,因此解码(CPU 开销)对于电话设备或 CD-ROM 或 DVD-ROM 播放器来说不是问题。

因此,正如您很快会看到的,当我们开始将数字音频素材压缩成各种格式时,我们可以使用 PCM 作为基线,但可能不会将其放入我们的。APK 文件,因为有其他格式,像 FLAC 和 AAC,它会给我们同样的质量,使用数量级更少的数据。

最终,真正找出 Android 中哪种音频格式对于任何给定的音频数据实例具有最佳数字音频编解码器的唯一方法是,实际上用我们知道支持良好且高效的主要编解码器对您的数字音频进行编码,并观察数据足迹结果,然后听听音频回放质量,以做出我们的最终决定。

数字音频优化:跨设备播放

优化我们的数字音频素材,以便在市场上的各种 Android 设备上播放,将比优化我们的数字视频甚至我们的数字图像更容易。

这是因为在 Android 设备之间,屏幕分辨率和显示器纵横比的差异要比数字音频播放硬件支持的差异大得多。这是因为用户的耳朵无法像眼睛一样感知视频中的音频质量差异。

一般来说,所有 Android 设备都有三个主要的数字音频支持“最佳点”,我们应该为我们的高质量音频提供支持。较低质量的音频(如旁白轨道或简短的声音效果)可以使用 8 位、12 位或 16 位分辨率的 22 kHz 或 32 kHz 采样。

这些高质量音频目标包括 CD 质量音频,也称为 44.1 kHz 的 16 位数据采样,HD 质量音频位于该音频频谱的另一端,也称为48 kHz采样率的24 位数据采样,以及未命名的“中间某处”规范,使用 48 kHz 采样率的 16 位数据采样。右手的最后一个选项可以产生类似于剧院中使用的 THX 质量音频的效果。

因此,我们在所有 Android 设备上优化数字音频素材的工作流程将是创建 44.1 kHz 和 48 kHz 的 16 位素材,然后以 Android 支持的不同格式对其进行优化(压缩),并查看哪些素材能够以最低的数据占用量提供最高质量的音频播放。

我们将使用最近发布的 Audacity 2.0.3 数字音频编辑和工程软件包来完成这项工作。这个软件包是开源的,因此我们所有的读者都可以访问,并且可以在所有流行的操作系统平台上使用,包括 Windows、Macintosh 和 Linux。

用插件和编解码器库设置 Audacity 2

如果你还没有下载 Audacity 的最新版本(在撰写本书时是 2.0.3 ),现在就去audacity.sourceforge.net下载你的免费版本并安装它。

请确保您还安装了最新的 Audacity 2 插件、FF-MPEG 和 LAME 编解码器,这样您就可以为数字音频文件格式导出最新的数字音频编码算法。

重要的是要注意,在不改变编解码器的解码器侧的情况下,编解码器的编码器侧可以变得更有效;因此,在任何给定的时间获得最新的编码器并不意味着 Android 需要更新其 OS API 库中的解码器支持,以便您能够从任何特定的编解码器中获得质量到文件大小的好处。

一旦你下载并安装了最新的 Audacity,你可以通过进入 Audacity 的下载选项卡(页面)并点击插件和库链接,用所有新的插件和编解码器来增强它。

要安装 90 多个免费的 LADSPA 插件,找到并点击链接“90 多个 LADSPA 插件”这会触发上的下载。如图 13-1 中 LADSPA 插件设置对话框的选择目标位置阶段所示。如果您没有将这些插件放在正确的文件夹中,它们将不会加载到 Audacity 中。

9781430257462_Fig13-01.jpg

图 13-1。为 LADSPA 插件选择目标位置设置对话框,指定\Audacity\Plug-Ins 文件夹

如果你愿意,你也可以安装奈奎斯特和 VST 插件,也列在这一页,虽然他们不是本书所需要的,因为我们不打算深入到数字音频工程在这个时候!

接下来滚动到页面底部,右键单击 LAME FAQ 链接,打开 LAME MP3 编码器页面,然后单击顶部的下载页面链接。向下滚动一半并单击文本链接。EXE 文件读取Lame _ v . 3 . 99 . 3 _ for _ windows . EXE并下载安装。EXE 文件复制到其默认目录位置。

接下来,滚动到页面底部,右键点击 FFmpeg FAQ 链接,打开 FFmpeg 导入导出库页面,然后点击顶部的下载页面链接。向下滚动 LAME 链接下面的两个链接,然后单击。EXE 文件FFmpeg _ v 0 . 6 . 2 _ for _ Audacity _ on _ windows . EXE并下载安装。EXE 文件复制到它的默认目录位置。

一旦您将所有这些插件和编解码器安装到您的内容制作工作站上,您就可以启动 Audacity,它将在其启动初始化例程中找到并安装最新的数字音频编解码器库支持和插件。

数字音频创作:寻找 Hello World 音效

为了找到一些用于商业用途的免费音频样本,我将使用谷歌搜索引擎,并键入一个查询,以获得免费音频样本,或免费数字工作室样本,或免费音频文件,或免费数字音频文件,以及类似的谷歌搜索术语组合。

请注意,由于在提供这些数字音频素材的不同网站中使用了关键字,每一次谷歌搜索都会出现完全不同的结果。

有几十个好的网站都符合我们的需求,所以当你有空的时候,一定要深入调查这些网站。确保您用于应用开发的那些软件是免费用于商业用途的,并且没有任何版税、使用或版权限制。

我们要寻找的是最高质量的未压缩 PCM (波或。wav 音频文件格式)样本,使用 16 位或更好的(24 位或 32 位)格式,并且希望有 44.1 kHz 或 48 kHz 的采样频率。

请注意,如果您下载并使用. MP3 文件(大多数也提供),它们将已经被压缩,并可以使用,但您将无法控制压缩和优化过程,因为许多原始数据在压缩过程中已经被丢弃。

数字音频压缩:关键概念和格式

首先你需要通过点击任务栏上的快速启动图标来启动 Audacity 2.0 ,并使用文件image打开菜单命令序列来打开我们从免费音频样本网站下载的 battle003.wav 文件。

第一次打开 Wave 文件时,你会看到如图图 13-2 所示的警告对话框。选择制作副本单选按钮选项,选中不再警告复选框,最后点击确定按钮将音频样本加载到 Audacity 中。

9781430257462_Fig13-02.jpg

图 13-2。导入音频文件的警告和适当的复制设置

使用这些音频文件导入设置(使用文件的副本,而不是实际文件本身)被称为无损音频编辑,是数字音频编辑和效果行业的常见做法。

这样做的原因是,如果您在音频加甜和特效应用中出错,并损坏了音频数据,您可以通过返回并加载原始音频数据来从头开始。

一旦 battle003.wav 样本数据被加载到 Audacity 2 中,您将会看到一个与图 13-3 中显示的屏幕一模一样的屏幕。左上角包含音频传输控制,包括暂停、播放、停止、后退、前进和录制。紧挨着它的是编辑工具,最右边是电平指示器,当您的音频正在播放时,它会显示绿色、黄色和红色信号峰值指示器。下面是扬声器和麦克风音量的设置,以及系统音频设置选择器下拉菜单。在 Audacity 窗口的底部,您会发现以 Hz 为单位的项目采样率,以及用于选择开始、结束或长度的小时、分钟、秒和毫秒显示,以及用于微调的音频位置。

9781430257462_Fig13-03.jpg

图 13-3。 Audacity 2.0 主编辑屏幕显示了 battle003 的 32 位浮点 11 kHz 样本数据

我们首先要做的是,在开始导出到我们所学的各种数字音频格式之前,确保我们的采样分辨率和采样速率设置正确,这样我们就可以看到每个音频编解码器可以为我们的 Android 项目提供的实际压缩结果。

在 Audacity 中设置采样率和采样分辨率

在位于音频样本可视显示左侧的蓝灰色控制面板中,您会看到 battle003 样本(文件)名称,以及旁边一个朝下的箭头。点击此箭头,下拉一个选项菜单,选择设置样本格式选项,然后从其子菜单中选择 16 位 PCM 选项,如图图 13-4 所示。这确保了我们导出到 Android 支持的 16 位样本分辨率。

9781430257462_Fig13-04.jpg

图 13-4。在导出为各种格式之前,将音频样本分辨率设置为 16 位 PCM(未压缩)

现在我们可以导出我们的基线 16 位未压缩 Wave 音频。wav 文件格式,用于查看 2.5 秒炸弹爆炸的 16 位 11.025 kHz 音频样本的最大文件大小。

它的文件大小应该与我们在 Audacity 2 软件中打开(导入)的原始文件非常相似,但我们将把它命名为 blast.wav ,因为这是我们将为这个音频素材使用的更简单的名称(在我们的 Android Java 代码中,我们将在下一章中编写)。

导出未压缩的 PCM 基线。WAV 格式文件

为了在 Audacity 中导出文件,我们将使用文件image导出菜单序列来打开导出文件对话框,如图 13-5 中的所示。这个对话框有几个关键区域,包括保存在:文件夹说明符,它指向我们的音频资源文件夹,文件列表窗格,它显示我们的原始 battle003.wav 文件,文件名:数据输入字段,我们将在其中命名我们的文件,在这个例子中是 blast ,在它下面是保存类型:下拉选择器,它包含所有注意我们在中设置了导出 blast.wav 的对话框。wav 16 位 PCM 格式,这就是为什么我们不需要指定的原因。文件名的一部分。

9781430257462_Fig13-05.jpg

图 13-5。导出我们的基准 16 位 PCM。wav 格式文件,并在导出时显示编辑元数据对话框

如果您单击显示在导出文件对话框右下角的选项按钮,您将看到对于 Wave 音频文件格式,您将得到一个对话框,通知您没有 PCM 格式的编码选项。

仔细想想,这是合乎逻辑的,因为 PCM 波是一种未压缩的音频格式,因此数据根本没有编码,因此没有音频编码参数或选项。

点击保存按钮后,会出现另一个编辑元数据对话框,如图 13-5 右图所示。该对话框包含艺术家姓名、曲目标题、专辑标题、曲目编号、年份、流派和注释的文本值数据字段。

因为我在这里进行了优化,以尽可能减少数据占用,而且我们的应用不需要音频元数据,所以我现在将这些字段留空,这样我们就可以准确读取音频数据的压缩情况。

如果你想知道 Android 是否能够读取和支持元数据,即使我们将这些数据放入我们的音频文件,答案是响亮的是的!事实上,Android 甚至有一个MediaMetadataRetriever类,开发者可以准确地利用它来实现这个特定的目的。

如果出于某种原因,您的音频应用需要利用元数据,您可以使用“编辑元数据”对话框,每当您在 Audacity 中保存任何类型的音频文件格式时,该对话框都会显示出来,此外还有 Android MediaMetadataRetriever 类,您可以通过以下 URL 研究和了解所有相关内容:

http://developer.android.com/reference/android/media/MediaMetadataRetriever.html

如果您查看我们刚刚保存的 blast.wav 16 位 PCM 文件,您会发现文件大小与原始的 battle003.wav 文件相同。

我们从中可以推断出,即使 Audacity 告诉我们这是以 11.025 kHzi 的 32 位采样分辨率编码的,但实际上它实际上是 32 位数据容器(或大量未使用的余量)中的 16 位数据,因为文件大小完全相同,直到最后一个字节。

因此,对于这个 2.5 秒的炸弹爆炸样本,我们的基线未压缩数据足迹是 50380 字节,我们可以使用这个数字来确定使用 Android 支持的所有主要格式将获得的压缩量。

导出无损的。FLAC 开源音频格式文件

我要尝试的第一种格式是 FLAC 音频编解码器,因为它使用无损压缩。这将使我们了解使用不丢弃任何原始音频数据的压缩可以减少什么样的数据占用空间,从而为我们提供与 16 位 PCM Wave audio 一样完美的结果。

再次使用文件image导出菜单序列,这次我们将下拉**另存为类型:**菜单并选择 FLAC 文件格式,如图图 13-6 所示。再次将文件命名为 blast 并放入 Audio 文件夹。

9781430257462_Fig13-06.jpg

图 13-6。导出名为 blast.flac 的 FLAC 音频文件,具有 8 级(最佳)质量和 16 位样本位深度

请注意,在此对话框的中心区域没有列出其他文件;这是因为现在我们已经选择了 FLAC 文件格式类型,该区域仅显示 FLAC 文件,而音频文件夹中目前没有 FLAC 文件。这是模态软件操作的另一个好例子,只是这次是在我们的音频工程软件中,而不是在数字成像软件中。

要设置我们的 FLAC 编解码器选项,请单击选项按钮,并将质量级别设置为 8(最佳),并将位深度设置为 16 位。请注意,在“位深度”下拉列表中,我们也可以使用 FLAC 来获得无损的 24 位高清音频。

一旦你输出了你的 blast.flac 音频素材,进入你的文件管理器,看看文件大小。你会看到是 33537 字节,或者减少三分之一(33537 除以 50380 就是 0.66568 或者三分之二)。

接下来让我们看看另一种开源格式,Vorbis,看看它是否能给我们带来更小的数据占用空间。由于 Ogg Vorbis 是一种有损文件格式,它应该给我们一个比 FLAC 更小的文件。

出口有损 Ogg Vorbis 开源。OGG 格式文件

使用文件image导出工作流程,如前打开导出文件对话框,从**保存类型:**下拉菜单中选择 Ogg Vorbis 文件。将文件命名为: blast (这将产生一个 blast.ogg 文件名)并将其放入 Audio 文件夹中。

点击选项按钮,在 0 到 10 之间选择一个质量设置等级。我使用默认设置 5 开始;在实际的数据占用优化过程中,您将尝试几种设置,以查看数据占用对质量折衷的影响。

9781430257462_Fig13-07.jpg

图 13-7。以 5 级(默认)质量和 16 位采样导出名为 blast.ogg 的 Ogg Vorbis 音频文件

一旦你输出了你的 blast.ogg 音频素材,进入你的文件管理器,看看文件大小。你会看到是 12995 字节,或者减少四分之三(12995 除以 50380 是 0.25794,或者等于四分之一)。这是一个重大的尺寸减少,音频听起来和以前一样(然后再一次,这只是一个基本的爆炸)压缩。

接下来,我们来看看市面上最常见的有损音频格式 MP3。看看 MP3 是否能给我们比 Ogg Vorbis 开源编解码器更小的数据足迹应该是很有趣的。

导出到一个有损 MPEG-3 格式的. MP3 音频文件

再次使用文件image导出工作流程,调出 Audacity 导出文件对话框,将另存为类型:下拉选择器设置为 MP3 文件

将文件命名为 blast 并选择音频文件夹,然后点击 MP3 选项按钮打开指定 MP3 选项对话框,如图 13-8 右侧所示。我使用了默认的质量比特率设置 128 kbps ,这对于音频数据来说是相当高的,以及一个恒定比特率模式设置和一个联合立体声通道模式,因为文件是单声道的。

9781430257462_Fig13-08.jpg

图 13-8。以 128 kbps 恒定速率(比特率模式)编码导出名为 blast.mp3 的 MP3 音频文件

如果您愿意,您可以尝试几种不同的质量位速率设置,以及可变和平均位速率模式,以查看它们如何影响您的音频文件数据占用空间。

如果这样做,只需用文件名中的设置来命名文件。因此,例如,具有 128 kbps 质量设置和可变比特率模式的文件将被命名为 blast128vbr.mp3

通过这种方式,您可以比较您的音频文件大小,并进行简单的数学运算来计算您的数据占用减少百分比,正如我们接下来将对我们的 blast.mp3 (或 blast128cbr.mp3,如果我们遵循我们的命名约定)文件所做的那样,您只需单击 Save 按钮即可生成该文件。

blast.mp3 文件大小为 19,643 字节,表示数据占用空间减少了 61%。为了解决这个问题,19,643 除以 50,380 等于 0.3898 ,这是原始未压缩文件大小的 39%。100%减去 39%等于文件大小减少 61%。

既然我们已经看到我们的. MP3 文件大小并不令人印象深刻,甚至不如 Ogg Vorbis,让我们看看 MPEG4,或 MP4,数据压缩如何改善 MPEG3。因为 MPEG-4 是一种更新更先进的编解码技术,它应该为我们提供更好的文件大小质量比。

导出到有损的 MPEG-4 格式. M4A 音频文件

按照之前的文件image导出工作流程调用 Audacity 导出文件对话框,从保存类型:下拉菜单选择器中选择 M4A (AAC)文件(FFmpeg) 。照常将音频目录中的文件命名为 blast (点击保存后导出器将命名为 blast.m4a),然后点击选项按钮打开指定 AAC 选项对话框,如图图 13-9 所示。我选择将质量设置提升到 500,看看当使用最高质量级别设置时,MPEG4 文件的大小是多少。单击“保存”按钮,立即导出 blast.m4a 文件。

9781430257462_Fig13-09.jpg

图 13-9。导出名为 blast.m4a 的 MP4 AAC 音频文件,最高质量设置为 500

blast.m4a 文件大小为 10,513 字节,表示数据占用空间减少了 79% 。为了解决这个问题,10,513 除以 50,380 等于 0.2087 ,这是原始未压缩文件大小的 21%。100%减去 21%等于文件大小减少 79%。

现在,我们已经看到我们的 M4A AAC 文件大小是迄今为止最令人印象深刻的,让我们看看更专业的 AMR-NB 窄带数据压缩编解码器是否会比 MPEG4 AAC 进一步改善数据占用空间。

尽管 MPEG-4 AMR-NB 编解码器和格式是专门为语音应用而设计和优化的,但可能还有一些其他应用,如某些短脉冲声音效果,可能会从该编解码器获得良好的效果。

毕竟,任何编解码器都只是一个作为软件实现的复杂的数学方程,并不区分,所以真正找出答案的唯一方法是通过编解码器运行原始的未压缩音频数据,看看会发生什么。让我们接下来这样做;然后我们将完成对 Android 中支持的音频编解码器和 Audacity 中提供的音频编解码器的比较。

导出窄带 MPEG-4 格式。AMR 音频文件

按照文件image导出工作流程调用 Audacity 导出文件对话框,从**保存类型:**下拉菜单选择器中选择 AMR(窄带)文件(FFmpeg)

像往常一样,我们将音频目录下的文件命名为 blast (点击保存按钮后,导出器会将其命名为 blast.amr),然后点击选项按钮,打开 AMR-NB 导出设置对话框,如图图 13-10 所示。

9781430257462_Fig13-10.jpg

图 13-10。导出名为 blast.3gp 的 AMR(窄带)音频文件,最大比特率设置为 12.20 kbps

我选择使用 12.20 kbps 比特率设置,以获得这种编解码器和数据格式可能的最高质量结果。这个工作过程让我可以看到当使用最大质量级别设置时,AMR-NB 文件的大小。

让我们点击保存按钮,现在导出我们的 blast.amr 音频文件。如您所见,这是我们迄今为止获得的最小数据足迹,然而当我们回放它时,它听起来仍然非常像炸弹爆炸。

blast.amr 文件大小为 3,686 字节,表示数据占用空间减少了 93% 。为了解决这个问题,3686 除以 50380 等于 0.0732 ,这是原始未压缩文件大小的 7%。100%减去 7%等于文件大小减少 93%。

我们现在唯一的问题,幸运的是只有最后一个非常合适的编解码器和文件格式,是 Audacity 想要输出 AMR-NB 文件。AMR 文件扩展名,而 Android 希望看到使用. 3GP 文件扩展名的 AMR-NB 音频文件。总是有事,不是吗?让我们希望在未来版本的 Android 操作系统中,也许是 5.0,Android 决定友好地接受。amr 文件扩展名!

所以我们回到绘图板,看看 Audacity 文档。

Audacity 手动文件导出对话框部分告诉我们,我们应该能够指定某些非标准的文件扩展名,并且能够成功,所以让我们回到 Audacity 并尝试一下!

按照通常的文件image导出工作流程调用 Audacity 导出文件对话框,并从**保存类型:**下拉菜单选择器中选择 AMR(窄带)文件(FFmpeg)

这次,我们将音频目录中的文件 blast.3gp 命名,然后点击选项按钮,在 AMR-NB 导出设置对话框中设置 12.20 kbps 质量选项,如图图 13-10 所示。

点击保存按钮后,您将看到如图图 13-11 所示的警告对话框。点击按钮,将文件保存为 blast.3gp,我们将很快确定(在下一章)blast.3gp 文件是否会在我们的 Android 应用中播放。

9781430257462_Fig13-11.jpg

图 13-11。Audacity on 文件中显示的警告对话框image导出文件扩展名为. 3GP 的 blast AMR-NB 文件

接下来,我们将把这些优化的数字音频素材放入我们的 Android 项目资源文件夹层次结构中的适当素材位置,以便我们可以在下一章的 Java 代码中访问它们。

在 Android 中使用数字音频资源:项目资源原始文件夹

现在,让我们打开操作系统的文件管理软件,将目前已经优化的六种数字音频文件格式复制到 Android Hello_World 项目文件夹的 /res/raw 子文件夹中。

进入您的数字音频素材制作文件夹,在我的情况下,它在我的一个四核工作站上,在 MindTaffyDesign 文件夹中的 Users 文件夹下的一个 Book image Audio 文件夹中,如图 13-12 顶部的所示。

9781430257462_Fig13-12.jpg

图 13-12。选择我们的六个优化的 blast 数字音频文件,并将它们拖到/res/raw 文件夹中

按住 Control 键的同时,点击选择我们优化过的六个文件,拖动到你的 Hello_World Android 项目文件夹中的/res/raw 子文件夹,也如图 13-12 所示。

一旦这些都在/res/raw 文件夹中,如图图 13-13 所示,我们可以重命名它们,这样当我们在 Java 代码中引用 blast 时,Android 就知道使用哪个了。

9781430257462_Fig13-13.jpg

图 13-13。我们的 blast 文件显示在/res/raw 文件夹中

如果我们在启动 Eclipse 之前不重命名这些文件,我们将在 Eclipse ADT 中位于主编辑窗格左下方的 Problems 选项卡中看到许多错误。

如果您想实际查看这些错误是什么,只是为了体验,那么现在就启动 Eclipse。请确保在重命名所有这些数字音频素材文件之前启动 Eclipse,否则您将看不到这些错误。如果你选择问题标签,你会看到十几个左右的 blast 错误,用红色文本标出。Eclipse 中的错误消息总是很难看!

这些错误将告诉您已经有一个 blast 素材,因此 Android 将把它找到的第一个 blast 数字音频素材作为要实现的素材,然后为其他素材生成错误。

这些错误将指定 Android 已经有一个 blast 音频素材可以利用,因此您必须删除以主文件名 blast 开头的任何其他音频素材,这是我们接下来要做的。

现在,让我们进入您的操作系统文件管理实用程序,我们将重命名所有这些音频素材,以便只有一个 blast.flac 可以被 Android 操作系统看到,我们将在下一章的代码中开始使用它。

图 13-14 显示了这个过程,正如你所看到的,我们已经将 blast.3gp 重命名为 blast_3gp.3gp,将 blast.m4a 重命名为 blast_m4a.m4a,将 blast.ogg 重命名为 blast_ogg.ogg,将 blast.wav 重命名为 blast_wav.wav,最后将 blast.mp3 重命名为 blast_mp3.mp3,依此类推,这是根据我们在 Android 中避免这些错误的数字音频素材占位符命名惯例。

9781430257462_Fig13-14.jpg

图 13-14。重命名 blast 数字音频文件及其编解码器类型,以便只有一个名为 blast 的文件

稍后,当我们想要在 Android 中实现任何给定的数字音频文件格式时,我们会将 blast.flac 重命名为 blast_flac.flac,然后将我们想要使用的编解码器重命名为 blast_m4a.m4a,这样 Android 就可以看到音频素材 blast 的 MPEG4 AAC 编解码器版本。最后,记住在文件名中只使用小写字母和数字;这是 Android 中需要遵守的文件名约定

现在让我们启动 Eclipse,或者如果你已经启动它来查看所有那些有趣的错误,那么使用右键单击项目文件夹并刷新工作流程,这样我们可以看到我们现在有了一个干净、无错误的 IDE 软件开发工作环境,如图图 13-15 所示。

9781430257462_Fig13-15.jpg

图 13-15。我们的 blast 数字音频素材显示在 Eclipse ADT IDE 内的/res/raw 文件夹中

您现在可以看到,我们所有优化的(在 Android 之外压缩的)音频和视频资源都在一起,位于/res/raw 文件夹中,左侧显示的是我们首先要访问的 blast.flac 文件,该文件突出显示。

播放数字音频:Android MediaPlayer 类

使用 Androidmedia playerclass 在你的应用中回放 Android 中的数字音频素材。毫不奇怪,MediaPlayer 类是 Android 的 media 包的一部分,因此它将使用熟悉的 importAndroid . media . media player代码语句导入。

如您所知,MediaPlayer 类是 java.lang.Object 类的一个子类,因为它是自己的专用类,用于播放音频和视频新媒体资源,因此它没有专用的超类,它是从 java 中的 Object 类(将所有内容都定义为对象)派生出来的。

这是因为 MediaPlayer 在被实例化时,是在 Java 代码的一个对象中,你很快就会在第十四章中看到。我们还将在第十五章中看到一个更加强大的 Android 音频排序类,称为 SoundPool 类,因此在接下来的几章中有很多音频素材编码要学习,这应该是一个爆炸!

摘要

在这一章中,我们了解了数字音频的概念、技术、流行的编解码器以及 Android 操作系统对它们的支持。

我们从模拟音频和声波的基础知识开始,我们了解了核心音频概念频率振幅,以及音调和波形以及噪声在音频采样中的作用。

接下来,我们了解了数字音频样本和采样过程,以及数字音频样本回放质量的核心决定因素,即以位为单位确定的样本分辨率(就像图像和视频一样),使用的主要样本分辨率为: 8 位(低质量,用于语音或声音效果)、 12 位(中等质量)、 16 位(高质量)和 24 位(高清质量)。很少需要 32 位采样分辨率。

我们还了解了采样频率,或者需要多少时间片来定义一个给定的模拟波形,以将其转换为数字数据。

常见的采样频率包括 8 kHz、11 kHz、22 kHz、32 kHz、44.1 kHz、48 kHz、96 kHz、192 kHz 和 384 kHz。我们了解到,对于我们的 Android 开发,我们应该使用合理的质量范围,因此使用 11 kHz(中等质量)和 48 kHz(高清质量)之间的采样率。

接下来,我们学习了如何使用 Audacity 2 优化我们的数字音频炸弹爆炸素材(在我们确保正确安装了最新版本的软件及其插件之后)。我们看了一下 Android 支持的所有主要数字音频格式,也在 Audacity 文件导出对话框**另存为类型:**下拉菜单中,幸运的是,这几乎是所有的格式。

我们将我们的基线 PCM 未压缩 Wave 音频导出到所有这些主要的 Android 支持的音频格式和编解码器,我们进行了数学计算,以找出哪些给了我们最小的数据足迹,同时仍然再现了我们正在寻找的音频效果。我们发现我们的无损 FLAC 格式使我们的数据占用空间减少了 33%,质量没有任何损失,而有损编解码器使我们的数据开销减少了 61%到 93%。

然后,我们将我们创建的六个优化文件复制到我们的 Android 项目中的 /raw 文件夹,并重命名这些文件,以便只有一个 blast.flac 音频素材对 Eclipse 可见,这样我们的 Eclipse ADT IDE 中就不会有任何重复音频素材错误

最后,我们看了一下 Android MediaPlayer 类,我们将在接下来的几章中使用它来回放我们的数字音频素材。我们知道 MediaPlayer 在Android . media . media player包中,是 java.lang.Object master 类的子类。在 Android MediaPlayer 的下一章中,我们将学习如何用 Java 代码实现所有这些数字音频素材,这些代码目前存在于你的 Hello_World Android 应用中。那将是非常令人兴奋的!

十四、在 Android 中播放音频:MediaPlayer

在本章中,我们将采用在前一章中优化的数字音频素材,并教您如何在 Hello _ World AttackPlanet.java 活动事件处理代码中实现这些素材以供回放。

我们将在攻击一个星球用户界面的 ImageButton 元素中添加音效,这样当用户点击这些动画按钮时,数字音频将会播放,增加了我们在之前的图形设计和动画章节中已经创建的令人印象深刻的视觉效果。

现在你知道了如何优化 Android 中的音频,我们还将使用一些来自世界知名的声音设计师和音乐作曲家弗兰克·塞拉芬的高质量音频,他是我的一个朋友,他很慷慨地给了我一些音频样本供我们在本书中使用。

这些声音效果、背景音乐和周围环境声音将允许我们极大地增强我们的其他活动屏幕,再次将我们的用户界面和用户体验带到一个全新的水平。

我们还将了解数字音频回放中的更多概念,这些概念与我们在数字视频以及帧和矢量动画章节中观察到的概念非常相似,例如无缝音频循环,在这种情况下,您无法判断背景音频循环(seam)在音频回放循环中的位置。

由于我们已经在上一章中讲述了 Android MediaPlayer 类的基本起源,我们现在将继续讲述这个高级类中可用的各种 Java 方法。我们将在本章的第一部分做这件事,然后我们将开始写一些实现这些核心 MediaPlayer 功能的代码。一旦我们做到这一点,我们将能够给我们的一些用户界面元素和我们的活动屏幕一些非常酷的数字音频功能,这要感谢 Serafine 先生。

Android 媒体播放器:方法和状态引擎

在 Android 中,MediaPlayer 类可以被称为状态机,也就是说,一个数字文件回放实体,它在任何给定时间都具有某些状态。我已经大方地在本节中包含了来自developer.android.com网站的状态机图(参见图 14-1 ),以便您可以在此处参考,以及本节中详细讨论它的文本。您可以在开发者网站上找到它:

http://developer.android.com/reference/android/media/MediaPlayer.html

9781430257462_Fig14-01.jpg

图 14-1。MediaPlayer 状态机示意图(来自【developer.android.com】的的 MediaPlayer 类页面)1

当我们回顾我们的 Java 编程原则时,我们在本书最早的一章中学习了状态。您的 Android MediaPlayer 将始终处于以下状态之一:空闲初始化(加载您的音频或视频数据)、准备准备开始(播放)、暂停完成(播放已完成)、以及停止

还有结束错误 MediaPlayer 状态。让我们先覆盖这两个特殊的状态,并把它们去掉。一旦 MediaPlayer 对象从最初需要使用它的内存中释放出来,就会发生 End,而 Error 则用于媒体播放过程出错时。

当 Java 方法**出现时,MediaPlayer 结束状态出现。释放()**被称为。因此,如果我们的 MediaPlayer 对象被命名为 bombPlayer ,在本章的下一节中,我们的 bombButton ImageButton 对象也将被命名为,您可以使用以下代码语句从内存中释放 MediaPlayer:

bombPlayer.release( );

这将从主内存中清除名为 bombPlayer 的 MediaPlayer 对象,以便该内存可用于其他目的。

OnErrorListener 接口时,出现 MediaPlayer 错误状态。onError( ) 方法通过下面的 Java 回调构造公共抽象布尔 onError (MediaPlayer mp,int what,int extra) 调用

.

mp 参数包含 MediaPlayer 对象的名称,例如 bombPlayer, what 参数包含类型的错误常数,而 extra 参数包含错误特定代码常数

哪些常量包括 MEDIA_ERROR_UNKNOWN 和 MEDIA_ERROR_SERVER_DIED,而额外的常量可以包括 MEDIA_ERROR_IO、MEDIA_ERROR_MALFORMED、MEDIA_ERROR_TIMED_OUT 和 MEDIA_ERROR_UNSUPPORTED。

关于 OnErrorListener 公共静态接口的信息位于:

developer.android.com/reference/android/media/MediaPlayer.OnErrorListener.html

现在让我们从 MediaPlayer 状态引擎图的顶部开始,如图 14-1 所示,一次一个状态地沿着状态树逻辑图向下,从空闲状态和它的 reset( ) Java 方法开始。

如果你用**。reset( )** 方法通过一个bombplayer . reset();代码行,MediaPlayer 对象重置自身(模拟其初始启动)并进入空闲状态或模式。空闲后的下一个模式是初始化的状态或模式;这是通过使用实现的。setDataSource( ) 方法来设置数据源引用到您想要使用的媒体文件。

对于 /res/raw 文件夹中的数据资源,应该使用**。onCreate( )** 方法(我们将在下一节中实现),并使用格式为 R.raw.filename 的第二个参数指定数据文件资源路径。

准备状态是从外部服务器访问数据文件时使用的,与外部服务器 URL 结合使用,由熟悉的 Uri.parse( ) 方法解析。尽管我们在应用中使用了内部媒体数据素材,但我将在这里向您展示这样做的代码,以完成我对这个 MediaPlayer 状态图的介绍。

你会用一个**。setOnPreparedListener( )** 调用 **new OnPreparedListener(),**的 Java 代码块使用 onPrepared( ) 方法,如下所示:

MediaPlayer mp = MediaPlayer.create(this,Uri.parse("http://www.url.com/file.mp3"));
     player.setOnPreparedListener(new OnPreparedListener() {
          @Override
          public void onPrepared(MediaPlayer mp) {
               mp.start();
          }
     });

一旦媒体播放器达到准备好的状态,通过使用**。onCreate( )** 方法或类似于上面指定的代码块,你可以接着调用**。start( )** 方法将 MediaPlayer 状态设置为 Started 状态(或者 mode,如果您更愿意以模态的方式来看的话)。

请注意,这些状态类似于我们在本书前面讨论的模式,除了我们讨论的模式,在使用 GIMP 2 时需要同时考虑多个数字成像模式。

图中的下一层是暂停停止状态,这取决于是否。pause( ) 方法是否被调用或者**。调用 stop( )** 方法。正如你在状态图中看到的,一旦 MediaPlayer 对象被暂停,你就可以使用**。start( )** 方法重新开始你的媒体播放。

当您的媒体文件完成播放时,将达到(或设置)播放完成状态。如果循环标志被设置为(其默认值),那么 OnCompletionListener 将调用 onCompletion( ) 方法(如果您在其中定义了代码),然后这些任务将被执行。

注意,一旦你的 MediaPlayer 达到初始化停止状态,那一个**。prepareAsync( )** 方法可用于将您的 MediaPlayer 对象置于准备状态。

这种准备状态通常发生在从远程位置获取(传输)媒体数据文件时,通常是某种媒体服务器,如亚马逊 S3 或 Akamai 或您自己的自定义数据服务器。

请注意,如果您打算将应用功能集中在 MediaPlayer 类上,而不仅仅是使用它来播放音频或视频资源,那么您应该查看本节开头提到的 MediaPlayer 开发人员页面。

使用 MediaPlayer 类来创建您自己的高级媒体播放器超出了像本书这样的 Android 入门书籍的范围,因为 Android 中的 Media Player 功能非常复杂,足以编写一本关于它的书籍。

Android 中还有另一个数字音频排序 类,称为 SoundPool 类,如果你的应用使用大量声音,并且这些声音需要实时混合和匹配,就像你在游戏或动画电子书或类似的强大新媒体应用中所做的那样,它实际上可能更适合使用。

这个 Android SoundPool 类将在下一章中介绍,所以请放心,您将在本书的这一部分中接触到 Android 中的关键、主要数字音频类和工作流程。

设置 MediaPlayer 对象并加载数字音频数据

在实现我们的 MediaPlayer 对象时,我们需要做的第一件事是在 AttackPlanet.java 活动子类的顶部声明它,在一行代码中将它声明为一个私有访问对象,并通过下面的代码将其设置为一个空值(直到我们稍后使用它):

private MediaPlayer bombPlayer = null;

您会注意到 Eclipse ADT red 在 MediaPlayer 下面用波浪线给代码加下划线。这是因为我们还没有在代码顶部导入它的库。将鼠标放在红色波浪下划线上,选择Import Android . media . media player包引用链接,让 Eclipse 为我们编写这段代码。

这个导入语句和 MediaPlayer 对象声明可以在图 14-2 中看到。

9781430257462_Fig14-02.jpg

图 14-2。声明一个名为 bombPlayer 的 MediaPlayer 对象,并将其值设置为空

接下来,我们需要编写一个方法来设置我们的 MediaPlayer 对象,以便在我们的攻击星球用户界面 ImageButton 图标中使用。我们将这样做,以便在单击每个按钮时播放不同的声音效果。

编写我们自定义的 setAudioPlayers( ) Java 方法

让我们调用这个定制的 Java 方法**setaudiopayers()**并添加我们的第一个 bombPlayer MediaPlayer 对象,它回放我们的 blast 音频素材。

首先,让我们使用下面的 Java 方法调用语句,将调用我们的新方法的代码行添加到 AttackPlanet 活动的顶部,就在 setContentView()方法之后:

setAudioPlayers();

接下来,我们需要编写 setAudioPlayers()方法本身,我们将在编辑屏幕的最底部完成,就在所有其他 ImageView、ImageButton、Animation 和 AnimationDrawawble 对象代码之后。

我们将 setAudioPlayers()方法声明为 private ,因为它仅由该类用来为我们的数字音频样本设置所有 MediaPlayer 对象,并且声明为 void ,因为它在被调用时不返回任何值。

在两个花括号内,我们使用添加了 MediaPlayer 对象创建代码。MediaPlayer 类的 create( ) 方法。此方法需要当前上下文,可以写成如下形式:

AttackPlanet.this

或者,您也可以利用 getApplicationContext( ) 方法,我们将在这里实现它,这样您也可以看到这个方法的使用。所需的第二个参数。create()方法是素材文件引用,我们知道它是 R.raw.blast ,因此我们创建 bombPlayer MediaPlayer 对象并加载数字音频数据的代码行应该编写如下:

bombPlayer = MediaPlayer.create(getApplicationContext( ), R.raw.blast);

当我们添加其他音效数字音频素材时,我们在这个方法中为每个素材添加类似的代码行。这样,当我们的 Activity 启动时调用这个方法,MediaPlayer 对象将被预先创建,并准备好供使用。

然后,在我们单独的 UI 按钮代码中,我们所要做的就是在最终用户点击相应的按钮时,从我们的事件处理代码中调用每个 MediaPlayer 对象(数字音频样本)。新的方法调用和方法体及其第一个 MediaPlayer 对象已经编码,如图图 14-3 所示。

9781430257462_Fig14-03.jpg

图 14-3。创建一个 setAudioPlayers()方法来设置和创建用于声音效果的 MediaPlayer 对象

接下来,我们将添加启动 MediaPlayer 对象所需的编程逻辑。该 Java 代码存在于每个 ImageButton 对象的每个 onClick()事件处理程序例程中。接下来,让我们为 bombButton ImageButton 对象编写代码,这样您就可以知道它的基本格式是如何编码的,以及使用什么方法。

使用我们的媒体播放器对象:使用。start()方法

在 Eclipse 中点击 +图标(在左边,bombButton 对象的旁边)并展开该 Java 代码块(如果它还没有展开以供查看)。在这个代码块中,您有 onClick()事件处理方法,它包含您的 Toast 对象,很快我们将添加当按钮被单击时触发音频示例的代码。

在你的 Toast.makeText()方法调用的下面添加一行代码(如果你愿意,也可以在它的上面),引用我们在 setAudioPlayers()方法中创建的 bombPlayer MediaPlayer 对象,它显示在图 14-4 的最底部。使用 Java 点标记法将。start()方法调用 bombPlayer MediaPlayer 对象,方法是使用下面一行 Java 代码:

bombPlayer.start();

9781430257462_Fig14-04.jpg

图 14-4。添加 bombPlayer MediaPlayer 对象 start()方法调用 bombButton onClick()事件处理方法

这个开始,或者更准确的说,播放,你的爆破数字音频样本,在你的中引用。在 setAudioPlayers( ) 方法中调用 create( ) 方法。

现在我们已经创建了 MediaPlayer 对象,它包含了我们的 blast 数字音频示例,我们已经将它连接到音频数据文件,该文件在上一章中放在 Hello_World 项目的/res/raw 文件夹中,最后在我们的 onClick()事件处理方法中触发音频示例以播放我们想要播放的音频音效的炸弹按钮。

我们已经导入了 Android MediaPlayer 库和类,编写了一个自定义的 setaudiopayers()Java 方法,将 MediaPlayer 对象连接到我们的数字音频示例,并使用了六行 Java 代码触发它进行回放。

现在,我们要为其他三个声音效果做的就是将它们添加到我们的 setAudioPlayers()方法中,然后在我们的 onClick()处理程序方法中使用 start()方法,在我们的每个 UI 按钮被单击时开始音频播放。

编码其他特效音频媒体播放器对象

复制private media player bombPlayer = null;位于 AttackPlanet 类顶部的一行代码,将它粘贴到自己下面三次。将您的 MediaPlayer 对象名称从 bombPlayer 分别更改为 transportPlayervirusPlayerlaserPlayer ,如图 14-5 顶部所示。

9781430257462_Fig14-05.jpg

图 14-5。完成我们的 setAudioPlayers()方法并声明我们的其他音效 MediaPlayer 对象

接下来,从您的 setAudioPlayers( ) 方法的第一行复制 bombPlayer 对象和 MediaPlayer.create( ) 方法调用,然后再将它粘贴到自身下面三次。再次将对象名称从 bombPlayer 分别更改为 transportPlayer、virusPlayer 和 laserPlayer。

现在我们准备添加一个 MediaPlayer 对象**。start( )** 方法在每个 ImageButton onClick()事件处理代码块中调用,如图图 14-6 所示。

9781430257462_Fig14-06.jpg

图 14-6。添加 MediaPlayer。start()方法调用我们的其他三个 ImageButton UI 元素

接下来,我们需要看看如何将无缝循环音频添加到我们的主屏幕 MainActivity.java 活动子类中。在这种情况下,我们可以简单地编写一个 setStartUpScreenAudio()方法来完成活动启动时的所有工作,比如声明一个对象、设置循环参数和启动。

循环播放我们主要活动的背景环境音频

在 MainActivity 类的顶部添加一行代码,在 onCreate( ) 方法中,这样您的音频 MediaPlayer 对象就与您的 World 对象值、屏幕文本和屏幕动画一起初始化了。

让我们调用我们的新方法 setStartUpScreenAudio( ) ,注意当您键入 setStartUpScreenAudio()时;Eclipse 用红色波浪线给它加下划线的代码行。将鼠标放在红色下划线的文本上,Eclipse 助手对话框就会弹出,并提供为您编写新方法的功能。选择显示的三个链接中的最后一个,创建方法 setStartUpScreenAudio()和 voila instant 方法(参见图 14-7 )!

9781430257462_Fig14-07.jpg

图 14-7。为我们的 MainActivity.java 主屏幕创建 setStartUpAudio()方法来播放音频循环

接下来,我们将用三个语句填充 setStartUpScreenAudio()方法的内部。第一个将实例化、命名和。创建( )我们新的 audioPlayer MediaPlayer 对象,第二个将使用新对象来调用**。setLooping( )** 方法设置为 true(循环)状态,第三个方法将调用循环音频以使用。start()方法,我们也将在新的 audioPlayer 对象中调用它。这三行新的 Java 代码如下所示:

MediaPlayer audioPlayer=MediaPlayer.create(getApplicationContext(), R.raw.ambient);
audioPlayer.setLooping(true);
audioPlayer.start();

Eclipse IDE 内部的最终代码可以在下面的图 14-8 中看到。请注意,我们将。在 media player 被实例化之后,将 Looping()方法设置为 true 的值,但是在之前的它是通过。开始()。

9781430257462_Fig14-08.jpg

图 14-8。编码 audioPlayer MediaPlayer 对象和。setLooping()和。setStartUpScreenAudio()中的 start()方法

接下来,我们需要添加一个很酷的外星人的声音到我们的添加一个行星活动中,这样当用户点击火星来创建它时,就可以得到一些音频反馈。我们通过使用一个流行的开源语音合成软件包 eSpeak 来完成这项工作,然后将它与 Audacity 结合使用,在不到 60KB 的数据中创建一个陌生短语。

为我们的 NewPlanet.java 创建语音合成添加一个星球活动

让我们去 SourceForge 下载 eSpeak 语音合成器,或 TTS(文本到语音)技术软件。它位于以下 URL:

[`espeak.sourceforge.net/`](http://espeak.sourceforge.net/)

一旦你下载完操作系统所需的版本,安装软件,然后启动它,这样我们就可以创建我们的外星人画外音了。

在 eSpeak 对话框的顶部中间区域,输入您想要语音合成器朗读或合成的文本。我们希望我们的外星人说“火星创造了”,所以在文本字段区域输入(不带引号),如图 14-9 中用蓝色突出显示的所示。

9781430257462_Fig14-09.jpg

图 14-9。运行 eSpeak 开源应用生成外星画外音

要测试你的外星声音,使用 Speak 按钮,它位于 eSpeak 对话框的右上角,第二个按钮。如果您想将当前的语音合成声音字体更改为不同的声音,请使用文本输入区正下方的声音下拉菜单。您可以使用此菜单来选择和测试不同的声音字体,方法是更改声音字体,然后使用“朗读”按钮来测试每种声音字体。

同样,如果你想微调你的声音,在 eSpeak 对话框的底部中间有滑块,用于调节语速音量(振幅)。

现在我们已经微调了所有设置,让我们生成一个数字音频数据文件。在 eSpeak 对话框的最底部,有一个下拉选择器,我们将使用它来选择我们的目标音频采样速率和目标音频采样频率。

我选择了可能的最高质量, 48kHz 16 位立体声,这样我们就可以进入 Audacity,看看在全 16 位分辨率和 48kHz 专业级采样率下使用立体声(双声道)音频可以减少什么样的数据占用空间。

最后,我们需要点击保存到。wav 按钮,以 48 kHz 未压缩的 16 位 PCM 波形样本格式保存我们的 alien voiceover 音频样本。

当保存对话框出现时,导航到我们一直用于音频资源的同一个音频文件夹,并用文件名 mars.wav 保存文件。接下来,我们将使用我们的 Audacity 音频编辑和优化软件,将样本大小从近 300KB 降低到不到 60KB,同时仍然保持我们应用的最高质量。

使用 Audacity 2.0 优化我们的 Alien Voiceover 音频样本

现在让我们启动 Audacity,使用文件image打开菜单序列找到并打开我们的 mars.wav 数字音频文件,这是我们之前使用 eSpeak 创建的。在图 14-10 中可以看到,有两个音频样本。

9781430257462_Fig14-10.jpg

图 14-10。在 Audacity 中打开我们的合成语音样本来优化 MPEG-4 AAC 数据文件

之所以有两个样本,是因为这是一个立体声音频样本,这意味着我们现在有左声道右声道音频样本。

这也意味着有两倍多的音频数据,因此,如果您可以使用单声道样本,就像我们在音效中使用的一样,您最终将获得更紧凑的数据足迹。

让我们看看在这个外星人画外音上我们能得到什么样的数据足迹优化。正如我们在上一章中所记得的,MPEG-4 AAC 为我们提供了最佳的数据占用减少,即使当我们使用最高(500)质量设置时也是如此,所以让我们在这里使用它,看看会发生什么。

如果您在操作系统文件管理实用程序中查看我们的 mars.wav 未压缩音频示例,您会发现原始数据大小为 293,894 字节的数据,或接近 300 千字节的原始音频数据。

让我们使用 Audacity File image Export 菜单序列打开 Export File 对话框,如图图 14-11 所示,选择下拉到: M4A AAC FFmpeg 编解码器选择。然后点击选项按钮,选择最高质量级别的 500 ,让我们看看这款 MPEG-4 编解码器在压缩专业质量 48 kHz 16 位立体声音频样本方面的效率如何。

9781430257462_Fig14-11.jpg

图 14-11。导出我们的 MPEG-4 AAC mars.m4a 音频数据文件,最高质量设置为 500(59KB)

再次进入文件管理实用程序,查看我们刚刚保存的 mars.m4a 文件。文件大小为 60,638 字节,即 59.2 千字节。这意味着数据占用空间减少了 80%,而质量没有任何损失,这是一个了不起的成绩。

如果你想知道我是怎么算出来的,用 60,638 除以 293,894,你会得到 0.206326,这意味着 60,638 大约是 293,894 的 20%。100%减去 20%会使该音频文件的数据占用空间减少 80%。

我们需要做的下一件事是将这个新的 mars.m4a 音频素材实现到我们的 NewPlanet.java 活动子类的 Java 代码中。

启动 Eclipse,如果它还没有打开的话,在中央编辑窗格中打开 NewPlanet.java 选项卡。在 Activity 类的顶部,添加一行代码,将 MediaPlayer 对象声明为 private ,还将其命名为 marsPlayer ,并将其设置为 null 。该行代码应该如下所示:

private MediaPlayer marsPlayer = null;

接下来,我们需要在 Activity 类的 onCreate( ) 方法中创建 MediaPlayer,以便为 marsPlayer MediaPlayer 对象创建 MediaPlayer 功能。那行代码看起来像这样:

marsPlayer = MediaPlayer.create(this, R.raw.mars);

请注意,在本例中,我使用了 this 来引用当前上下文,只是为了向您展示引用上下文的两种方式,要么使用关键字 this ,要么使用 getApplicationContext( ) 方法。

9781430257462_Fig14-12.jpg

图 14-12。在 onClick() 中创建 Java 代码来实现我们的合成语音说出“行星火星已创建”

接下来,我们将进入 marsImage ImageView 对象的 onClick()事件处理方法,并添加启动 marsPlayer MediaPlayer 对象的代码行。这一行 Java 代码将如下所示:

marsPlayer.start();

现在我们应该准备好使用作为 Android 应用运行工作流程,并在 Android Nexus S 模拟器中测试我们在本章中已经完成的所有音频工作。

模拟器启动后,您应该可以在主屏幕上听到环境背景音频循环。接下来,单击模拟器上的菜单按钮,进入“攻击一个星球”活动,单击每个 ImageButton 图标按钮,聆听每个按钮触发的酷炫音效。

退出攻击行星屏幕,返回主屏幕,再次使用模拟器中的菜单按钮,转到添加新行星活动,并单击行星火星,听我们的外星人画外音说“行星火星已创建”,正如您所见,我们所有的音频素材都已实现!

为我们的“配置一个星球”活动创建按钮点击音频特效

接下来让我们添加一个用户界面按钮反馈音效;因此,在本章中,你会有实现所有不同类型音频的经验,因为所有不同的原因,你会在你的用户界面设计或用户体验设计中使用音频,用于你的 Hello World Android 应用开发或任何其他应用。

让我们首先声明一个私有 MediaPlayer 对象,将其命名为 clickPlayer ,并暂时将其设置为一个 null 值,使用下面一行 Java 代码:

private MediaPlayer clickPlayer = null;

接下来,我们需要使用。使用我们的上下文和数据资源引用创建( )方法,如下所示:

clickPlayer = MediaPlayer.create(getApplicationContext(), R.raw.click);

正如您在图 14-13 中看到的,我们在 ConfigPlanet 活动的最顶端(第一行)创建了 clickPlayer MediaPlayer 对象,并使用。在我们使用 setContentView( )方法设置活动内容视图之后,立即创建()方法。

9781430257462_Fig14-13.jpg

图 14-13。将 clickPlayer MediaPlayer 对象添加到我们的 ConfigPlanet.java 活动并调用。创造()

接下来,我们需要使用下面一行 Java 代码,在每个用户界面按钮对象 onClick()事件处理方法代码块中添加启动 MediaPlayer 对象的代码:

clickPlayer.start();

我们需要为所有七个按钮对象添加这一行 Java 代码,如图 14-14 所示。一旦我们完成了这些,我们就可以在 Nexus S 模拟器中测试我们的 Hello_World Android 应用,然后我们将完成在每个主要活动中实现音频。

9781430257462_Fig14-14.jpg

图 14-14。向按钮 UI 元素 onClick()事件处理方法中添加 clickPlayer.start()方法

这种代码设置比我们在本章中介绍的其他代码设置更加优化,因为我们只需创建一个对象并初始化它,但是我们的七个用户界面元素可以通过事件处理方法中的一小段 Java 代码来利用对象和 MediaPlayer 功能。

使用 Run As Android Application 工作流程启动您的 Android ADT Nexus S 模拟器,然后单击菜单按钮,选择您的 Configure a Planet 菜单选项,并单击每个用户界面按钮以确保它们正常工作。

摘要

在这一章中,我们仔细研究了 Android MediaPlayer 类,它可以用来回放音频样本,为我们的用户界面按钮实现声音效果,以及播放循环环境背景音频或循环背景音乐。

我们仔细查看了 Android 开发人员网站上的 MediaPlayer 状态引擎图,我们从上到下、一个状态一个状态、一种方法一种方法地浏览了一遍,以便更好地了解 MediaPlayer 的确切功能。

我们了解到,在初始化之前,MediaPlayer 处于空闲状态,需要通过使用准备好**。prepareAsync( )** (流)或**。create( )** 方法(受控),在从远程服务器流式传输期间,它将处于准备状态,并且一旦准备好,它可以处于开始停止、暂停状态。

我们看了一下实现了哪些方法接口、回调来控制 MediaPlayer 的各种状态,或者播放/res/raw 文件夹中的强制音频数据文件,或者将音频从某种远程音频媒体服务器流式传输到您的应用中。

然后,我们为我们的 Attack a Planet Activity 子类编写了一个名为 setAudioPlayers( ) 的自定义方法,这样我们就可以为我们的用户界面按钮实现几个短脉冲音频音效。我们这样做是为了当我们的用户点击一个动画按钮时,会有一个动画对象实际声音的音频表示。

然后,我们编写了一个名为 setStartUpScreenAudio( ) 的自定义方法,用于我们的主活动主屏幕,并设置了**。将 Looping( )** 方法设置为 true ,这样我们就可以循环播放环境空间背景音频以获得特殊的音频效果。

接下来,我们利用一个名为 eSpeak 的开源 TTS 技术语音合成器软件包,为我们的“添加行星活动”子类中的 NewPlanet.java 行星火星创建任务创建一个外星人语音。

然后,我们学习了如何使用 eSpeak 来合成和微调我们的外星人声音样本,然后我们优化了使用 Audacity 创建的音频数据。最后,我们在 Add a Planet 活动中编写了实现这个很酷的外星人画外音所需的 Java 代码。

最后,我们在 Configure a Planet Activity 用户界面中添加了一个点击按钮声音效果,以便在用户点击数据输入按钮时提供音频反馈。在这个例子中,我们利用一个 MediaPlayer 对象为七个用户界面元素提供数字音频效果。

因此,我们在 Hello World Android 应用中添加了音效、点击、背景环境音频和合成外星人画外音。我认为这很好地涵盖了数字音频的使用范围。

在下一章,我们将看看一个更高级的音频播放类,叫做 SoundPool ,它可以被用作音频序列器。音频排序和实时混合相当于我们在本书前面的数字成像、位图(基于帧)和程序矢量动画以及数字视频章节中了解的合成。

Android SoundPool 可以实时存储、触发和混合大量的音频样本(不尽然,因为音频并不太重)。这个音频类用于更高级的应用,例如游戏。它还可以用于其他高级音频应用,这些应用可能需要对大量样本进行更精细的管理,而不必为每个样本创建一个 Android MediaPlayer 对象,正如我们所看到的,如果有大量数字音频样本,这可能会变得难以处理。

1 本页部分内容转载自 Android 开源项目创建和共享的作品,并根据知识共享 2.5 归属许可中描述的条款使用。

http://developer.android.com/reference/android/media/MediaPlayer.html