安卓应用开发学习手册(二)
五、Android 意图和事件:增加交互性
在这一章中, 我们将使用我们在前三章中创建的 Hello World Android 应用,并添加更多的 Java 代码来开始使它具有交互性和可用性的过程。
现在我们已经创建了 Android 应用项目(在第二章),定义了我们的WorldGen对象结构(在第三章),创建了我们的五个用户界面布局容器、四个活动屏幕和一个应用菜单结构(第四章),是时候将所有这些连接在一起,以便用户可以使用主菜单在应用的功能屏幕中导航,以体验当他们单击一些(占位符)用户界面元素时的实际结果。
同时,我们也将利用这一章来教授 Android 编程中两个非常重要的概念:意图和事件。这些甚至以一种奇怪的方式押韵!意图和事件都允许应用内部的通信,这就是它们允许实现交互性的原因。
Intents 允许在 Android 应用内进行更全局层次的通信,允许 Android 应用的组件,例如我们在上一章中创建的 functional Activity 子类,在最终用户需要时被调用和访问(通过 Java 代码)。
事件允许在 Android 应用中进行更本地化的通信,因为它们使应用的最底层组件,通常是用户界面元素,或小部件,能够与您的 Java 和 XML 编程逻辑对话,或更准确地说是回调。
因为你已经是一个程序员了,所以你可能很熟悉事件处理的概念。在本章的后半部分,我们将详细介绍 Java 和 Android 是如何做到这一点的,以及 Android 支持哪些事件,以及它们是如何被处理的。我们开始吧!
Android 高层通信:Android 意图对象
Android Intent实际上是一个对象,所以包 android.content 中的 Android Intent类是 java.lang.Object 类的子类。它在 Android Content ProviderAndroid . Content包中,因为 Intents 可以用来快速访问数据库记录,这一点我们将在本书后面了解。一个Intent对象可以(通常是)非常简单,然而,它也可以很复杂,因为一个Intent对象可以有多达七个不同的部分或组件。我们在本书的结尾(在第十六章中)更详细地介绍了意图,但现阶段需要在我们的应用中使用它们,所以我们将在本章中以先驱的水平介绍它们。
Intent被恰当地命名,因为它实际上是一个描述需要完成的事情的对象,并从应用的一个组件发送到另一个组件。这类似于现实生活中传达个人执行或完成某事的意图。
Android 应用可以处理Intent对象的主要领域包括活动,我们已经创建了这些活动;服务,用于后台处理,我们将在后面介绍;和广播接收器,用于向 Android 终端用户发送消息。
Android 的这三个不同的功能领域都有自己的Intent对象,该对象针对这些领域的独特属性进行了优化。毕竟,活动被优化为显示屏幕界面、用于执行繁重后台处理的服务以及用于广播通信消息的广播接收器,因此这些应用组件中的每一个都必须具有不同的意图对象处理结构,以便彼此最佳且无缝地工作。
那么这些Intent对象在应用组件之间传递什么类型的信息呢?意图对象的功能区域包括:
- 需要处理意图的组件(类)的组件名
- 需要执行的动作
- 动作需要操作的数据
- 正在处理的数据的类型(MIME 类型)
- 该处理所属的类别
- 进一步定义需要执行的处理所需的任何标志和附加标志
组件名称
Intent对象中最重要的字段是组件名,每个Intent对象的实例变量包含一个引用到完整的路径到意图对象是目标的类。包含组件名的意图被称为显式意图,而不而指定组件名的意图被称为隐式意图。
类名的完整路径包括包名和类名,如果这个类在另一个包中的话。所以如果你要从另一个包中启动我们的NewPlanet Activity 类,你可以使用 chapter.two.hello_world。NewPlanet.class 作为类的完整路径名。如果您在 chapter.two.hello_world 包中(在本章后面的 Intent 示例中,我们就是这样),您将使用 NewPlanet.class 作为对我们的NewPlanet.java类代码的运行时引用。
动作
意图对象中下一个最重要的变量是动作,Android OS 中有许多预定义的动作常量,可以通过意图对象访问。如果你想全部复习,去 http://developer.android.com/reference/android/content/Intent.html 。
Android 操作系统的 Activity 类常用动作的一些例子包括:
ACTION_DIAL(显示要拨打的电话号码)ACTION_CALL(打电话)ACTION_MAIN(启动应用的主活动)ACTION_EDIT(编辑数据库)
使用广播接收器的一些常用动作包括:
ACTION_TIMEZONE_CHANGED(用户进入了一个新的时区)ACTION_POWER_CONNECTED(用户已插入设备)ACTION_SHUTDOWN(用户关闭了 Android 设备)
数据参考
Intent 中下一个最重要的引用是数据引用,它指向动作要操作的数据。每一个动作都需要一些操作(一些使用或改变的数据值),每一个数据值都需要一些指定的动作(对数据做什么)。因此,意图动作和数据是携手并进的,数据通常使用一种叫做 URI 或通用资源标识符的东西来指定。
类型参考
Intent 中下一个最重要的引用是类型引用,它引用了一个 MIME 类型,这是一个在互联网 1.0 (HTTP)和 2.0(移动或消费电子)平台中使用的标准数据分类。
MIME 最初代表多用途互联网邮件扩展,最初创建它是为了定义电子邮件服务器支持的电子邮件(附件)数据类型,但 MIME 已经被许多不同类型的数据服务器采用,现在,甚至在客户端,也被 Android 操作系统本身采用。
这里需要注意的是,如果意向对象参数分类的七个区域中的任何一个被遗漏,Android 将推断什么应该在那个参数槽中。对于一个被省略的 MIME 类型来说,这可能是最容易的,因为这比 MP3 音频数据文件的 MIME 类型应该是:内容类型:音频/mp3 或者 MP4 视频数据文件的正确 MIME 类型应该是:内容类型:视频/mp4 更容易推断。
Android 类别常量
意图对象中下一个最重要的引用是 Android 类别常量,用于定义意图指向的 Android OS 的特定区域。一些更受欢迎的类别常量包括:
- 类别 _ 默认
- 类别 _ 可浏览
- 类别 _ 标签页
- 类别 _ 启动器
- 类别信息
- 分类 _ 首页
- 类别 _ 偏好
- 类别 _ 汽车 _ 码头
- 类别 _ 桌面 _ 码头
- 类别 _ 汽车 _ 模式
- 类别 _ 应用 _ 市场
类别常量都在 http://developer.android.com/reference/android/content/Intent.html 列出并描述(通过链接)。
举例来说,如何将类别与意图动作一起使用:为了启动用户的 Android 设备主屏幕,您将利用预先加载了 ACTION_MAIN 动作常量以及 CATEGORY_HOME 类别常量的意图对象。
旗帜和额外物品
可以添加到更复杂的意图对象结构中的最后两种引用类型是 标志和附加。正如您从编程经验中了解到的,标志是布尔值,就像选项开关一样,可以通过 Intent 对象传递。 Extras 是需要的其他类型的数据,如文本或其他对象结构。
因为我们不会在本书中讨论高级意图,所以我们不会在我们的意图对象中使用标志和额外功能,但我想让您知道,它们包含在意图对象层次中,如果您的高级意图对象设计需要它们,它们是可用的。
隐式意图:定义隐式意图处理的意图过滤器
正如我前面提到的,隐式意图是Intent对象,它不指定意图对象本身内的任何组件说明符,也就是说,意图对象不指定(不引导意图)它需要去哪里得到处理或处理到它的分辨率。
在这种情况下,Android 操作系统必须通过使用其他Intent对象参数来推断它需要将Intent对象传递到操作系统中的什么区域(或者更准确地说,当前正在系统内存中执行什么代码),以便成功处理该意图或解析。
Android 基于对所有各种动作、数据、MIME 类型以及实际上在Intent对象中定义的类别的仔细比较来执行这个推理过程,使用系统内存中当前可用的代码组件来处理这个意图。
您还可以使用意图过滤器创建自己的推理引擎,这可以使用应用 AndroidManifest.xml 文件中的 XML 标签来定义。您可以在 developer.android.com/reference/android/content/IntentFilter.html 找到更多关于意图过滤器的详细信息。
设计处理隐式意图和利用意图过滤器 XML 的 Android 应用超出了学习 Android 应用编程的入门书籍的范围;然而,我们将在这里讨论这个概念,给你一个大概的概念,用它们可以完成什么,在什么情况下你会想要使用隐式意图和意图过滤器。
通过使用 <意图过滤器> 标签,意图过滤器结构在AndroidManifest.xml文件中声明。它们基于意图对象的七个属性中的三个来过滤隐含的意图;意图动作,其数据,可能还有其类别,如果包括且适用的话。
意图过滤器标签可以包含(嵌套)在 < activity > 和****XML 标签内,以及 < service > 和 < receiver > 标签内。这是因为,正如我们在本章前面所了解到的,这三个独特的 Android 领域中的每一个都有不同类型的意图对象:活动、服务和广播接收器。
如果您想查看关于使用 <意图过滤器> 标签实现意图过滤器结构定义的更多详细信息,请访问意图过滤器元素 Android 开发者页面,位于 developer.android.com/guide/topics/manifest/intent-filter-element.html 。
意图过滤器标签结构用于定义需要匹配的Intent对象配置的描述。如果遇到不止一个匹配,它们还允许实现一个优先级属性。
首先测试意图过滤器的动作匹配,然后测试数据匹配**、,最后测试类别**匹配。如果没有指定动作意图过滤器,则根本不会测试意图的动作参数,将测试过程向下移动以测试意图的数据参数。同样,如果没有指定数据意图过滤器,那么只匹配不包含数据的意图。
对于包含数据特征的意图过滤器,数据参数分为四类,包括数据类型 (MIME 类型)数据方案(例如 http://)数据授权(服务器主机和服务器端口,指定为 host:port) 、以及最后的数据路径。
到您的 strings.xml 文件**、的数据路径例如、将被指定为C:/Users/UserName/workspace/Hello _ World/RES/values/strings . XML .**
比如下面的意图过滤数据参数规范content://com . a press . projects:500/project _ files/Android/file _ number _ 2,t 数据方案为 content:// ,数据权限(主机先指定)为 com.apress.projects:500 (端口后指定),,数据路径分别为**/project _ files/Android/file _ number _ 2,**。
下面是一个意图过滤数据定义的简单示例,它(从应用AndroidManifest.xml文件内部)指定视频 ( MPEG4 H.264 格式数据)和音频 ( MPEG3 格式数据)将使用 HTTP 数据方案从互联网**、**远程访问:
<intent-filter>
<data android:mimeType="video/mp4" android:scheme="http" />
<data android:mimeType="audio/mp3" android:scheme="http" />
</intent-filter>
隐式意图和意图过滤器通常用在更高级的应用开发场景中,当应用被设计为由其他开发人员使用或者与其他开发人员事先不知道使用方法的应用一起使用时。在这种情况下,需要构建意图过滤器结构来处理外部开发人员对应用功能集的不可预见的访问,这些开发人员没有接受过如何访问应用数据结构和功能的培训。
在本书中,我们将关注应用开发的明确意图结构,因为我们知道我们将针对哪些应用组件。
使用明确的意图:让我们的 Hello_World 菜单起作用
现在是时候使用明确的意图,通过使我们的应用菜单功能化,将我们的Hello_World Android 应用提升到一个新的水平。
在过去的三章中,我们已经将定制的 Java 程序逻辑编码成六个不同的类,其中五个是我们 100%从头开始创建的。到目前为止,我们已经创建了 Java 代码,允许我们的用户创建新的世界,殖民这些世界,到每个世界旅行,军事化一个世界,使用力场保护一个世界,甚至对另一个世界发动攻击。
我们已经编写了 Java 代码来创建WorldGen对象,并编写了 XML 标签标记来定义屏幕布局,这些布局包含用户界面元素、菜单和按钮,它们可以访问我们 Android 应用的不同功能。我们在前一章中编写的四个新类利用活动将所有这些 UI 元素保存在它们自己的功能屏幕中。
然而,如果没有明确的意图,这些都只是孤立的组件,不能一起使用来做任何有意义的事情。当然,每个人都会自己做一些事情,比如创造新的星球(NewPlanet.java),给一个给定的星球配置殖民地、殖民者、基地和军队(ConfigPlanet.java),旅行到一个星球(TravelPlanet.java,或者攻击一个星球(AttackPlanet.java)。但是为了使应用成为其组成部分的有用总和,我们需要通过我们的主屏幕选项菜单系统访问所有这些功能屏幕模块(活动),这要求我们使用意图对象来调用和启动这些活动。
因此,我们需要做的下一件事是利用显式的Intent对象来使我们的四个主菜单项调用我们已经创建的用于保存应用的这些不同功能区域的Activity子类。现在让我们设置菜单功能,这样我们就可以使用明确的意图来调用我们创建的这些活动。
膨胀我们的菜单
菜单创建涉及到 onCreateOptionsMenu( ) 方法,该方法包含我们的应用选项菜单展开例程,我们已经利用该例程通过展开我们的菜单 XML 定义来创建我们的主应用菜单。
要使这个选项菜单起作用,我们需要向我们的MainActivity.java类添加第二个方法。 onOptionsItemSelected( ) 方法与onCreateOptionsMenu()方法一起使用,它包含了所有允许根据具体情况选择和执行各种菜单选项的代码。这是通过 Java switch 代码构造完成的,你们中的一些程序员可能也称之为 case 语句。
让我们现在编写这段代码,这样我们就可以看到它们是如何协同工作的。首先,使用我们的MainActivity.java类中每个方法左边的小减号来关闭所有这些 Java 方法的代码视图,在编辑窗格中给我们更多的代码编辑空间。
接下来让我们在onCreateOptionsMenu()方法后添加一行空格(通过按回车键),然后按下Ctrl-空格键击键组合,这是我们在前一章中学到的。这将打开方法选择器帮助器对话框,在这里我们可以找到 onOptionsItemSelected( ) 方法,双击它在当前的MainActivity.java类(Activity 子类)内部实现它。
请注意,它有公共访问控制,因此所有代码都可以访问菜单选项,还有一个布尔返回类型,因此可以将处理状态(true 表示已处理,false 表示未处理)返回给调用实体。该方法将一个名为 item 的 MenuItem 对象作为其参数。
在onOptionsItemSelected()方法中,我们将使用一个 switch 语句在五个菜单选项或案例之间进行选择,在switch语句中的每个 case 分支内都有代码,这些代码将在案例出现时执行。switch语句本身需要 MenuItem 对象的 ID 作为它的参数,因为名为 item 的MenuItem对象作为它的参数被传入了onOptionsItemSelected()方法,我们也可以使用**。switch 语句参数区域内名为 item 的 MenuItem 对象上的 getItemId( )** 方法,我们用一行代码对其进行了编码,如下所示:
switch(item.getItemId()) { the case statements will go inside of these brackets }
因此,让我们将它输入到我们的方法中,然后添加我们的第一个 case 分支,使用我们在前一章中为我们的 Add a Planet 菜单项创建的 menu_add ID 参数,如下所示:
case R.id.menu_add:
这些都显示在图 5-1 中,还有下一步,我们添加显式意图对象,该对象调用我们的NewPlanet活动类。
图 5-1。添加一个 onOptionsItemSelected()方法来处理我们的菜单选项并导入意图类
添加新行星意向
在下一行代码中,我们创建了第一个显式 Intent 对象,我们用它来调用我们在上一章中编写的 NewPlanet Activity 子类。这与我们在第三章中声明我们的 WorldGen 对象的方式类似,使用 new 关键字,就像这样:
Intent intent = new Intent(this, NewPlanet.class);
注意图 5-1 中的【Eclipse 用红色错误波浪线给我们的 Intent 对象声明加了下划线,我们现在意识到这是因为 Intent 类没有通过使用 import 语句在我们的代码中正确声明。让我们将鼠标悬停在红色下划线的 Intent 关键字上,当助手对话框弹出时,选择选项从包含 Intent 类的 android.content 包中导入 Intent 。
现在您已经熟悉了这个对象声明过程,您意识到上面的代码行声明了一个名为 intent 的 Intent 对象,并使用 this 的当前上下文用一个 new Intent 对象加载它,然后通过一个 NewPlanet.class 运行时类引用将它指向NewPlanet Activity 类。由于我们稍后将向这个switch结构添加更多的意图对象,我们将把这个意图对象命名为 intent_add ,以便更清楚地反映它是哪个意图对象。
Intent intent_add = new Intent(this, NewPlanet.class);
接下来添加一行调用意图对象的的代码。startActivity( ) 方法,它使用我们刚刚创建的 Intent 对象来启动我们刚刚在前面的代码行中定义的活动子类 NewPlanet.class,并使用代码从当前上下文对象(this)中调用它:
this.startActivity(intent_add);
最后,我们添加一个**break;**语句,这样我们就可以在需要执行的代码处理完之后退出switch case语句的这一部分。
这显示在图 5-2 中,以及我们将在接下来的几段中添加的其余case语句。Java switch 容器中的每个单独的 case 语句都需要像这样构造:
case R.id.menu_add:
Intent intent_add = new Intent(this, NewPlanet.class);
this.startActivity(intent_add);
break;
图 5-2。添加五个发送意图对象的 case 语句,调用我们的 Hello_World 功能活动屏幕
在本章的下一节,我们将在 Java switch 容器中添加菜单选项的 case 语句。
添加其余的意图
接下来,让我们采用程序员的快捷方式,复制并粘贴第一个case语句到它的下面,并进行必要的修改,使它调用我们的ConfigPlanet.java活动子类。将R.id引用更改为 R.id.menu_config ,将意图对象的名称更改为 intent_config ,并将活动类设置为 ConfigPlanet.class ,以便在选择配置行星菜单选项时打开正确的屏幕。
我们一半的 Activity 子类现在都是通过选项菜单中的 Intent 对象来调用的;让我们使用刚才使用的相同的复制和粘贴方法添加另外两个。在第二个 case 语句下复制我们的 switch 代码结构中的第一个或第二个 case 语句,以添加第三个 case 语句,然后将 R.id 引用更改为 R.id.menu_travel ,将第三个 Intent 对象的名称更改为 intent_travel ,并将第三个 Activity 类 call 设置为 TravelPlanet.class ,以便在选择 Travel to Planet 菜单选项时打开 Travel to a Planet 屏幕。
让我们在下一个 case 语句中调用第四个 AttackPlanet 活动子类,通过再次粘贴 case 语句,在第三个 case 语句下创建第四个 case 语句,然后将 R.id 引用更改为指向 R.id.menu_attack ,并将第四个也是最后一个意图对象的名称更改为 intent_attack ,同时将第四个活动类 call 设置为 AttackPlanet.class ,这样,当选择 Attack on Planet 菜单选项时,我们将打开我们的应用攻击行星功能屏幕。
最后,在每个switch或case语句的底部,我们可以有一个偶发事件语句,它处理一些代码,这些代码只有在 switch 构造中没有 case 语句与执行这些 case 语句的任何参数相匹配时才被执行。
在 Android switch 语句中,这被称为默认 case 语句,在这种情况下,它通过一行代码将控制权返回给超类,该代码如下所示:
default: return super.onOptionsItemSelected(item);
这本质上与为整个switch语句返回一个 false 值是一样的,并将控制权传递回超类的onOptionsItemSelected()方法。让我们右键单击我们的项目文件夹,然后作为 Android 应用运行,并运行我们的代码,看看它是否工作。菜单项现在调用我们在前一章中设计的每个活动类和布局容器(查看图 4-16)。
启用我们的新星球活动的用户界面:事件处理
既然我们可以从我们的MainActivity类选项菜单中启动我们的 Activity 子类,我们需要能够使用这些 Activity 子类来执行它们各自的功能,然后将用户返回到我们的MainActivity主屏幕。这是通过 android.app.Activity 类(或我们的子类) finish( ) 方法完成的,该方法在我们使用完一个活动后关闭它。
让我们编写部分NewPlanet.java类(Activity 子类),向您展示如何实现 finish( ) 方法,以及如何使用 onClick( ) 事件处理方法来处理对我们的一个新行星图像(或者现在,对我们的一个占位符图像)的点击。
首先,我们将在创建类时创建的onCreate()和setContentView()方法之后添加一行代码,并创建一个 ImageView 对象,用于引用我们之前在 activity_add.xml 文件中定义的 imageMars UI 元素。这是通过声明一个名为 marsImage 的 ImageView 对象,并使用引用 XML android:id 参数的 findViewById( ) 方法将其引用到 XML UI 元素定义来实现的,如下所示:
ImageView marsImage = (ImageView)findViewById(R.id.imageMars);
请注意,当我们将这一行代码输入 Eclipse 时,它会在这个 ImageView 类引用下面加红色下划线,因为我们还没有导入它以供使用。让我们将鼠标悬停在这个突出显示的代码元素上,选择**Import ImageView(android.widgetpackage)**初始选项,并让 Eclipse ADT 为我们编写导入代码语句,如图图 5-3 所示。
图 5-3。在 NewPlanet.java 创建一个名为 marsImage 的 ImageView 对象,它引用了 XML UI 定义
既然我们已经定义了在 Java 中使用的 marsImage UI 对象,我们想通过使用点符号为它附加一个方法,允许它处理用户的点击(或者触摸,如果使用了触摸屏设备的话)。这是使用完成的。来自View类的 setOnClickListener( ) 方法。
让我们使用新的关键字和setOnClickListener()方法为我们的marsImage ImageView UI 对象设置一个OnClickListener()方法,如下所示:
marsImage.setOnClickListener(new View.OnClickListener() { code goes in here } );
这一行简洁的代码所做的是在视图类中声明一个新的 OnClickListener( ) 方法,该方法位于一个 setOnClickListener( ) 方法内,该方法附加到一个 marsImage ImageView 对象,我们在前面的一行代码中创建了该对象。
注意,Eclipse 再次给我们的 Java 代码加了红色下划线,因为我们使用的方法来自一个视图类,该视图类还没有导入到我们的NewPlanet.java代码中。现在让我们将鼠标悬停在代码中的视图类引用上,以获得如图图 5-4 所示的帮助器对话框,该对话框将为我们导入视图 ( android.view包),这样我们就不必自己编写导入代码了。
图 5-4。将 setOnClickListener 方法添加到 marsImage ImageView 对象并创建新的 OnClickListener( )
一旦我们的导入 Android . view . view;语句已经为我们准备好了,Eclipse 会重新评估我们的代码,并且只在 OnClickListener( ) 方法下面加上红色下划线。
这个新的错误高亮显示出现是因为,至少现在,这个监听器方法目前是一个空的或者未实现的方法,因为我们还没有在两个花括号内添加任何事件处理代码。
让我们借此机会让 Eclipse 为我们多写一些代码,再次将鼠标悬停在红色下划线的代码上,并选择添加未实现的方法选项,如图图 5-5 所示。瞧啊。Eclipse 编写了这个方法:
@Override
public void onClick(View v) {
//Add Code To Process Here
}
图 5-5。实现继承的抽象方法视图。OnClickListener.onClick(View)通过 Eclipse 助手
接下来,我们在这个 onClick( ) 方法中添加代码来处理我们想要发生的事情(当用户单击一个marsImage ImageView UI 元素时),这个方法被传递了一个需要处理的视图对象(名为v)。
因为这是NewPlanet类,我们希望在火星图像上单击(或触摸)来创建一个新的 WorldGen 对象,该对象配置有火星的属性。这是通过调用我们的 WorldGen( ) 构造函数方法来完成的,就像我们之前对 MainActivity.java 类中的 earth 对象所做的那样。
我们在 onClick( ) 事件处理程序中的第一行代码创建了一个名为火星的新WorldGen行星对象,然后通过传递给WorldGen()构造函数的参数配置其名称、质量和重力:
WorldGen mars = new WorldGen("Mars", 642, 3.7);
注意,一旦我们输入这个新的对象创建代码行,Eclipse 黄色会在 mars 对象下面加下划线,因为它还没有被使用。我们可以忽略这个警告,如图 5-6 中的所示,但是为了清晰的代码编辑视图,我们将添加一个殖民地到火星,以满足 Eclipse。
图 5-6。添加 WorldGen()构造函数方法调用 onClick(View)事件处理程序方法创建 mars 对象
为了给火星添加一个殖民地,我们将调用来自火星对象的.setPlanetColonies(1)方法,我们只是使用如下的点符号实例化:
mars.setPlanetColonists(1);
接下来,我们需要告诉NewPlanet Activity 我们已经使用完了,并希望返回到我们应用的主 Activity 主屏幕。这是使用 finish( ) 方法完成的,这是我们在onClick()事件处理方法中的下一行代码,如这里和图 5-7 所示:
finish();
图 5-7。通话。对 mars 对象的 setPlanetColonies()方法使用本地 mars 变量;调用 finish()意向方法
现在,让我们右键单击Hello_World项目文件夹,并选择作为 Android 应用,它会保存我们所有的代码更改(如果我们没有通过 CTRL-S 保存它们的话),并启动 Nexus S 模拟器,使用我们新的意图和事件处理代码运行我们的Hello_World应用。
当你点击模拟器菜单按钮时,屏幕再次看起来如图 4-16 所示。接下来,单击添加新行星菜单项,Intent 将引用并打开新行星活动屏幕及其添加行星布局,这是我们在布局文件夹的 activity_add.xml 文件中创建的。
现在,让我们看看我们的事件处理代码是否工作正常。单击右上角的(火星)图像占位符图标,onClick()事件处理程序创建一个新的火星星球和一个殖民地,然后通过我们的onClick()方法代码语句末尾的finish()方法调用,将您返回到应用的主屏幕。
接下来,让我们将事件处理添加到我们的其他应用功能活动屏幕,以便我们的应用选项菜单结构可用于在应用的各个区域之间导航。这样,当我们完成本书的第一部分(第一部分)时,我们已经有了应用的基本结构。
启用我们的 ConfigPlanet 活动用户界面:事件处理
接下来,让我们准备我们的 ConfigPlanet 活动和 UI 布局,以便在用户完成当前星球的配置后将他们返回到主屏幕。我们通过向 activity_config.xml 添加一个Done configuration按钮和向 strings.xml 文件添加一个匹配的 button_name_done 字符串常量来实现这一点。然后,我们将使用 Java 代码连接这个 XML Done 按钮,允许用户在单击 Done 按钮时返回到 Hello_World 主屏幕。我不会在这一部分使用很多屏幕截图,因为你之前已经看到了工作流程,所以不需要它们。
在你的 Eclipse 编辑标签的右上方的中央窗格中有一个数字(在图 5-7 中是一个 10);单击此处可下拉包含所有打开的编辑选项卡的菜单。选择 activity_config.xml 编辑选项卡,这样我们可以添加完成配置按钮 xml 标记。
将这个 XML 布局定义中的最后一个按钮复制并粘贴到它自己的下面,并将 android:id 标记更改为 doneButton 并将 android:text 标记字符串常量引用更改为 button_name_done 并按 Ctrl-S 保存 XML 文件。然后再次点击“选项卡未显示数字”下拉菜单,选择 strings.xml 编辑选项卡,通过复制粘贴 button _ name _ ffoff string 标签,添加新的button _ name _ Donestring 标签以及Done configuration的值,然后更改其名称和数据值。
既然我们的 XML 标记编辑已经完成,我们可以转向在ConfigActivity.java类中编辑 Java 代码了。这里我们需要添加一个按钮对象,在其上附加我们对完成按钮的onClick()事件处理,就像我们在上一节中对 ImageView UI 元素所做的那样。
让我们经历一个与我们在NewPlanet类中所做的类似的工作过程,并添加一个名为 doneButton 的按钮对象,它引用我们刚刚编辑完的 XML 文件中的 doneButton ID。我们通过使用 findViewById( ) 方法,使用一行代码来实现这一点,如下所示:
Button doneButton = (Button)findViewById(R.id.doneButton);
接下来,我们将使用这个刚刚创建的 doneButton 按钮 UI 对象,并通过熟悉的将一个 OnClickListener( ) 方法附加到这个 UI 对象。setOnClickListener( ) 方法创建一个新的OnClickListener如下:
doneButton.setOnClickListener(new OnClickListener() { our code goes in here } );
我们用于将用户返回到主屏幕(MainActivity)的 Done Configuring 按钮的 Java 事件处理代码再次涉及到在 onClick( ) 事件处理程序中使用 Activity 类的 finish( ) 方法,如图 5-8 中我们修改过的ConfigPlanet类所示。
@Override
public void onClick(View v) {
finish();
}
图 5-8。添加一个。OnClickListener()方法添加到 ConfigPlanet.java 类中的 doneButton 按钮对象
接下来,我们将修改我们的TravelPlanet.java类,这样就可以从主选项菜单中调用它,并在稍后将用户返回到主屏幕。
启用我们的 TravelPlanet 活动用户界面:事件处理
正如我们为 ConfigPlanet 类所做的那样,现在让我们准备我们的 TravelPlanet 活动和 UI 布局,以便当用户完成当前活动星球的旅行时,将他们返回到他们的主屏幕。
我们通过向我们的 activity_travel.xml 添加一个 Return Home 按钮,并向我们的 strings.xml 文件添加一个匹配的 button_name_return 字符串常量来实现这一点。然后,我们将使用一些 Java 事件处理代码连接 XML 返回按钮 UI 元素,每当用户单击返回按钮时,这些代码允许用户返回到他们的 Hello_World 主屏幕。
在你的 Eclipse 编辑标签的右上方的中央窗格是一个数字(在图 5-8 中,是 11);再次单击此按钮,下拉包含所有打开的代码模块的选择菜单。选择 activity_travel.xml 编辑选项卡,这样我们可以添加 Java 事件处理程序代码将要引用的返回主页按钮 xml 标记。
让我们采取一个快捷方式,复制并粘贴我们在您的activity_config.xml布局定义中使用的最后一个按钮,并在我们的 < VideoView > UI 元素标签下使用,这样我们就有一个按钮可以在这个布局容器中使用,如图图 5-9 所示。<按钮> XML 标记应该是这样的:
<Button android:id="@+id/returnButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_name_return" />
图 5-9。在 Eclipse 中向 activity_travel.xml 布局容器 xml 定义添加一个返回主页按钮标签
接下来,让我们将 android:id 标签更改为 returnButton ,将 android:text 标签字符串常量引用更改为 button_name_return ,然后按 Ctrl-S 保存我们修改后的 XML 布局容器定义。
再次单击 tabs-not-displayed-number 下拉菜单,这一次,选择 strings.xml 编辑选项卡,这样我们可以将字符串常量添加到我们的新按钮 UI 对象引用中。添加新的 button_name_return 字符串标签,以及 return Home 的值,方法是再次复制并粘贴button_name_done字符串标签,然后将值从 done 更改为 Return,并从 Done 配置更改为 Return Home。
现在我们的 XML 标记编辑已经完成,我们可以转向在TravelActivity.java类中的 Java 代码编辑,在这里我们需要添加另一个按钮对象,将我们下一个 onClick( ) 返回主页按钮的事件处理方法附加到该对象,就像我们在上一节中在ConfigActivity.java类中所做的一样。
让我们完成一个与 ConfigPlanet 类类似的工作过程,并添加一个名为 returnButton 的按钮对象,它将引用我们刚刚编辑完的 XML 文件中的 returnButton ID。我们通过使用一个 findViewById( ) 方法来实现这一点,使用一行代码,如下所示:
Button returnButton = (Button)findViewById(R.id.returnButton);
接下来使用我们刚刚创建的 returnButton 按钮 UI 对象,并通过熟悉的将一个 OnClickListener( ) 方法附加到这个 UI 对象。setOnClickListener( ) 方法创建一个新的 OnClickListener,如下所示:
returnButton.setOnClickListener(new OnClickListener() { your code goes in here } );
我们用于将用户返回到主屏幕 (MainActivity)的 Done Configuring 按钮的 Java 事件处理代码再次涉及在 onClick( ) 事件处理程序内使用 Activity 类的 finish( ) 方法,如下面的 Java 代码所示:
@Override
public void onClick(View v) {
finish();
}
图 5-10 中的显示了我们修改后的TravelPlanet类。
图 5-10。将 returnButton 按钮 UI 对象和事件处理代码添加到我们的 TravelPlanet.java 类
我们越来越近了!一旦我们修改了我们的AttackPlanet.java类以返回到我们的应用的主屏幕,我们的整个 Hello_World 用户界面导航基础设施将拥有它的高级导航,允许我们的最终用户通过我们的主选项菜单在我们的应用的功能活动屏幕之间移动(没有双关语),然后从每个应用屏幕返回到主应用主屏幕。
启用我们的攻击星球活动用户界面:事件处理
就像我们为 TravelPlanet 类所做的一样,现在让我们准备我们的 AttackPlanet 活动和 UI 布局,这样我们就可以在用户完成了对其他星球的有趣攻击后将他们返回到他们的应用主屏幕。
我们通过向我们的 activity_attack.xml 布局规范添加一个退出攻击模式 ImageButton UI 小部件来实现这一点。请注意,因为我们使用的是 ImageButton UI 元素,所以我们而不是需要向我们的strings.xml文件添加一个 button_name_exit 字符串常量。稍后,我们将使用事件处理程序代码连接 Exit ImageButton 元素,该代码允许用户在单击 Exit ImageButton 时返回 Hello_World 主屏幕。
在你的 Eclipse 编辑标签的右上方的中央窗格中有一个数字(在图 5-10 中,是 10);再次单击此按钮,下拉包含所有打开的代码模块的选择菜单。选择 activity_attack.xml 编辑选项卡,这样我们可以添加 Java 事件处理程序代码将要引用的退出攻击模式按钮 xml 标记。
让我们走捷径,复制并粘贴我们最初在activity_attack.xml布局定义中定义的最后一个ImageButton标签,并将其用作我们的第五个ImageButton UI 元素,这样我们就有了另一个匹配的图像按钮用于这个布局容器,如图图 5-11 所示。第五个< ImageButton >标签及其参数应该类似于以下 XML 标记:
<ImageButton
android:id="@+id/exitButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/content_desc_exit"
android:src="@drawable/ic_launcher" />
Make sure that you add a <string> tag to your strings.xml file for your Exit button content description parameter that says Exit to Home Planet using the following XML mark-up:
<string name="content_desc_exit">Exit to Home Planet</string>
图 5-11。向我们的 activity_attack.xml 布局标记添加第五个退出攻击模式 ImageButton UI 元素标签
还要注意在图 5-11 的中,我们将 LinearLayout 容器标签的 android:orientation 参数从水平改为垂直。这是为了让我们的五个图像按钮(目前是图标占位符图像,直到我们到达第七章)更容易地在屏幕上上下移动,而不是沿着顶部移动,现在已经有五个了。
现在我们的 XML 标记编辑已经完成,我们可以在AttackActivity.java类中编辑 Java 代码了。这里我们需要添加一个 ImageButton 对象,在其上附加下一个 onClick( ) 事件处理方法,使我们的退出攻击模式按钮具有交互性,就像我们在上一节中在TravelActivity.java类内部所做的一样。
让我们进行一个与我们在ConfigPlanet类中所做的类似的工作过程,并添加一个名为 exitButton 的ImageButton对象,该对象引用我们刚刚编辑完的 XML 文件中的 exitButton ID。我们将使用一个 findViewById( ) 方法,通过一行代码来实现,如下所示:
ImageButton exitButton = (ImageButton)findViewById(R.id.exitButton);
接下来,我们将使用刚刚创建的exitButton ImageButton对象,并通过熟悉的将一个 OnClickListener( ) 方法附加到这个 UI 对象。setOnClickListener( ) 方法创建一个新的 OnClickListener,如下所示:
exitButton.setOnClickListener(new OnClickListener() { the code will go in here } );
我们将用于让用户返回主屏幕(MainActivity)的 Exit Attack Mode 按钮的 Java 事件处理代码涉及到再次使用 Activity 类的 finish( ) 方法,在 onClick( ) 事件处理程序内部,如我们在图 5-12 中修改的AttackPlanet类所示。
图 5-12。将名为 exitButton 的 ImageButton UI 对象添加到 AttackPlanet.java 类来处理事件& finish( )
现在右键单击Hello_World项目文件夹,并选择运行为 Android 应用,这将再次启动我们的 Nexus S 模拟器,这样我们就可以使用新的意图和事件处理代码来测试我们的应用。
当你点击模拟器菜单按钮时,屏幕再次看起来如图 4-16 所示。接下来,点击配置行星菜单项和意向参考,打开我们在activity_config.xml文件中创建的配置行星活动屏幕及其配置行星布局。现在让我们看看我们的事件处理程序代码是否在工作。点击完成配置按钮,onClick()事件处理程序调用finish()方法并返回主屏幕。
接下来,点击 Travel to Planet 菜单项,查看 Intent 是否引用并打开我们在 activity_travel.xml 文件中创建的 TravelPlanet 活动屏幕及其 Travel to Planet 布局。要查看我们的事件处理程序代码是否正常工作,请单击返回主页按钮。那个onClick()事件处理程序也应该调用我们的finish()方法并返回我们的家。
最后,点击攻击行星菜单项,意图引用并打开我们在activity_attack.xml文件中创建的攻击行星活动画面及其攻击行星布局。让我们也测试一下 ImageButton UI 元素的事件处理程序代码是否工作正常。点击左下方的图标退出攻击模式 ImageButton,查看我们的onClick()事件处理程序是否调用了finish()方法。
现在,在 Hello_World 应用的功能屏幕上导航一切正常,让我们来看看事件处理—键**中更重要的一个方面。**因为现在很大比例的 Android 设备都有这样或那样的键,我们接下来将看看 onKey( ) 事件处理方法,这样我们就涵盖了本章中的所有“主要”内容!
小键盘或键盘的事件处理:OnKey 事件处理器
到目前为止,我们已经使用了onClick()事件处理程序来捕获用户触摸我们的活动屏幕 UI 元素,或者使用许多 Android 设备上的导航箭头板和中心选择按钮进行导航。
到目前为止,我们的大部分报道都集中在onClick()处理程序方法上,因为onClick()事件处理程序是 Android 开发中最常用的。这是因为onClick()事件处理程序涵盖了触摸屏的使用,以及任何导航硬件(导航键、轨迹球等)。)使用一个单一的事件处理方法。
随着 GoogleTV 或 Android iTVs 的出现,我们还需要考虑更多的键盘(或小键盘)将可供我们的最终用户使用。市场上也有带滑出式或可连接迷你键盘的智能手机(甚至平板电脑)型号,所以我也将在本章中介绍 onKey( ) 事件处理方法。
让我们为我们的应用添加一个键盘快捷键,使用通用的 X 键退出我们的每个活动屏幕功能区域,表示单词退出。
为此,我们需要在每个 Activity 子类中编写方法,使用 Android onKeyDown( ) 事件处理程序监听被按下的 X 键。按下一个 X 键会让我们的用户回到主屏幕。
onKeyDown()方法接受两个参数,一个整数和一个对象,然后对它们求值。第一个参数包含键码,,它是一个整数值,代表被按下的键的指定数值(常数)。第二个参数是一个名为 Event 的 KeyEvent 对象,它是正在处理的键事件。
onKeyDown()方法是公共的,所以任何东西都可以访问它,而布尔的,当它返回一个真(已处理)或一个假(未处理)返回数据值。
将传入该事件处理程序的键码与字母 X 的 KeyEvent 类常量(键码 _X )进行比较的代码使用了 onKeyDown( ) 事件处理方法中的基本 if 循环。代码如下所示:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_X) {
finish();
return true;
}
return false;
}
让我们将这段代码输入到我们的ConfigActivity.java类中,就在onCreate()方法之后,因为我们希望我们的活动屏幕“捕获”用户在任何时候按下 X 键返回主屏幕。
注意,当我们输入这段代码时,Eclipse 发现我们没有通过 import 语句声明使用的 Android KeyEvent 类。一旦您完成了方法的输入,将鼠标悬停在 KeyEvent 关键字上,让 Eclipse 为我们添加我们的import语句,如图图 5-13 所示。
图 5-13。添加一个 onKeyDown 方法,并通过选择:Import KeyEvent 来删除错误突出显示
Java 代码在onKeyDown()方法中所做的事情是相当基本的,但是我将在这里为那些不熟悉 Java 的人讲述它。我们在 if 循环中使用了一个==数字比较运算符,或者在这种情况下,它更多地被用作 if 语句,将代表用户所按下的键的 keycode 整数与 KeyEvent 类中的KEYCODE_X常量进行比较,以查看用户是否按下了 X。
如果按下了 X 键(如果两个值相等),那么执行 If 结构中的语句,并执行finish();执行代码语句,从被调用的方法返回一个 true 值,表示它已经成功完成了它的事件处理。
如果按下任何其他键(如果两个值不相等),那么这个 If 构造中的语句将被执行而不是,并且该方法将返回一个假值,表示该事件没有被处理。请注意,我们可能创建的任何其他事件处理程序仍然可以对该事件进行操作;事件从一个处理程序到下一个处理程序被称为气泡,直到它被处理(或者如果我们不指定那个键,就永远不会被处理)。
图 5-14 显示了我们完成的 ConfigActivity.java类事件处理代码,我们现在将在其他三个 Hello_World Activity 子类中复制它(如果我们真的聪明的话,可能通过剪切和粘贴)。
**
图 5-14。【ConfigPlanet 类的最终 Java 代码,用于处理我们应用导航的点击和按键事件
我们这样做是为了让用户在错误地进入应用的错误区域时,只需按下 X 键,就可以轻松退出我们的每个应用功能屏幕。您可能知道,键盘快捷键在大多数流行的软件应用中都很常见。
让我们先做我们的代码复制工作流程,在我们利用现在熟悉的 Run As Android Application 功能最后一次测试我们的应用之前,在我们继续攻克本书的用户界面设计和用户体验设计部分之前。
选择完整的onKeyDown()方法,按下 Ctrl-C 键将其复制到你的系统剪贴板,然后点击标签区右边的数字(在图 5-14 中,是 12)下拉活动标签菜单。接下来,选择您的TravelPlanet.java文件,并使用 Ctrl-V 键盘序列将onKeyDown()方法代码块粘贴到 TravelPlanet 类的底部,就在最后一个花括号之前。
我们还需要为我们的NewPlanet.java和AttackPlanet.java类做同样的事情,这样我们应用的所有四个 Activity 子类都实现了我们新的 X 键用于退出键快捷特性。现在复制这个工作流程,以便onKeyDown()事件处理出现在我们所有四个功能屏幕活动子类中。
最后,我们需要测试我们在本章中完整添加的应用导航。右键单击我们的项目文件夹,选择 Run As Android Application 菜单选项,启动 Nexus S emulator,这样我们就可以看到我们实现的所有东西是否都工作正常。
要在应用运行后对其进行测试,请调用选项菜单,并单击四个应用区域中的每一个,然后按下计算机键盘上的 X 键(仿真器使用计算机的键盘来模拟设备键盘),并确保您的应用将最终用户返回到他们的主屏幕。
您会发现,如果您在此时测试onClick()事件处理,这两种类型的事件可以完美地并行处理。我们的应用现在可以添加实际的功能了!
其他事件处理方法:OnFocusChange 和 OnLongClick
还有另外四种主要的 Android 事件处理方法,我们不打算在本章中介绍,但我们将在本书的后面实现它们,因为我们会在应用中添加更复杂的用户界面元素、图形设计、动画以及新的媒体素材和功能。
第一个是 OnLongClick( ) 事件处理方法,它相当于使用鼠标在计算机或其他设备上右键单击的 Android 方法。通过触摸并按住屏幕、轨迹球或任何 Android 设备上的中心选择按钮来调用长点击。
就像 onClick()事件处理程序一样,在 Java 代码中实现了一个onLongClick()事件处理程序。
第二种类型的事件处理程序是一个 OnCreateContextMenu( ) 事件处理程序方法,它创建用于 Android 的上下文菜单。上下文菜单也类似于 PC 操作系统中的菜单,通过右键单击软件的对象或区域来获得该对象专用的上下文相关选项菜单。
第三种类型的事件处理程序是 OnFocusChange( ) 事件处理程序方法,用于处理当用户从一个元素前进到下一个元素时,用户界面元素发出的焦点事件。
当最终用户正在应用屏幕上使用给定的用户界面元素时,该 UI 元素被称为具有焦点,当用户停止使用该 UI 元素,并开始使用另一个 UI 元素时,焦点被称为已经改变。
当焦点从一个 UI 元素转移到另一个元素时,Android OS 会发出一个 FocusChanged 事件,该事件可以被OnFocusChanged()事件处理方法捕获。这对于微调您的应用 UI 控件,以及精确跟踪用户如何访问您的 UI 元素,以及用户如何使用您的应用非常有用。
最后,Android 中还有一个 OnTouch( ) 事件处理程序,它只处理触摸屏事件。我建议对触摸屏设备(以及非触摸屏设备)使用onClick()事件处理程序,用最少的代码覆盖最广泛的 Android 设备。
但是,如果您确定所有用户都将拥有并使用触摸屏 Android 设备,那么您可以使用onTouch()事件处理方法以及onClick()方法,或者代替它们。
OnTouch()事件处理程序也用于只能使用触摸屏的 Android 操作系统功能,最好的例子是一个名为手势的 Android 功能,其实现超出了本书的介绍范围。
摘要
在这一章中,我们学习了 Android 编程语言中两个最强大和最有用的代码结构:意图和事件??。****
可以说,意图和事件允许我们“连接”我们的 Android 应用,以便当我们的最终用户点击我们的用户界面元素时,如我们的选项菜单项、图像按钮、文本按钮或图像视图,我们的应用能够处理这些点击(或触摸),并前往应用需要去的地方(意图调用活动屏幕),并做它需要做的事情(事件调用程序逻辑来实现某些东西)。
首先,我们了解了一个意图对象的结构,以及一个意图对象可以包含的七个主要功能区域(组件、动作、数据、类型、类别、标志、附加功能)。我们了解了为什么这些区域都很重要,为什么需要它们来处理意图对象,以及这些区域在意图处理请求中的作用。
我们了解了 AndroidManifest.xml 文件中的隐式意图和 <意图过滤> 标签,它们实现了这些意图推理引擎,可以由高级开发人员自定义构建。我们了解到意图过滤器将处理动作,然后是数据,然后是类别,以确定开发人员希望他们的意图如何被处理和执行。
然后,我们了解并在 Hello_World 应用中实现了显式意图,以实现我们在前面章节中创建的选项菜单结构,并让我们的菜单调用我们之前创建的四个自定义活动子类,以保存我们的应用功能屏幕。
一旦我们的菜单导航开始工作,我们需要学习事件处理,以便在每个应用功能屏幕中使用我们的用户界面元素,让我们返回到应用的主屏幕。
我们学习了onClick()事件处理程序,以及如何在我们的 Java 代码中实现它来捕获各种 UI 元素(小部件)上的点击事件,以便我们的用户可以调用 Activity finish()方法并返回到主应用活动(主屏幕)。
然后,我们为各种流行的 Android 用户界面小部件编写了onClick()事件处理程序,包括 ImageView 对象、 Button 对象和 ImageButton 对象。通过这样做,我们实现了应用中每个主要功能屏幕(活动)之间的无缝导航。
接下来,我们学习了 onKey()事件处理程序,并设置了 onKeyDown()处理程序,允许我们捕获点击 X 键的用户,这样我们就可以为我们的应用实现一个退出键盘快捷键。在那之后,我们回顾了其他一些 Android 事件处理程序,我们将在本书的其他三个部分中实现它们。
在本书的其余部分,我们将经常利用这些意图和事件能力;我只是觉得我们需要在这本书的第一部分,很早就注意到这些基本的意图和事件概念和原则。你是编码员—你能处理好它。当然,没有双关语的意思。
在本书的第二部分(用户界面设计)中,在我们开始让应用看起来更原始之前,我在前面介绍了这个主题材质,因为这些 Intent 和 Event Java 对象和方法对于让我们的应用在更高级的层次上工作是非常重要的。
在本书的第二部分中,我们将采用我们在本书第一部分中创建的应用基础架构,并开始专注于我们的 Hello_World 应用的用户界面设计和图形设计,增强我们的用户体验(UX)并使 Hello_World 看起来更像一个专业的 Android 应用。
在下一章中,我们将学习 Android UI 设计小部件,并开始为 Hello_World Android 应用中的五个主要屏幕区域微调用户界面设计。然而,在第六章中,我们将主要使用 XML 标记(大部分)来精炼和增强我们的 UI 元素。
此外,在第六章中,我们将探索这些主要 UI 元素选项(作为 XML 参数)中的许多选项,用于微调我们的用户界面“外观”,以及探索我们的 UI 元素在我们的 Android 应用屏幕上的精确像素位置。**
六、Android UI 设计:通过 XML 使用视图和小部件
在本章中,我们将使用我们在本书第一部分中创建的 Hello World 应用,并致力于使其各种功能屏幕的应用用户界面设计更加专业。在 Android 中,这主要是通过 Android 用户界面小部件的 XML UI 参数来完成的,我们已经知道这些小部件是视图的子类。
同时,我们也将利用这一章,让那些不是用户界面设计者的读者领略在努力使用户界面设计看起来专业,同时对最终用户保持易用性时遇到的各种设计考虑。
我将再次尝试同时完成这些目标。极大地增强了 Hello World 应用的用户界面,同时还在一个统一的章节中介绍了关键的 XML UI 参数,该章节将概述在 Hello World 应用的上下文中使用 XML 定义用户界面元素的技术和功能。
Android 用户界面元素:Android 视图和子类
正如我在本书前面提到的,所有用户界面元素,至少是用户用来控制应用的可视元素,都是 Android View 类的子类,这意味着视图本身就是一个对象,也是 java.lang.Object 主对象类的子类。
一些最常见的用户界面小部件对象和布局容器对象,包括 ViewGroup、ImageView 和 TextView,都是 View 类的直接子类。其他我们已经使用过的,比如 Button、VideoView 和 ImageButton,是 View 类的间接子类。
间接子类是子类的简单子类;例如,Android 按钮类,可以通过 android.widget 包找到(导入)(你可以导入 android.widget.Button 以便使用按钮 UI 元素对象)是从 android.widget.TextView 类的子类。这是因为 button 对象具有 TextView 对象所具有的所有功能,以及附加功能(按钮外观和感觉元素),因此对 TextView 对象进行子类化以创建 Button 对象是合乎逻辑的。
我们将在本书后面看到的其他 View direct 子类包括 AnalogClock 和 ProgressBar 然而,我们将使用的许多用户界面小部件位于视图类层次结构的更底层(也就是说,它们是更专门化的用户界面特性实现),因此是 Android 视图类的间接子类。
在这一章中,我们将关注所有 Android 应用开发项目中使用的主流视图类或窗口小部件。我们的应用 UI 小部件将包括直接子类 TextView 和 ImageView ,因为图像和文本是任何应用中最重要和最常用的元素,以及按钮、 ImageButton 、 EditText 和 VideoView UI 元素小部件。
优化我们的 NewPlanet 活动 ImageView UI 小部件
让我们从我们的第一个选项菜单项和活动屏幕开始,在NewPlanet.java类中定义,我们在第三章中写了它。这个 UI 屏幕让我们单击一个行星的图像,在 Hello World Android 应用中创建基本的行星对象。如果你忘了它看起来像什么,这一章会再次展示,还有行星图片,在图 6-2 中。
现在,我们使用新的 Android 应用项目对话框系列在第二章中为我们创建的默认 Android 应用启动图标作为占位符图像。在这一节中,我们将最终用实际的行星图像替换这些占位符图像,稍后我们将使用 XML 参数对它们进行格式化,以使屏幕显示出专业的效果。
我为每个星球创建了四个不同分辨率的版本;一个是 256 像素见方的 ITV,一个是 192 像素见方的平板电脑,一个是 128 像素见方的触摸屏智能手机,一个是 96 像素见方的 240 或 320 像素屏幕的手机。
将我们的图像素材放入可绘制的 DPI 文件夹
接下来,将这些行星图像文件复制到资源文件夹下相应的 drawable 文件夹中。请注意,我们将很快在第七章中探讨开发这些不同分辨率图形素材背后的所有理论。现在,我将简单地介绍一下我们正在做的事情的基础,这样我们就可以在本章的用户界面设计工作中使用 drawables(位图图像)。
让我们从行星地球开始,因为这是我们在第三章中开发的 activity_add.xml 布局容器中的第一个 XML ImageView 标签。使用 Windows 资源管理器文件管理器,将earth256.png文件复制到 Hello_World 项目资源文件夹中的 drawable-xhdpi 文件夹中,该文件夹位于:
C:/Users/YourNameHere/workspace/Hello_World/res/drawable-xhdpi/
XHDPI 代表超高密度像素图像,这个文件夹包含我们最高分辨率的图像素材,在这种情况下,是一张 256 像素的正方形地球图像。接下来将 earth192.png 文件复制到 drawable-hdpi 文件夹,用于高密度像素图像,然后将 earth128.png文件复制到 drawable-mdpi 文件夹,用于中密度像素图像**,最后将 earth96.png文件复制到 drawable-ldpi 文件夹,用于低密度******
接下来,我们将把这四个图像文件分别重命名为earth.png。我们这样做是因为 earth.png 是我们将在 XML 标记和 Java 代码中引用的图像素材名称,Android 会自动查看用户使用的屏幕大小,并进入正确的文件夹,获取分辨率密度图像素材。我们将在第七章中进一步探讨这个概念。
接下来,我们需要复制并重命名其他五个行星图像素材,这样我们就有六个了。四个可绘制文件夹中的 png 文件。四个文件夹中的每一个都应该有六个。png 文件,每个文件都以行星的名称开始,并且全部是小写字符。
接下来,我们必须使用 Eclipse 中的 Refresh 命令来更新我们的项目文件夹,以显示我们从 Eclipse“外部”(使用 Windows Explorer 文件管理器)复制到这些文件夹中的这些新素材。
为此,右键单击您的项目文件夹并从菜单中选择刷新,或者您也可以左键单击该文件夹并按下计算机键盘上的 F5 功能键。这样做的目的是告诉 Eclipse 扫描您所有的项目文件夹和文件,并更新 Package Explorer 导航窗格,以及它自己的跟踪例程,以便这些图像文件在我们的代码中被调用时,不会被标记为从我们的项目素材层次结构中缺失(或不存在)。
参考并对齐我们的行星图像源文件
接下来,我们需要进入我们的activity _ add . xmlrelative layout XML 文件,并更改我们在第四章中写回的 XML 标记,以指向正确的星球文件名,这些文件名在每个 ImageView 标签中使用 android:src 参数引用。更改占位符图片,目前为 @drawable/ic_launcher ,用小写字母表示每个星球的名称。第一个 ImageView 标签 imageEarth 的参数如下:
android:src="@drawable/earth"
为所有六个 ImageView 标签完成此操作后,单击编辑窗格底部的图形布局选项卡,并使用其预览功能查看是否显示了所有六个行星。如果是,那么您已经正确地完成了工作流程中的前几个步骤。请注意,作为一个整体,所有六颗行星都需要向右移动一点。为了实现这一点,我们将添加现在熟悉的Android:layout _ margin left参数。因为我们希望整个布局(作为一个整体的行星组)被移动,我们将把这个 marginLeft 参数放在父 RelativeLayout 标签中,如图 6-1 中阴影线所示。
图 6-1。添加 RelativeLayout 参数将所有行星居中,并将 android:src 参数设置为新图像
使用 22 密度独立像素 (DIP,或 DP)的设置值,通过以下 XML 标记行将新的 Planet selection ImageView PNG 图形居中显示在我们的 NewPlanet Activity 类用户界面屏幕上:
android:layout_marginLeft="22dp"
现在,返回并单击底部的图形布局选项卡,预览您添加的这个新的 android:marginLeft 参数,并查看这个 22 倾角设置如何将行星组推到显示屏的中心,以获得更专业的 UI 屏幕外观。这显示在图 6-2 中,以及右边的 Eclipse 属性窗格,你可以通过将鼠标放在属性窗格的顶部边框上来放大它,当你的光标变成一个双箭头“拖动以调整我的大小”指示器时,这时你可以向上拖动这个边框,给你自己一个更大的属性编辑区域。
图 6-2。activity _ add XML 代码预览,在属性面板中有行星图片和新的边距设置
请注意,您在 XML 标记视图窗格中编码的参数也在属性窗格视图中设置,因此如果您愿意,也可以用这种方式添加参数。蓝色突出显示的是我们刚刚添加的左边距布局参数 22dp 设置。另外,请注意中间的模拟应用用户界面屏幕,以及我们六颗行星的更专业的视图。这项活动进展顺利。
添加屏幕字幕文本和完成按钮
让这个屏幕完全专业的下一件事是在屏幕顶部添加一个 TextView UI 小部件(标签)来告诉用户这个屏幕是干什么的,以及一个按钮,当他们完成添加行星时可以点击它。doneadingbutton按钮 UI 元素(标签)调用我们的 finish() 活动方法,点击 planet ImageView UI 元素生成那些新对象。我们将在本章的后面写我们的 Java 代码;现在我们正在通过 XML 学习 UI 设计,所以让我们添加标签。
首先,让我们添加一个按钮用户界面元素标签,以便我们的用户在添加完行星后可以退出这个屏幕。最简单的方法是进入 activity_config.xml XML 标记,复制完成配置按钮标签,然后将该标记粘贴到 activity_add.xml 文件的顶部,RelativeLayout 容器标签下。
将 android:id 值改为 @+id/doneAddingButton ,将 android:text 值改为**@ string/button _ name _ Done _ add**,然后进入 strings.xml 文件,复制粘贴button _ name _ Done标签,创建一个 button_name_done_add 名为 string 的常量
接下来我们要做的是从 imageEarth ImageView 标签中剪切并粘贴(不是复制)两个标签Android:layout _ alignParentLeft和Android:layout _ alignParentTop,并在我们添加的按钮标签中使用它们。这是因为按钮标签现在是这个修订的相对屏幕布局的最顶层的屏幕布局用户界面元素。
因为我们想在屏幕的右上角右对齐新的 All Done 按钮 UI 元素,所以将Android:layout _ alignParentLeft更改为Android:layout _ alignParentRight并保留 android:alignParentTop。在我们的doneadingbuttonUI 元素中,我们并没有真正使用任何新的参数,我们只是“借用”了左上角 ImageView UI 元素中的参数,并对它们进行了更改,以创建一个右上角的按钮 UI 元素。接下来,我们将为我们的屏幕标题添加一个 TextView UI 元素(见图 6-3 中突出显示的行)。
图 6-3。添加一个文本视图标签来添加一个屏幕标题和一个 doneAddingButton 按钮标签来退出活动
接下来,让我们从我们的 activity_main XML 布局容器中复制第一个 TextView UI 元素(标签),并将其粘贴到我们刚刚创建的按钮标签上方。将 android:id 更改为 @+id/addPlanetText 并将 android:text 参数指向**@ string/Add _ Planet _ caption**,然后进入您的 strings.xml 文件,复制并粘贴hello _ world标签,用它创建一个 add_planet_caption 命名的字符串常量,值为:“点击行星到
通过编辑窗格底部的图形布局编辑器选项卡预览 UI 设计,您会注意到文本有点小,因此返回 XML 编辑模式,并在 TextView 标签中的 android:id 参数之后添加一个设置为 22sp 的 android:textSize 参数,如图图 6-3 所示。
文本字体使用标准像素(sp) 而不是密度无关像素来定义它们的值。如果您尝试将 dp 或 dip 用于任何与字体相关的大小调整,Eclipse 将标记它并警告您使用 sp 值来调整文本大小。
我们需要做的下一件事是将我们的屏幕标题文本与按钮 UI 元素对齐,以便它看起来专业并且对齐,位于屏幕布局的顶部。我们可以用一个名为Android:layout _ align baseline的参数来实现这一点,它将一个 UI 元素与另一个 UI 元素(在本例中是我们的按钮 UI 元素)的基线对齐。
现在让我们实现它,通过使用参数代码设置 alignBaseline 参数指向我们的按钮 UI 元素的 android:id 值:
android:layout_alignBaseline="@+id/doneAddingButton"
这一行代码也显示在图 6-3 中,当我们现在点击编辑窗格底部的图形布局编辑器选项卡时,我们可以看到在 UI 设计中实现了更大的字体大小和基线对齐。
还要注意,在图 6-4 中,Eclipse 实际上向我们展示了基线,它使用这个基线来对齐按钮标签 UI 元素中的文本和屏幕标题 TextView UI 元素。如果你仔细观察,你会看到一条绿线,证明我们的 TextView 和 Button 对象现在是基线对齐的。
图 6-4。查看相对于 doneAddingButton 按钮的 TextView layout_alignBaseline 参数结果
如果没有看到绿色基线,请返回到 XML 编辑模式窗格,并单击 alignBaseline 参数的代码行以突出显示它。
一旦该行代码被突出显示(浅蓝色),当您再次单击该选项卡时,它将是在图形布局编辑器视图中预览的代码。还要注意在图 6-4 中,我在属性窗格中突出显示了我们的文本大小参数,显示它也可以在那里设置。接下来,让我们以 Android 应用的身份运行**,看看我们的 NewPlanet.java 活动屏幕用户界面布局在所有新变化到位后看起来如何。结果可以在图 6-5 中看到。**
图 6-5。Nexus S 模拟器中我们的 NewPlanet 活动画面
添加 Java 代码来整合我们的新用户界面元素
最后,我们需要对我们的NewPlanet.javaJava 代码进行一些添加,以将我们在 XML UI 设计中所做的更改吸收到 Java 代码函数中,为我们的 Hello_World Android 应用添加一个新的 Planet 活动。我们需要做的第一件事是添加一个按钮对象。
在 Java 中设置按钮 UI 对象类似于设置 ImageView 对象来处理 Mars ImageView 对象上的点击。我们使用一行 Java 代码来声明我们的按钮对象,该代码声明了一个按钮对象,将该对象命名为完成按钮,,并通过引用一个完成添加按钮 ID 的 findViewById() 方法将其绑定到我们的 XML 定义,如下所示:
Button doneButton = (Button)findViewById(R.id.doneAddingButton);
接下来,我们利用熟悉的**。setOnClickListener()** 方法来设置一个新视图。OnClickListener() 为 doneButton 按钮对象。这又包含一个 onClick()事件处理程序,它监听点击,然后为我们的活动执行一个 finish() 方法,允许用户退出活动屏幕,并返回到主屏幕。代码如下所示:
doneButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
finish();
}
});
接下来,我们需要从 onClick()事件处理程序方法中移除 finish()方法调用,该方法附加到我们的 marsImage ImageView 对象,因为我们现在有了用于退出活动的 doneButton UI 按钮,并且我们不再希望当我们单击其中一个行星时屏幕消失。
代替 finish()方法,让我们添加一个 Toast 对象和方法调用,让我们的用户知道 Mars 对象实际上已经被创建了。这是引入 Android Toast 对象的逻辑位置,因为它们通常用于用户界面和用户体验的优化和增强。
引入(并利用)Android Toast 类
Android Toast 对象用于将消息发送到您的活动屏幕上,该屏幕会在预定义的时间内向用户发送消息。在我们的例子中,当用户单击 marsImage ImageView UI 元素时,他们将发送消息“Mars Created ”,以便用户知道 Mars 对象是通过用户单击该行星的图像而创建的。
Toast 对象来自于 android.widget.Toast 包和类,并且是 java.lang.Object 的子类(而不是 android.view.view 的子类,因为它不需要任何 View 类特性),因为它们本质上很简单,尽管它们在使用时也非常有效。
声明一个 Toast 对象最简单的方法就是使用**。Toast 对象的 makeToast()** 方法。该方法需要当前上下文、要烘烤的消息以及常量形式的持续时间。有两个持续时间常数, LENGTH_LONG 和 LENGTH_SHORT ,我们将使用 LENGTH_SHORT 常数快速直观地确认我们的火星星球已经创建。Toast 调用编码如下:
Toast.makeText(NewPlanet.this, "Mars Created", Toast.LENGTH_SHORT).show();
注意,我们已经使用点符号将 Toast show() 方法附加到 Toast.makeText()方法调用的末尾。这是 Java 方法链接的一个很好的例子,这种技术允许我们编写更密集的代码,其中一整块代码可以写在一行代码中。要使用两行单独的 Java 代码“手写”编写这一行代码,您应该编写以下代码:
Toast myToast = Toast.makeText(NewPlanet.this, "Mars Created", Toast.LENGTH_SHORT);
myToast.show();
正如你在这里看到的,我们写的第一行代码要简洁得多!
现在添加我们开发的这行代码来将消息显示到屏幕上,代替我们之前的 finish() 方法调用,我们完成的 NewPlanet Java 代码将看起来像图 6-6 中所示的代码。
图 6-6。添加一个按钮对象和一个 Toast 对象。makeText 方法调用我们的 NewPlanet.java 活动
请注意,当我们作为 Android 应用运行来测试我们的代码时,我们现在会看到一个屏幕,上面有一个“全部完成”按钮,当我们单击火星图像时,它会创建一个火星对象,并向我们提供火星对象已经创建的反馈。
稍后我们将完成应用这一区域的 Java 代码,现在我们正专注于我们的用户界面设计,所以让我们继续,接下来微调我们的 ConfigPlanet 活动屏幕的 UI。
优化我们的 ConfigPlanet 活动 UI:按钮和文本 UI 小部件
让我们通过增强我们的 activity_config.xml 文件中的 XML 标记来改进我们的第二个选项菜单项 Configure Planet 的用户界面设计。首先,我们将通过在每行上放置两个标签参数来压缩一些按钮标签 XML 标记,这样我们就可以在屏幕上放置一些新的 UI 元素标签。请注意,标记参数只需用一个空格字符分隔;参数不需要在它们自己的行上。这可以在图 6-7 中看到,在 LinearLayout 中,我们的按钮标签现在只占了十几行标记。为了增强 UI 设计并使屏幕更具功能性,我们需要在 UI 屏幕的右侧放置一些可编辑的文本字段,与每个按钮相对。
图 6-7。添加 EditText UI 元素标签并嵌套两个 LinearLayouts 以创建并排的 UI 布局
使用嵌套布局容器创建复杂的用户界面设计
为此,我们需要将两个 LinearLayout 容器嵌套在一起,一个用于左侧垂直对齐的按钮 UI 标签,另一个用于右侧垂直对齐的 EditText UI 元素标签。让我们复制 LinearLayout 开始标签,粘贴到 LinearLayout 结束标签下面,开始一个新的容器,如图图 6-7 所示。在它下面复制一个 LinearLayout 结束标记,这样我们的结束标记也就到位了。
然后复制 LinearLayout 开始标签并将其粘贴到 LinearLayout 开始标签的上方**,以创建一个新的顶级 LinearLayout 容器,并将其 android:orientation 参数从垂直更改为水平,因为两个嵌套的 LinearLayout 容器将彼此相邻。然后再次复制 LinearLayout 结束标记,并将其粘贴到布局容器的最底部,以便第一个水平 LinearLayout 标记包裹两个内部(嵌套)垂直 LinearLayout 容器标记。**
为了设置两个嵌套的 LinearLayouts 之间的宽度比,我们需要向第一个(左侧)标签添加一个 android:layout_width 参数,以定义它将拥有多大的屏幕( 170dp )。右边的 LinearLayout 负责剩下的部分。
如果你愿意,你可以稍微改变嵌套标签的缩进来显示嵌套的内容,如图 6-7 所示。接下来,我们要做的就是将我们的 EditText UI 元素标签添加到第二个 LinearLayout 的内部。
为可编辑文本字段引入 Android EditText 用户界面元素
要添加 EditText 用户界面元素,我们可以使用图形布局编辑器将 EditText UI 元素拖动到屏幕的右侧,或者通过编写一个元素的标记,然后复制五次,同时更改参数以匹配按钮 ID 和输入类型,来手动编码 EditText 标记及其参数:
<EditText android:id="@+id/editTextColonies" android:inputType="number"
android:ems="12" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_marginTop="12dp" />
第一个 EditText UI 字段在 Add Colonies 按钮的对面,有一个 editTextColonies 的 android:id 和一个 android:inputType 的 number (与整数数据匹配)。inputType 参数定义了字段使用的数据类型,并且需要与访问该字段的 Java 代码中使用的数据类型相匹配,稍后当我们编写 Java 代码来填充这些数据字段时,我们将会看到这一点。我们使用数字和文本数据类型作为 inputType 参数,但是如果需要的话,也可以使用其他类型,比如 **numberDecimal,**表示实数(十进制)。
android:ems 参数为数据字段中使用的文本值设置字体大小,我们将从 12 ems 的值开始,然后如果我们希望数据字段中有更大的文本,稍后再对其进行调整。我们的 EditText UI 元素标记的下一个参数是 android:layout_width ,它被设置为 match_parent ,告诉 android 将 EditText UI 元素(字段)的宽度与父容器对象(标记)匹配,在本例中是第二个嵌套的 LinearLayout。
我们的 EditText 标签中的 android:layout_height 参数有一个 wrap_content 值,它本质上与 match_parent 值相反。wrap content 常量意味着使 UI 元素的边缘符合其中包含的内容,而 match parent 常量则相反:将 UI 元素的边缘扩展到包含它的容器中。
最后,我们将使用 12dp 的一个 android:layout_marginTop 参数将我们的每个 EditText 字段与我们的按钮对齐,这样我们在数据字段之间就有了均匀的间距,并且每个字段都与紧邻其左侧的按钮 UI 元素的底部边缘对齐。
现在我们已经编写了一个代表性的 EditText 标签,让我们将它复制五次,并更改 android:id 参数以匹配按钮 id 名称,并将两个 Forcefield 指示器字段的 android:inputType 参数更改为 text ,这将包含文本值。
升级我们的 Configure Planet Activity screen XML 以在并排的 Linearlayouts 中包含 EditText 数据编辑字段的最终结果可以在图 6-7 中看到,它显示了我们最终的 XML 代码和我们在 Eclipse 右侧的 Outline 窗格中使用的 UI 元素的概述。通过使用作为 Android 应用运行工作流程,检查以确保当前 UI 设置在仿真器屏幕上看起来不错。
添加 Java 代码以整合我们的“配置一个星球”屏幕的新用户界面元素
接下来,让我们修改我们的ConfigPlanet.java活动类 Java 代码,将它“连接”到这些新的 UI 元素,这样它就可以在我们的应用中起作用了。
我们要做的第一件事是为我们的 doneButton Button 对象复制我们的按钮代码,这样活动屏幕上的所有按钮都可以正常工作。注意在图 6-8 中,我通过放置 **finish()使 onClick() 代码块更紧凑一些;**花括号内的语句放在一行上,因为目前只有那一条代码语句。
图 6-8。在 ConfigPlanet.java 的 Java 代码中添加按钮对象和 onClick()事件处理程序
接下来,我们需要将所有与 doneButton 相关的(5)行 Java 代码复制六次到 doneButton 代码块下面,如图图 6-8 所示,所以我们创建了 colonyButton、colonistButton、baseButton、militaryButton、forceFieldOnButton,最后是 forceFieldOffButton 代码块。确保在每个代码块中为每个按钮 UI 元素引用正确的 XML ID 名称,并确保**。setOnClickListener()** 方法连接到正确的按钮对象名,该对象名定义在它的上一行中。最后,点击每个代码块左边顶部的减号图标,折叠代码块,如图 6-9 所示,这样我们就有了更多的编辑空间。
图 6-9。添加一个名为 colonyText 的 EditText 对象,并通过:Import EditText 调用 Eclipse 的自动导入特性
现在是时候将我们的 EditText UI 对象添加到我们的 Java 代码中了,这样我们就可以利用用户在我们的程序逻辑中放入这些字段的数据值,因为我们将在整本书中继续构建这个应用。
您应该很擅长在 Java 代码中声明 UI 元素,使用它们的小部件类型、名称和 findViewById() 方法,如下所示:
EditText colonyText = (EditText)findViewById(R.id.editTextColonies);
注意在图 6-9 中,Eclipse 错误标记了我们的 EditText 对象,因为我们不允许在代码中使用它,直到我们导入它,所以将鼠标放在红色下划线的对象上,点击导入 EditText android.widget 包选项,让 Eclipse 导入我们的 android.widget.EditText 类。
我们需要做的下一件事是在我们的 EditText 字段中设置一个默认值,它符合我们建议用户添加的属性的数量。在菌落的情况下,我们使用“1”值,一次添加一个菌落。通过设置文本。带有两个参数的 setText() 方法,一个值,一个缓冲区类型常量。缓冲区类型可以是可编辑的、正常的(固定的)和可扩展的。对于我们的 colonyText 对象,我们的代码如下所示:
EditText colonyText = (EditText)findViewById(R.id.editTextColonies);
colonyText.setText("1", EditText.BufferType.EDITABLE);
现在,我们所要做的就是将最后两行 Java 代码复制到前两行的下面五次,并将 colony 更改为殖民者、基地、军队、forcefieldOn 和 forcefieldOff。然后,确保每个 EditText UI 元素的 XML ID 标记名与我们在 activity_config.xml 中的 XML 相匹配,然后将 colonistsText 的“1”值更改为“100”,将 militaryText 的“10”值更改为这些数据字段设置更合理的默认值。
对于我们的 forcefieldOn 和 forcefieldOffText 数据字段,我们将使用“Forcefield is Off”作为 forcefieldOffText 数据默认设置,并使用 null(空引号)设置作为 forcefieldOnText 数据默认设置。
所有五个复制的两行代码块的最终 Java 代码应该如下所示:
EditText colonistText = (EditText)findViewById(R.id.editTextColonists);
colonistText.setText("100", EditText.BufferType.EDITABLE);
EditText basesText = (EditText)findViewById(R.id.editTextBases);
basesText.setText("1", EditText.BufferType.EDITABLE);
EditText militaryText = (EditText)findViewById(R.id.editTextMilitary);
militaryText.setText("1", EditText.BufferType.EDITABLE);
EditText forcefieldOnText = (EditText)findViewById(R.id.editTextForcefieldOn);
forcefieldOnText.setText("", EditText.BufferType.EDITABLE);
EditText forcefieldOffText = (EditText)findViewById(R.id.editTextForcefieldOff);
forcefieldOffText.setText("Forcefield is Off", EditText.BufferType.EDITABLE);
现在我们只用了十几行 Java 代码就建立了 EditText UI 元素,如图 6-10 所示。稍后,我们将为按钮 UI 元素的 onClick()事件处理程序添加 Java 代码,这些代码将从我们使用设置的 EditText 字段中获取数据值。getText() 方法,并用这些值相应地设置我们的 WorldGen 对象数据变量。
图 6-10。复制六个 EditText 对象和。用 Java 实现 EditText UI 字段的 setText()方法
接下来,我们需要确定我们的 XML 标记和 Java 代码是否给我们提供了我们一直试图实现的更精炼的用户界面设计结果。右键点击项目文件夹,选择 Run As Android Application 菜单项,当 Nexus S 模拟器启动时点击菜单按钮和 Configure Planet 菜单选择,观察 UI 屏幕。
注意,我们对 ems 字体大小设置的估计是正确的,并且 EditText 字段中的数据是一个很好的大小,因此我们将保留那些标记参数设置。然而,EditText 字段的底部并没有与按钮元素的底部完美地对齐;看起来像是 12dp 的 android:layout_marginTop 值太大了,这将每个数据字段从其上的数据字段向下推得太远。因此,让我们将 marginTop 值减少 33%至值 8dp 和再次作为 Android 应用运行。
正如你在图 6-11 中看到的,我们的配置 Planet UI 屏幕排列良好,Java 代码中设置的数据值正确地出现在每个 UI 数据字段中,就像我们设计的那样。在第七章中,我们添加了半透明的按钮,让事情变得更加有趣,但现在,这个活动屏幕的用户界面设计已经升级,并在我们的 Java 代码中实现了,所以让我们继续前进,接下来拨入我们的星球旅行活动屏幕。我们在这个应用上取得了巨大的进步!
图 6-11。使用实现的可编辑文本字段配置活动 UI 屏幕
优化我们的 TravelPlanet 活动:Android 的 VideoView Widget
我们的 TravelPlanet.java 活动类拥有最简单的 XML 标记代码,并将拥有最复杂的 Java 代码(当我们进入视频章节时,第十一章和第十二章),因为我们将使用一种先进的新媒体类型:数字视频。我们在这一部分的目标是使我们的应用的“到行星旅行”部分在外观和用户体验上更专业,我们将通过去掉位于视频视图屏幕顶部的按钮来实现这一点,以便用户可以享受全屏视频播放。为了完成这个,我们需要从我们的 UI 中移除按钮 UI 元素。
配置我们的 VideoView 用户界面元素
进入 activity_travel.xml 编辑页签,将按钮 UI 元素标签从 FrameLayout 容器中全部删除,如图图 6-12 所示。因为我们不再需要点击按钮来退出我们的活动,我们将需要使视频视图本身可点击,所以让我们寻找一个参数来实现这一点。键入 android ,然后按下冒号键从 Eclipse 中获得参数选项助手对话框,然后向下滚动并寻找任何处理可点击性的参数。
图 6-12。移除按钮 UI 元素标签,改为使 VideoView UI 元素可点击
请注意,列表中有两个名为 android:clickable 和 android:longClickable 的参数,双击 android:clickable 来添加它,然后使用相同的工作流程返回并双击 android:longClickable 参数来添加它,此时我们正在这里编写标记。在本章后面的 Java 代码中,以及在第十一章和第十二章中,我们将更深入地讨论数字视频,我们使用 Android onTouch() 事件(因为视频正在触摸屏上播放)在视频屏幕上实现点击和长时间点击。
接下来,我们添加了另一个 UI 参数,当我们的用户在一个星球的表面旅行时,它可以增强我们在数字视频播放过程中的用户体验。许多 Android 设备优先考虑节能而不是用户体验,因为我们不希望我们的 Android 设备屏幕在用户的旅行体验中变暗(甚至变黑),所以我们将寻找一个参数来解决这个问题。
再次键入 android 关键字和冒号键激活器并从 Eclipse 中调出参数选项助手对话框,并寻找一个在 VideoView 播放期间保持我们的 android 设备屏幕打开的参数。
这里需要注意的是(如果你还没有注意到的话),这个 Eclipse 参数选项助手对话框将只用与你在其中输入 android 关键字和冒号激活符的标签相关的参数填充它的视图。因此,这是准确找出每个用户界面元素的标签有哪些可用参数的好方法。双击 android:keepScreenOn 参数并添加。
一旦您完成了 activity_travel.xml 文件的 XML 编辑,它应该看起来像图 6-12 。现在,我们只需要更改 Java 代码来访问我们在 XML 编辑会话中所做的事情,这样一切就像手套一样合在一起了。
添加 Java 代码,将我们的旅行融入行星屏幕的新用户界面元素
我们需要做的第一件事是删除实例化我们的按钮对象的 Java 代码,或者编辑它,用一个 travelVideo VideoView 对象替换我们的按钮对象,如下行代码所示:
VideoView travelVideo = (VideoView)findViewById(R.id.travelVideoView);
一旦这样做了,Eclipse 将会突出显示一个错误和一个警告;根据错误import Android . widget . VideoView导入 video view 类以供使用,另一个黄色警告强调了这样一个事实,即您不再需要import Android . widget . button语句,因此如果您想的话,现在可以删除该 import 语句(您不必非要这样做—这是一个警告级别,不会阻止代码正确编译和运行)。
图 6-13。用 Java 中的 VideoView 对象和 onTouch()处理程序替换按钮和 onClick()处理程序
一旦我们创建了 travelVideo VideoView 对象,我们就可以在它上面设置一个事件监听器。因为数字视频最好通过触摸屏设备使用,所以让我们利用这个机会使用 onTouch() 事件监听器,而不是 onClick()事件监听器。要设置 OnTouchListener() ,使用以下代码:
travelVideo.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Toast.makeText(TravelPlanet.this, "Going Home", Toast.LENGTH_SHORT).show();
finish();
return true;
}
});
有了这段代码,我们的用户所要做的就是触摸屏幕(在我们添加视频后,视频最终会出现在屏幕上),然后 onTouch() 事件处理程序向屏幕广播一个 Toast ,上面写着“回家”,然后执行活动 finish() 方法,将用户带回主页。现在,我们的 VideoView UI 元素处理按钮 UI 元素曾经处理的事件,按钮不会阻挡我们查看视频视图屏幕。这样的 UI 设计有更专业的用户体验。
在第七章中,如果我们需要触摸事件来控制视频回放或视频传输,那么我们可以将触摸事件改为长点击。现在,我们只是把基础放在适当的位置,以便我们可以在以后对它进行扩展,但在某种程度上,它可以在当前的应用功能中很好地工作。这是开发我们代码的最好方法,通过确保它在开发的每个阶段都能很好地工作。
因此,让我们作为 Android 应用运行,并确保代码正常工作。一旦应用启动,点击菜单按钮,并选择旅行到星球菜单选项,它会带你到一个黑色的屏幕(空白视频视图屏幕),你可以在屏幕中间点击(模拟手指触摸)。一旦你这样做了,一个写着“回家”的祝酒信息就会出现,应用会返回到主屏幕。现在我们已经有 75%的应用活动屏幕在工作,我们需要微调我们的攻击星球屏幕,然后我们将准备进入一些图形设计!
优化我们的攻击星球活动 UI: ImageButton UI Widgets
在我们修改attack _ activity . XMLlinear layout 容器中的 ImageButton 标签的 XML 标记之前,我们应该将 ImageButton 标签将用于自定义按钮图标的图像元素放入正确分辨率的可绘制资源文件夹中。我们在本章的“配置行星”一节中已经这样做了,关于我们的 ImageView 标签使用的六个行星图像(只有这些图标更酷)。先这样吧。
将图像素材放置到位
复制五个 ImageButton 图形的 96 像素版本。png 文件到您的Hello _ World/RES/drawable-xhdpi/文件夹中。它们被命名为 attackbomb96px.png、attackinvade96px.png、attackvirus96px.png、attacklaser96px.png 和 attackexit96px.png。一个巧妙的方法是选择第一个,然后按住键盘上的 Ctrl 键,同时选择其他四个。这允许不连续的文件选择,而 Shift 键将选择一系列文件(单击第一个文件,按住 Shift,然后单击所需范围内的最后一个文件)。然后右键选择复制,然后选择目标文件夹,右键选择粘贴。
一旦它们在 /res/drawable-xhdpi 文件夹中,删除文件名的 96px 部分,这样文件(最终,一旦我们完成所有这些复制)都有相同的通用名称:attackbomb.png、attackinvade.png、attackvirus.png、attacklaser.png 和 attackexit.png。
接下来,对这五个文件的 80px 版本执行完全相同的工作过程,将它们复制到 /res/drawable-hdpi 文件夹中,并删除文件名中的 80px 部分,因此它们都是通用名称。再次进行相同的工作过程,将这五个文件的 64px 版本放入 /res/drawable-mdpi 文件夹,最后再将这五个文件的 48px 版本放入 /res/drawable-ldpi 文件夹。
参考、对齐和合成我们的攻击图标图像源文件
现在,我们已经准备好进入 Eclipse XML 编辑窗格,并添加增强新用户界面屏幕设计所需的标记参数。我们还需要更改标记的 android:src 参数中指定的图像源文件名,以便我们令人印象深刻的新自定义 ImageButton 图形图标在每个 ImageButton 标记中正确引用。
让我们首先修改 ImageButton 标签(图 6-14 ),使用新的文件名,以及功能参数,如声音效果、背景颜色值和边距,以便在图像图形之间的 UI 屏幕上留出一些空间。
图 6-14。升级 ImageButton 标签参数以引用新图像并添加背景透明度
因为最终(在第七章之后)我们所有的应用屏幕都将有深色或黑色的背景,以太空中发现的星际场和等离子场为特色,让我们给 LinearLayout 容器添加一个新参数,给它一个黑色背景。我们将通过 android:background 参数来实现这一点,正如我们将在第七章中了解到的,颜色黑色在计算机中通过 #000000 值来表示。
当我们添加 android:background 参数时,让我们为每个 ImageButton 添加相同的标记参数,并使用值 #00000000 ,这是透明的值,因为额外的两个零控制透明度。同样,我们将在下一章图形中学习为什么会这样。
接下来,我们将更改 launcher_ic 占位符图形(最后!),带有我们的通用攻击图形名称,这些名称现在位于我们的四个分辨率特定的可绘制文件夹中。如果你在包资源管理器 /res/drawable 文件夹中没有看到它们的名字,如图图 6-14 所示,那么你可能忘记了在之前使用 Windows 资源管理器将文件复制到正确的文件夹中之后,右击 Hello_World 文件夹并刷新IDE 在硬盘上的项目文件夹的“视图”。
一旦 Eclipse 可以“看到”新文件,将每个 ImageButton 的启动器图标 android:src 参数引用更改为新文件名的第一部分,如图图 6-14 所示。为了更专业地分隔屏幕上的 ImageButton UI 元素,添加一个初始值为 9dp 的 android:layout_margin 参数,以便在我们为 ImageButton 使用的每个新图形周围留出一些空间(没有双关语,至少现在没有)。
最后,让我们使用参数助手对话框来查找一个音效标签参数,方法是键入 android ,然后按下冒号键。向下滚动并搜索与启用 ImageButton 声音效果相关的标记参数。双击添加Android:soundEffectsEnabled参数,一旦在列表中找到**,将其值设置为 true。。确保您为五个新的 ImageButton 标记中的每一个标记进行了所有四个更改和添加,如图图 6-14 和图 6-15 所示。接下来,我们将添加文本。**
图 6-15。按下朝左的人字形<字符来调用 Eclipse 中的添加标签助手对话框
向我们的 XML 标记添加屏幕标题文本视图
为了确保用户知道当他们到达这个攻击 UI 屏幕时要做什么,让我们在我们的 UI 屏幕顶部添加一个标题来告诉用户当他们在这个屏幕上时要采取什么行动。在第一个 ImageButton 标签前添加一行空格(按回车键),如图图 6-15 所示,然后按键盘上的朝左的人字形<键(Shift-Comma) ,注意在 Eclipse 中会弹出一个添加标签助手对话框。
找到 TextView 标签,双击将其添加到您的 XML 标记中。请注意,这里有数百个标签,足以装满几本书,您可以使用这个弹出助手来探索一些不常用的标签。在本书中,我们将介绍您最常用来实现文本、图像、视频、动画 UI 元素等的标签,但是当您有时间时,可以通过这个很酷的功能随意探索更适合的 UI 元素。现在让我们配置我们的文本视图标题。
因为我们的 UI 屏幕背景现在是黑色的,让我们通过使用 android:textColor 参数和一个数据值 #FFFFFF,使我们的标题文本变成白色,我们很快就会知道它是白色的十六进制值**。使用 android:textSize 参数将您的文本大小设置为 18 标准像素( sp ,并确保添加 wrap_content 的标准 layout_width 和 layout_height 参数设置;如果您忘记了这些,Eclipse 将标记您的 XML。**
最后,在 strings.xml 文件中添加一个 attack_planet_caption 标记,它设置这个值:使用图标选择攻击类型,然后通过一个 android:text 参数和一个@ strings/Attack _ planet _ caption**值引用它。
要预览到目前为止您所做的工作,您可以使用编辑窗格底部的图形布局选项卡,或者使用作为 Android 应用运行工作流程来获得您的 UI 屏幕在 Android 智能手机上的更精确的渲染视图。随着这个攻击 UI 屏幕变得更加复杂和详细,您会看到图形布局预览只是一个估计,并没有提供像模拟器一样好的预览。
正如您将在预览中看到的,UI 屏幕在顶部和左侧有元素,但有很多黑色空间,不是我们在本章中试图实现的专业外观的 UI 屏幕,因此我们需要在每个 ImageButton 图标的右侧添加一些文本,以告诉用户每个图标的作用。
使用嵌套的 LinearLayout 容器创建更复杂的用户界面设计
为了创建一个更复杂的 UI,我们将需要遵循一个类似于我们将配置星球 UI 屏幕提升到下一个级别的工作流程,因此让我们在一个父级水平 LinearLayout 中添加两个嵌套的垂直linear layout(一个用于 ImageButtons,另一个用于 TextViews)。
将顶部的 LinearLayout 标签复制到其下,并将第一个(现在是父级)LinearLayout 上的方向参数更改为水平。删除第二个 LinearLayout 的 android:background 参数,因为第一个(父)现在设置 UI 屏幕的全局参数。将第二个 LinearLayout 的 android:layout_width 从 match_parent 更改为 70dp ,因为我们现在希望左边的 LinearLayout 符合我们的 ImageButtons。
请记住,确保在 XML 定义的底部有两个嵌套的 LinearLayout 结束标记,然后复制第二个 LinearLayout 标记并将其粘贴到倒数第二个 LinearLayout 结束标记之后,以创建第三个 LinearLayout 标记。将 TextView UI 元素复制到第二个嵌套的垂直 LinearLayout 容器中,因为该容器将保存我们的 TextView UI 元素。同样,确保你的第二个嵌套的 LinearLayout 容器在 XML UI 定义的底部也有一个结束标签,并缩进嵌套的 linear layout,如图 6-16 所示。
图 6-16。嵌套 LinearLayout 容器以添加到 TextView 标签中来标记我们的攻击星球选项图标
接下来,让我们复制下面的Attack _ planet _ captionTextView 来创建一个Attack _ planet _ bombTextView 标签,我们可以为一些更大更丰富的文本进行配置,以标记每个攻击 ImageButton 图标的功能。将 android:textColor 值更改为 #FFFFBB 使其变为黄色并将 android:textSize 值更改为 25dp 使其比标题文本大 40%。添加一个带有 18dp 值的Android:layout _ margin top标签,将文本从屏幕标题下推至第一个 ImageButton 旁边。
现在,通过你的图形布局和运行 Android 应用工作进程来渲染UI 屏幕,看看它看起来怎么样。在我们将这个 TextView 标签复制四次以创建我们的其他 TextView 标签之前,我们仍然需要一点微调,所以让我们接下来做这件事。
微调我们的用户界面设计,给我们的攻击图标添加文本标签
添加一个Android:layout _ margin left参数,其设备无关像素(DIP,或 DP)值为 8dp ,以将我们的文本从 ImageView 图标向用户界面屏幕的中心推一点。
现在我们准备复制并粘贴这个attack _ planet _ bombTextView 四次,创建我们的其他 attack _ planet TextViews。确保将 android:text 参数更改为引用您在 strings.xml 文件中创建的< string >标签常量,这些常量根据每个文本视图的功能为它们创建文本标签(参见图 6-17 )。
图 6-17。Nexus S 模拟器中的攻击星球 UI 屏幕预览
将复制的 TextView 标签的 android:layout_marginTop 参数调整到 36dp 到 40dp 之间,使标签靠近每个图标图形的中心,如图图 6-17 所示。这是一个迭代的过程,您可以调整这些值,并在模拟器中来回移动,直到您获得将每个标签放在屏幕上每个图标中间的值。接下来,更改 textColor 值以设置从黄色到紫色的颜色渐变。
接下来,我们需要升级我们的AttackPlanet.java活动子类中的 Java 代码,以实现我们尚未实现的四个 ImageButton 对象。请记住,我们之前已经对 exitButton ImageButton 对象进行了编码,并让它调用我们的 Activity finish()方法,Java 代码我们将保持不变,实际上,我们将复制它,然后再粘贴四次,以创建我们的其他四个 ImageButton 对象。
添加 Java 代码来整合我们的攻击星球屏幕用户界面元素
复制包含 ImageButton 对象创建的代码块,并将。setOnClickListener 代码块在 exitButton 对象设置代码下实现了四次 OnClickListener()和 onClick()方法。
将 exitButton 代码保留为第五个也是最后一个代码块,转到第一个代码块,在所有三个位置(名称、引用和方法调用)将 exitButton 更改为 bombButton 。接下来,让我们将 finish() 方法调用改为 Toast.makeText() 方法调用,它告诉我们的用户当他们单击 ImageButton 图标图形时做了什么。
我们的**。makeText()** Toast 对象方法有一个 AttackPlanet 类上下文,一个炸弹掉了!文本消息和短的显示长度持续时间,如下所示:
Toast.makeText(AttackPlanet.this, "Bombs Away!", Toast.LENGTH_SHORT).show();
接下来,执行相同的工作流程,用 onClick()事件处理程序创建 invadeButton 、 infectButton、和 laserButton ImageButton 对象,这些对象向部队发送、**病毒传播、和激光发射!**分别消息。
图 6-18 显示了完成后五个代码块的样子!
图 6-18。在 AttackPlanet.java 为每个攻击方法添加 ImageButton 对象和 onClick()处理程序
现在,我们可以完整地测试我们的攻击星球活动用户界面。
右键单击项目文件夹并作为 Android 应用运行,以启动 Nexus S 模拟器。加载后,单击菜单按钮并选择攻击星球菜单项,在攻击星球 UI 屏幕出现后,单击前四个 ImageButton 图标图形中的每一个,并观看您的祝酒词消息完美地出现在 UI 屏幕的底部。然后单击第五个退出图像按钮,观看屏幕退出并返回您的家!
我们现在已经实现了 XML 标记和 Java 代码,使我们的整个 Hello World 应用在基本用户界面设计级别和导航级别上 100%可用。我们准备在本书接下来的几节中实现一些非常酷的富媒体元素。
我们将在接下来的几章中添加这些新的媒体元素,包括图形设计、动画、数字视频和数字音频。这应该会很有趣,因为我们现在要让这个应用对我们所有的最终用户来说真的很有趣,像游戏一样!
摘要
在本章中,我们仔细研究了一些最常用的 Android android.widget 包用户界面类,并在我们的 Java 代码中将它们实现为我们自己的 Hello World 应用的功能 UI 对象。我们了解到 UI 元素窗口小部件是从古老的 Android 视图类中派生出来的子类,我们看了看 Android 应用开发项目中最重要和最普遍使用的 UI 窗口小部件类。
在这一章中,我们学习了更多关于 Eclipse 特性的知识,特别是我们如何简单地通过在一个空行中键入 < (朝左的人字形字符),键入 android 关键字,然后键入冒号键激活器,就可以调出标签助手对话框和参数助手对话框。这是一个很好的方式来探索 Android 操作系统中在任何给定时间(在任何给定的 API 级别安装上)可用的数百个标签和参数。
对于我们的新星球活动,我们仔细研究了 Android 的 ImageView 类,以及 Android 的 TextView 和 Button 类,然后我们使用 XML 中的一些自定义参数在更高级的层次上实现了这些 UI 元素类型。然后,我们在 Java 代码库中实例化这些 UI 元素对象中的每一个,为新的行星选择屏幕创建一个高度可视化的用户界面,我们需要用户使用该界面在我们的应用中选择和创建新的行星。
对于我们的 Configure planet 活动,我们利用了 Android 按钮类,以及通过 Android EditText 类的文本编辑字段,然后我们用 XML 中的自定义参数实现了这些 UI 元素类型中的每一个,然后用 Java 实例化它们,以创建我们的数据输入用户界面,用于我们的用户能够在我们的应用中定义 Planet 属性所需的 Planet 配置数据输入屏幕。
对于我们的 Travel Planet 活动,我们查看了 Android VideoView 类,我们在 XML 中实现了一些高级参数,以便我们的 VideoView 可以被点击和长时间点击(或触摸和长时间点击)来实现功能。我们还使用一个 android:keepScreenOn 参数来确保任何 Android 设备屏幕在我们的视频内容播放过程中保持打开。最后,我们用 Java 实例化了我们的 VideoView 对象,并实现了一个 OnTouchListener() 和 onTouch() 事件处理程序,为我们的用户在我们的应用中旅行到他们的星球表面所需的星际旅行数字视频播放屏幕创建了一个光滑的用户界面。
对于我们的攻击星球活动,我们研究了 Android ImageButton 类,该类允许 Android 开发人员实现由图像和动画等图形素材构成的按钮。正如你可能想象的那样,我们将在下一章图形设计中更详细地讨论这个小部件,但是我们想在讨论这本书的这一部分之前先介绍一下基础知识。我们在 ImageButtons 中添加了自定义按钮图标图形,以替换我们正在使用的占位符图像,并添加了其他参数,为我们使该屏幕支持声音效果和合成做好准备,我们将在未来的章节中学习这些内容。然后,我们在 Java 代码中连接这些 ImageButtons,使这个 UI 屏幕在用户界面设计上既实用又专业。
在本章中,我们学习了 Android Toast 类和对象,以及如何实现 Toast,向我们的用户广播反馈消息,向他们提供可视化更新,告诉他们在他们使用我们的用户界面屏幕活动子类期间,我们的应用正在执行什么任务。
在下一章中,我们将学习在 Android 应用中使用图形,我们将开始为我们的 Hello World Android 应用定制屏幕布局和用户界面,使用 XML 标记代码(大部分)以及 Java 代码来定义和实现这些令人惊叹的用户体验和用户界面元素。接下来的几章将会非常有趣,因为我们将会使我们的应用具有商业可行性!**
七、Android 图形设计:概念和技术
在本章中,我们将了解 Android 中数字图像和图形设计的基本概念,以及这些概念如何在 Android 操作系统中实现,以及一些数字图像技术。
我们将了解 android.graphics.drawable 包和 android drawable 类,以及 Android 支持的多种类型的 Drawable 对象。我们还将学习什么是 ARGB ,什么是阿尔法通道**,以及所有关于数字成像的概念,如像素、色深、抖动、分辨率、纵横比、图层、混合、图像压缩、和格式**
然后,我们将应用这些新知识,通过一种称为合成的数字成像技术,添加一些与空间相关的背景元素,并确保它们无缝实现,从而将我们迄今为止创建的 Hello World 应用提升到一个全新的专业水平,我们也将在本章中了解更多信息。
为了在我们的应用中实现所有这些新的数字图像功能,我们将继续学习以新的方式应用 XML 用户界面参数,正如我们从第二章开始所做的那样,也正如我们将在本书的其余部分继续做的那样。
本章将更多地涉及图形设计元素、数字成像概念和成像技术,而不是编程概念,因为这些天来,Android 应用越来越多地涉及视觉设计、3D、图形设计、动画、数字图像和其他新媒体元素,这使得本章变得非常必要。
让我们从学习 Android graphics 包和 drawable 类开始,它们让所有这些视觉魔法对我们的最终用户变得生动,并允许我们开发人员以逻辑和结构化的方式在 Java 代码和 XML 标记中实现我们心目中的东西。
Android 图形设计最高水平:Drawable 类
类似于我们在前一章中看到的与 UI 元素或小部件相关的 Android 视图类,Android Drawable 类也是与图形设计相关的对象的最高级别类,这些对象在 Android 中被称为 drawables ,因为它们被绘制到我们的显示屏上。
这就是为什么项目资源文件夹中的一些子文件夹以单词 drawable 开头,因为它们包含了应用的图形设计元素。到目前为止,我们已经使用了数字图像 drawables,但在我们完成之前,我们还将使用其他几种类型的 drawables,以实现动画或图像交叉淡入淡出效果。
和 View 类一样,Drawable 类通常不直接使用,但是它有无数的子类。与视图子类一样,Drawable 既有直接子类又有间接子类。这些 Drawable 子类完成了 Android 图形中的所有繁重工作,因此,在本书中,我们将在我们的应用中引入并利用其中的许多子类。让我们回顾一下哪些可绘制子类是直接的,哪些是间接的,以及这些可绘制子类对我们的应用有什么作用。
Android Drawable 类的直接子类
Drawable 类的直接子类本质上等同于我们可以在 Android 应用中使用的图形设计元素的类型。
到目前为止,Drawable 类最常用的直接子类被称为 BitmapDrawable 子类 ,它用于数字图像,例如我们目前在应用中使用的 PNG 文件以及 WEBP、JPEG 或 GIF。
ColorDrawable 是最基础的关卡 Drawable 类,它用于定义屏幕的颜色,就像我们在第六章中为我们的攻击星球活动用户界面屏幕定义黑色屏幕颜色一样。
GradientDrawable 用于创建从一种颜色到另一种颜色的渐变,可以由任何形状定义。Android GradientDrawables 支持的形状有:直线、矩形、椭圆(卵形)、圆形、环形(箍形或环形),可以画出一个渐变线性(直的,任意角度)径向(从一个点发出),或者扫掠(线性但绕一个点旋转)。
ShapeDrawable 用于在 Android 中创建一个矢量形状。向量或形状是定义 2D 体积外部的 2D 线或曲线,如心形或星形。熟悉 Adobe Illustrator 的人会熟悉矢量形状,以及渐变和彩色图形工具。
我们讨论的最后三个 Drawable 子类(ColorDrawable、GradientDrawable 和 ShapeDrawable)为 Android 操作系统提供了许多矢量软件包(如 Illustrator 或 InkScape)将提供的基本功能,只是我们在 Android 中的 Java 代码级别上提供了这些功能。这使我们能够创建与在 InkScape 中创建的相同类型的矢量艺术作品,然后通过 Java 使艺术作品具有交互性或动画效果。
LayerDrawable 让我们可以处理多层图像,就像人们在 Photoshop 或 GIMP 数字成像软件中看到的那样,以及它们的图像层功能。这是一个更高级的 drawable,用于高级游戏和实时合成应用。我们将在本章的后面了解更多关于图像图层和合成,以及它们是如何使用的。
InsetDrawable 允许我们使用一个叫做插图的屏幕区域或子集来显示图形(可绘制)元素。这用于像窗口小部件这样的东西,它们只使用显示屏的一部分作为它们的应用或目的所需的屏幕区域。设备主屏幕的时钟可能是使用 InsetDrawable 的一个很好的例子。
Android Drawable 类的间接子类
Drawable 的间接子类是更复杂或更详细的图形函数,它们利用直接子类来创建更复杂的图形设计效果和运动,如动画。
例如,AndroidTransitionDrawable,其中交叉淡入淡出两幅图像以创建图像过渡,是 LayerDrawable 的子类。因为这些 LayerDrawable 对象处理层中的多个图像,所以它们是创建 TransitionDrawable 类的子类的逻辑类,TransitionDrawable 类获取两个图像并制作它们的 alpha 通道值的动画(是的,我们将很快介绍这一点)以创建令人印象深刻且有用的图像交叉淡入淡出效果。
除了 TransitionDrawable 类,我们还将大量使用 Drawable 的Animation Drawable,它是 Drawable 的另一个关键的间接子类,在 Android 中实现了帧动画。我们将在后面专门关注动画的章节中介绍帧动画和矢量动画。因为我们在这里讨论了主要的 drawable indirect 子类,所以我现在将它放入正确的上下文中。
LevelListDrawable用于进度条和类似的应用,在这些应用中,需要根据某种活动的级别来替换屏幕上的图形元素。类似的 StateListDrawable 类 用于根据应用运行时可能遇到的不同状态变化来替换图形元素。StateListDrawable 可以以任何特定的顺序访问图形元素,也就是说,不按顺序,而 LevelListDrawable 则按顺序访问图形,从一个级别到另一个级别。
LevelListDrawable 和 StateListDrawable 都是从 Androiddrawable containerdirect 子类派生出来的子类,该子类通常不被直接使用或调用,但如果 LevelList 或 StateList drawable 容器不能完全满足您的需求,它可以用于创建您自己的自定义可绘制容器。
因为大多数应用(和大多数网站,就此而言)使用位图进行图形设计,本章接下来的几节将更详细地介绍位图图像的概念和特征,因为它们是创建既美观又专业的 Android 应用用户体验的关键。我们将从位图图像的基础开始,即像素,并从那里开始构建,就像数字图像本身是一次构建—一个像素一样!
数字成像的最低级像素:像素
数字图像是由微小的彩色点组成的;如果你曾经使用过数字成像软件包中的变焦功能,比如 GIMP,你可能已经知道了。一张图片的每个元素被称为一个像素,它是单词图片(在流行俚语中称为 pix)和单词元素 (el)的变形。我打开了 GIMP 2.8,将我们攻击病毒的 48 像素版本从正常的(100%)放大到 800%,这样你就可以看到图 7-1 中组成图像的各个图片元素(像素)。我们将在本书中使用 GIMP 2.8 进行我们的数字成像工作流程,因为它是开源的,我们所有的读者都可以免费下载并安装它(就像我们在第一章中所做的那样)。请注意软件底部下拉小部件中的缩放设置,以便轻松访问新设置。还要注意,在屏幕的顶部有关于文件名、颜色深度、图层和分辨率的信息,所有这些我们将在本章接下来的三节中详细讨论。
图 7-1。我们的攻击病毒图片元素(像素)
像素是使用位定义的,作为程序员,我们都知道这些位是代表数据的二进制值,在本例中是颜色值。这就是图像被称为位图的原因,因为它们实际上是位值的映射,使用二进制(十六进制)数据格式定义每个像素的颜色值。这就是为什么我们有 8 位图像(8 位数据用于定义每个像素)和 24 位图像,以及 32 位图像,我们将在本章稍后关于色深的章节中很快了解所有这些。
使用像素塑造图像:分辨率和纵横比
在 GIMP 窗口右上角的图 7-1 中,你会看到一个 48x48 符号。这是以像素为单位的图像的分辨率 ,即 48 像素宽,48 像素高。图像分辨率用 2D 或二维来表示,即宽度乘以高度。图像体积或图像中的像素数是通过宽度乘以高度来计算的,因此我们的 48x48 像素图像在其体积中包含 2,304 个像素。我们的 Nexus S 模拟器屏幕分辨率为 800x480,包含 384,000 像素。
所以分辨率的概念相当简单,假设你知道如何使用计算器将两个数相乘!这个关于长宽比 的新概念有点复杂,因为它涉及到屏幕分辨率的多少像素宽与多少像素高的比。
当涉及到缩放一幅图像时,纵横比是最重要的,这是指放大或缩小图像的大小。如果您在缩放图像(或视频)时没有保持纵横比,您的图像将会失真。这是数字成像和数字视频中常见的错误;我们都见过那些看起来像锥子或者脸看起来有点宽的人的照片。这就是为什么数字图像缩放(调整大小)对话框有一个保持纵横比锁定功能,你应该总是选择这个功能。
计算纵横比很像在分数数学中寻找最小公分母,因为纵横比很像分数。纵横比是用宽:高还是宽:高来表示。因此,从技术上来说,我们的 Nexus S 的长宽比为 800:480,但在行业中,这通常会减少到可以显示该比例的最小的两个数字。
所以,让我们逆向工作(这是我如何在我的脑海里做这个计算,不使用计算器),并删除零。从技术上讲,80:48 也是 800x480 显示器的正确纵横比。因为它们是偶数,所以我们将它们分成两半,得到 40:24,再得到 20:12。现在我们离个位数的数字对越来越近了!既然它们都还是偶数,我们再把它们切成两半,得到 10:6,现在其中一面有个位数。它们仍然是偶数,所以再次将它们分成两半,我们得到 5:3,这是 800x480 屏幕的正确纵横比。
其他常见的宽高比包括 16:9 (HDTV),自 1920 年以来除以 16 乘以 9 等于 1080,4:3 是所有分辨率的原始计算机显示器宽高比,除了 1280x1024,它是 5:4 的宽高比。另一种流行的 Android 智能手机分辨率(和迷你平板分辨率)是 854x480,也是 16:9。因此,800x480 的屏幕是 5:3,854x480 的屏幕是 16:9,尽管它们在分辨率上看起来很接近,但在 UI 设计方面,它们相差很远,因为它们的纵横比非常不同。我们将在本书后面的第十二章中处理这两个特别接近(分辨率)但远离(纵横比)的分辨率之间的差异。
请注意,数字越接近相等,图像就越方;我们在图 7-1 中的 48x48 像素图像的长宽比为 1:1。2:1 宽高比的图像仅仅意味着图像的宽度是高度的两倍,而 3:1 或 4:1 宽高比的图像可以被称为完全的全景图像!
塑造像素的颜色:色深的概念
现在我们已经讨论了像素、分辨率和长宽比,我们可以进入像素着色本身,并讨论颜色理论。有两种类型的彩色显示,减色 用于印刷,其中油墨颜色彼此相减,以及加色,用于发光显示(显示使用光)产品,其中颜色值彼此相加。
在减色中,红色和绿色(油墨)值产生紫色,而在加色中,红色和绿色(浅色)值产生黄色。差别很大!减色法,用于印刷行业,遵循一种 CMYK 颜色模型 ,代表青色品红黄色黑色(我猜他们把黑色这个词的 K 拿错了头)。
加色在消费电子行业用于以显示屏为特色的产品,遵循 RGB 颜色模型 。 RGB 代表红绿蓝,通过使用这三种颜色的光,可以创造出可见光谱中的任何颜色。不同的颜色是通过改变每个 RGB 颜色成分的强度(暗,或完全关闭,到亮,或完全打开)而产生的。
在数字世界中,这些 RGB 颜色中的每一种都可以有 8 位或 256 级的颜色强度。因此,对于红色,0 将完全关闭,或者黑色(完全没有红色值)和 256 将完全打开,或者 100%红色。
由于我们对图像中的每个 RGB 颜色通道都有个 8 位的颜色值,所以该图像可以说是一个 24 位颜色图像 ,也称为真彩色图像。Truecolor 图像可以显示 16,777,216 种不同的颜色值,可以通过将 256 乘以 256 (65,536 种颜色,也称为 16 位颜色)然后将该值再次乘以 256 来计算。
正如我们在上一章中看到的,256 个级别可以用两个字符表示,使用十六进制,或以 16 为基数的数字表示,其中不是从 0 计数到 9(以 10 为基数),而是从 0 计数到 F,给我们一个字符“槽”16 个值因为 16 乘以 16 是 256,所以两个槽给了我们表示 256 个不同值的能力。
为了获得上一章图 6-17 中“入侵行星”选项的橙色颜色值,我们使用了十六进制颜色值 #FFDDBB ,这意味着红色的全强度(256),绿色的低强度(196,或 14 乘以 14),蓝色的更低强度(144,或 12 乘以 12)。
除了 24 位真彩色图像,还有 8 位索引彩色图像 。索引的彩色图像使用一个 8 位的索引,在调色板到中有 256 种颜色,近似于图像中的所有颜色。这对于某些图像比其他图像效果更好,例如,在晴朗的蓝天中蓬松的白云的图像可能作为索引颜色图像效果很好,因为使用的 256 个颜色值可以分布在仅使用 256 种颜色而不是 16,777,216 种颜色模拟真彩色图像所需的白色和蓝色阴影上。
索引彩色图像的数据占用空间(文件大小)通常比真彩色图像小得多(压缩前是真彩色图像的三分之一),可以很好地表现图像中的颜色。我们在第六章的攻击星球 UI 屏幕中使用的图标都使用索引色,正如在截图顶部的图 7-1 中可以看到的,它指定了索引色。因为我们的病毒细胞使用粉红色、紫色和红色的阴影,所以它是使用索引色模拟真彩色图像质量结果的非常好的候选,使用的数据少得多(2.86 千字节)。
即使图像中有多种不同的颜色,索引彩色图像也能产生令人印象深刻的结果,正如我们在下一节的图 7-2 所示的攻击激光图像中所看到的。一般来说,图像的像素分辨率越高,索引颜色的效果就越好。我将在下一节关于 Alpha!的压缩技术中向您展示如何优化索引图像压缩的结果。
图 7-2。攻击激光数字图像,其阿尔法通道通过棋盘图案显示
定义图像中的透明度:Alpha 通道的概念
24 位图像有三个(RGB)颜色通道**,而 32 位图像有四个( ARGB )颜色通道。32 位图像中的第四个通道称为 alpha 通道 。像图像的每个 RGB 通道一样,alpha 通道也可以有 256 个级别,但这 256 个级别不是定义颜色强度,而是定义该像素的透明度级别(或半透明)。**
对透明度进行微调、逐像素控制的主要用途是图像合成** ,这是将许多图像层合成在一起以创建一个最终图像或特殊效果的过程。攻击激光图像中的 alpha 通道,如图 7-2 所示,在激光炮周围是黑色的(0 或透明),在图像中你看到激光炮的地方是白色的(256 全不透明)。GIMP 通过一个棋盘图案 来表示透明度,你也可以在图 7-2 中看到。
如果你想看看 alpha 通道在将一幅图像合成到任何其他图形上时有多有效,看看图 7-2 中屏幕截图的左上角,GIMP 在窗口标题栏上合成了你的激光炮。看起来它是被设计在那里的,不是吗?我们在 Hello World 应用中使用的几乎所有图像,—行星、激光炮、士兵和炸弹—都有一个阿尔法通道,这样当我们将星际机场和等离子云放在它们后面时,背景将完美地显示图像中的每个细节。也就是说,图像无缝地合成了**。**
我们已经将 UI 元素参数变得完全透明,在 #00000000 的 XML 中使用了 ARGB 设置。前两个零指定不透明度** 为 100%关闭,因此透明度为 100%打开。不透明是透明的反义词,因此不透明程度和透明程度彼此相似。如果一个像素 30%透明,那么它 70%不透明。
Alpha 通道在应用开发中经常使用,因为我们经常想要使用内容开发引擎(在这里是 Android)作为图像合成工具。在图像合成中,合成中的每个层都带有一个 alpha 通道或 alpha 数据组件,以定义通过该层显示的内容以及该层上的哪些数据将以某种方式应用于最终的图像合成结果(最终的视觉图像)。
除了使用图像层及其 alpha 通道之外,合成还包括在每个层上使用一种混合算法 ,该算法对像素数据值进行求和(或求差),基于复杂的算法可以创建图像合成特效,如叠加、屏幕、变暗、变亮、异或等等。
android 通过 PorterDuff 算法 支持这些混合模式,在一个名为Android . graphics . porter duff的包和类中,因此,Android 不仅像 InkScape 一样被类固醇;还像打了类固醇的 GIMP 2.8.4!编码混合算法有点超出了像这样的关于 Android 的入门书籍的范围,但是如果你想将 Android 数字成像提升到一个新的水平,它将在我的“Pro Android 图形设计”书中(Apress,11/2013)出现!
Android 中的图像格式支持:PNG8、PNG24、web、JPG、gif
现在我们已经掌握了数字图像中的不同属性,我们可以讨论 Android 支持的一些不同的文件格式,因为这些图像文件格式中的每一种都有不同的组合和对颜色深度和 alpha 通道的支持级别。
Android 操作系统对其中一些格式的偏好超过了其他格式,这将决定我在本节中讨论这些格式的顺序。
便携式网络图形(PNG)格式
Android 最喜欢的格式是便携式网络图形或 PNG (读作 Ping )文件格式。PNG 有两种风格, PNG8 或索引色 PNG 和 PNG24 或真彩色 PNG。因为 PNG24 也可以“携带”一个 alpha 通道,从技术上讲,一个带 alpha 的 24 位 PNG 应该是一个 PNG32 。
Android 喜欢 PNG 格式的图像,因为 PNG 使用无损图像压缩,产生最高质量的结果,因为 PNG 图像在压缩过程中不会损失任何原始图像质量(或数据)。
联合图像专家组(JPEG)格式
下一个最适合在 Android 中使用的图像格式是 JPEG,它代表 ?? 联合图像专家组。这种图像格式采用有损图像压缩,即“丢弃”部分原始图像数据以达到更好的压缩效果,但以牺牲图像质量为代价。如果你放大一个 JPEG 图像,你会看到看起来脏或变色的区域;这些是压缩产物,也是 JPEG 格式不是 Android 开发首选图像格式的原因之一。
JPEG 图像格式的另一个重要方面是它不能携带 alpha 通道,因此它不能用于图像合成,除非它是图像合成层堆栈中的底层,或者除非 alpha 信息后来在应用内部附加到它。
图形信息(GIF)格式
Android 中最不理想的图像格式是 Compuserve GIF 格式,它代表图形信息格式。GIF 图像仅支持 8 位索引颜色,并且比 PNG8 文件具有更大的数据占用空间(较弱的压缩算法)。除非万不得已,尽量不要在 Android 开发中使用 GIF。
Web 照片(web)格式
自 4.0 (API Level 14 冰淇淋三明治)版本和更高版本以来,Android 操作系统中增加了一种额外的图像格式,称为: WEBP 。WEBP 代表网络照片,它类似于 PNG32,但数据占用空间要小 25%;即其压缩算法优于 PNG24 和 PNG32。
然而,如果你想将你的 Android 应用交付给数百万使用 Android 1.5 到 3.2 (API 等级 3 到 13)设备的用户,你可能会想使用 PNG24 和 PNG32 格式,以及 PNG8(当它工作良好时)(正如我们在本书中为 Hello World 应用所做的)。
减少图像数据足迹:图像压缩概念
一旦我们定义了我们的图像和 alpha 通道,以及我们将要使用的文件格式,在我们的图像被创建和合成以及任何 alpha 通道被定义和添加之后,我们经历的最后一步是压缩图像以获得最小的文件大小。
注意我们压缩图像的原因是因为我们的 Android 应用的总大小是我们所有图像加上我们的 Java 代码和 XML 标记的总和,因为我们都知道文本比像素压缩得更好,所以应用的大部分数据足迹反映了我们优化新媒体素材的效果。
在这一节中,我将介绍影响压缩算法的关键因素,以及最终应用压缩过程(压缩算法)后,图像文件大小的最终数据足迹。
分辨率是调整图像以获得更好压缩(文件大小)效果的最大因素。正如我们之前了解到的,要压缩的像素的绝对数量总是归结为像素宽度乘以高度。
如果不进行压缩,图像的原始数据大小将按如下方式计算:像素宽度乘以像素高度乘以 3(对于 RGB)或 4(对于 ARGB)。
因此,对于我们的 ARGB 96 像素激光炮,如图 7-2 所示,原始未压缩图像数据是9216像素(96x96)乘以 4 (ARGB),这是36864总像素要压缩,或四个图像通道中的每一个都是 9216 像素。36,864 除以 1,024(一千字节数据中的像素数)得到的原始文件大小正好是 36 千字节,或36 千字节。
我们的 96 像素激光炮图像的最终文件大小为 4.89KB,比小 7.36 倍(36 除以 4.89),或原始数据占用空间的 13.6% (4.89 除以 36)。100%减去 13.6%会产生 86.4%的压缩率。
我们做了什么来压缩这个图像几乎 87%,它仍然看起来很棒?因为我们无法改变它的分辨率,所以我们改变了它的颜色深度。颜色深度是图像压缩中第二重要的因素。这是因为您必须将像素的数量乘以颜色通道的数量,如果您可以减少这个乘数,您也可以极大地减少结果文件的大小。所以我们没有使用 32 位的 PNG,而是使用了 8 位的 PNG;通过这样做,我们将压缩的像素数量从 36,864 减少到 9,216。
正如你在图 7-2 的截屏顶部看到的,attacklaser.png 是一个 8 位索引彩色 PNG 图像,然而它看起来像是一个更高质量的真彩色图像。这是因为我使用了抖动,这是 8 位图像压缩的一个关键选项。
抖动包括在 8 位索引调色板中模拟超过 256 种允许的颜色。这是通过使用微妙的点模式来混合两种颜色,形成介于两种颜色之间的第三种颜色。这种技术欺骗眼睛,让它认为有超过 256 种颜色被用来创建图像。由于最近流行的 XXHDPI(超高密度像素图像)Android 屏幕中的像素尺寸较小(像素尺寸称为点距),抖动是一种有价值的技术。
抖动对 8 位图像特别有用,这些图像表现出一种称为条纹 的颜色深度转换特性,因为它可以通过这种技术模拟 512 种颜色来减轻或消除条纹。请注意,图像数据中的抖动模式会稍微增加文件的大小,因此请确保打开和关闭抖动选项,以查看图像质量是否提高到足以值得增加到图像数据占用空间中的那几个额外的数据字节。
提升我们的新行星活动:应用成像概念
现在是时候看看这些概念如何应用到我们的 Android 应用开发中了。让我们给我们的新行星活动屏幕添加一个星域背景,这样我们的行星就在它们所属的太空中了。
给我们的 HelloWorld 应用 添加一个星星图像背景
我们需要做的第一件事是将这些 starfield 图像素材复制到各自的文件夹中。将 stars320x480.png 复制到 drawable-ldpi 文件夹,将 stars480x800.png 复制到 drawable-mdpi 文件夹,将 stars1024x600.png 复制到 drawable-hdpi 文件夹,最后将 stars1280x720.png 文件复制到 drawable-xhdpi 文件夹。将每个分辨率版本复制到各自的 drawable 文件夹后,请务必将每个版本重命名为简单的stars.png。
首先,注意这些是索引的 PNG8 文件,大小从 6KB 到小于 24KB 不等,尽管最大的是 HD 分辨率。星星是用于索引文件格式的最佳图像类型的完美示例,因为有很多黑色区域,没有太多图像细节或颜色变化,所以在这种情况下,PNG8 的压缩结果令人惊讶。
编辑我们的屏幕布局 XML 来引用星星背景图片
接下来打开 activity_add.xml 文件,添加一个 android:background 参数,通过 @drawable/stars 路径寻址机制引用 stars.png 文件,如图图 7-3 所示。然后选择 Hello_World 项目文件夹,点击 F5 键刷新项目视图,或者右键单击项目文件夹,选择刷新菜单选项。然后点击编辑窗格底部的图形布局选项卡,预览我们的图像合成结果。正如你在图 7-4 中看到的,行星在星空背景下看起来很棒,但是我们的说明文字似乎已经消失了,在我们用户界面屏幕的左侧有一条 22 度倾斜的白色条纹。
图 7-3。在 activity_add.xml 文件中添加一幅 starfield 背景图像,以合成包含一些恒星的行星
图 7-4。使用 Eclipse 中的图形布局编辑器选项卡预览我们的 android:background 参数
这不是我们期望的专业用户界面结果,所以我们需要改变我们标签中的几个标签参数,以进行调整,现在我们有了深色的星空背景,而不是白色的背景色(见图 7-4 )。
调整我们的 XML 标签参数以适应新的星星背景图片
这里发生的事情是,我们已经在我们的 RelativeLayout 布局容器中使用了 marginLeft 参数来将行星作为一个组居中;现在我们已经为布局容器设置了背景图像,marginLeft 参数也将背景图像推过 22 度,从而在左侧产生白色条纹。
Android 操作系统使用白色的默认应用背景颜色,就像它对所有布局容器的默认颜色值一样。这就是所显示的,因为我们通过 android:marginLeft="22dp "参数推动整个布局容器。
为了纠正这一点,我们将从相对布局容器标签中移除参数 marginLeft ,取而代之的是,我们将把它放入我们的每个 ImageView 标签中,以相同的量推动它们,如图图 7-5 所示。
图 7-5。修改我们的 TextView 和 Button 标签,添加白色文本颜色参数和 MarginLeft 参数
我们还需要将 marginLeft 参数的另一个副本放入 TextView,并将其推回,这样它就不会触及用户界面屏幕的左边缘。
调整我们的 TextView 标签的 textColor 参数 来增加对比度
接下来,我们需要向 TextView 和 Button 标签添加一个设置为 #FFFFFF (白色)的 android:textColor 参数,这样我们就可以在 stars.png 背景下看到标题和按钮标签。在数字成像理论中,我们需要学习的另一个重要概念是对比度,为了获得可读的文本,我们需要高对比度,即深色背景下的亮色文本。该标签参数添加如图 7-5 所示。
现在我们已经对需要它们的标签进行了 android:marginLeft 和 android:textColor 参数更改,是时候使用作为 android 应用运行并调用 Nexus S 模拟器,并实际查看我们新升级的添加行星用户界面屏幕看起来是什么样子。
正如你在图 7-6 中看到的,由于白色文本和深色 starfield 背景之间的高对比度,文本现在是可读的,按钮看起来也不错。你有没有注意到,当我们把 stars.png 的图像放在后面时,按钮的背景变暗了?这是因为按钮背景不仅是灰色的,它也是透明的,事实上,你可以看到一个小星星就在按钮的顶部,透过这种透明显示出来。
图 7-6。在 Nexus S 模拟器中预览我们的图像合成结果
这是一个混合的例子,我之前提到过,其中按钮背景颜色是通过算法添加到每个背景像素颜色中的。这允许您将按钮放在图像上,以获得更专业的视觉效果。注意,这个混合量也可以在 XML 参数或 Java 代码中更改。
因为这看起来很好,让我们继续将 stars.png 图像添加到 AttackPlanet 用户界面屏幕背景。在应用中多次使用图像素材是从图像素材中获得更多收益的好方法,这有助于使应用的数据足迹更小。
升级我们的 TravelPlanet UI:为我们的攻击病毒 创建阿尔法通道
在我们将 android:background 参数添加到 activity_attack.xml 文件之前,就像上一节添加行星屏幕一样,我们需要为 UI 的一个图标图像创建一个 alpha 通道。您可能已经注意到,在将图标复制到项目子文件夹中时,其中一个图像,attackvirus.png,在病毒周围有一个实心的黑色背景,而不是像其他四个图标那样具有透明度。
让我们使用 GIMP 开源图像编辑软件来完成纠正这一问题的工作流程,这样您就知道这是如何完成的了,因此稍后,我们可以在任何背景上放置病毒,并获得无缝的结果。
添加透明度:创建一个阿尔法通道蒙版
启动 GIMP 2.8.4,然后使用文件打开菜单序列打开位于项目资源 drawable-xhdpi 文件夹中的attackvirus.png文件(见图 7-7 ),因为该文件的分辨率为 96 像素。在数字成像中,我们总是从最高分辨率开始,然后逐渐降低以避免任何像素化。
图 7-7。打开 drawable-xhdpi 文件夹中的 attackvirus.png 文件,然后调用“按颜色选择”工具
点击选择菜单和【按颜色 图像区域选择工具,这将使您进入连续颜色选择模式,该模式将选择包含具有相同 RGB 颜色值的像素集合的颜色区域。
一旦进入工具模式,将阈值(选择灵敏度)设置为 40 ,并点击图像四个角之一的黑色像素。由于我们的病毒细胞本身有几种不同的颜色值,所以选择病毒细胞中而不是的颜色,而不是试图选择病毒细胞的颜色,这是一个更合理的工作过程。一旦你的选择框(行进的蚂蚁轮廓)看起来像在图 7-8 中一样,使用键盘上的删除键删除黑色像素。
图 7-8。通过显示选择和选项对话框的颜色选择框进行选择,阈值设置为 40
一旦你按下 Delete 键,你会看到 GIMP 中使用的白色背景色(就像 Android 使用的作为其 app 背景色一样),这将使我们的 alpha 通道图像蒙版的进一步编辑(清理)更加容易(图 7-9 )。
图 7-9。删除选中的黑色值,显示 GIMP 默认的白色背景色
遮罩定义了将要在图像合成中显示或使用的图像部分,因此,我们在此进行的过程称为将攻击病毒细胞从攻击病毒细胞的图像中掩蔽。
接下来,再次下拉选择菜单,选择无选项,确保在我们编辑图像之前,图像中没有任何内容被选中。然后选择橡皮擦工具(在你的工具面板中寻找一个老派橡皮擦)并将工具设置为 1 像素、方形纵横比 (0 值)和角度设置为 0(与屏幕成直角),并从动态下拉菜单中选择铅笔通用。使用橡皮擦工具,单击未被选中的边缘黑色像素,因为它们与您单击以调用该工具的黑色像素值不够接近。
这个过程被称为清理你的遮罩,这样做是为了让你在攻击病毒图像对象周围获得一个更紧密的选择区域,我们稍后将在我们的 Android 应用中使用该区域进行合成效果。
通过比较图 7-9 和图 7-10 可以看出这次黑色像素清理过程的前后对比。请确保在边缘留下一些暗像素,以更好地混合我们将在 Hello World 应用中使用的暗背景。
图 7-10。使用橡皮擦工具清理我们的面具,准备创建一个阿尔法通道
反转 Alpha 通道选择病毒
现在,我们将再次使用按颜色选择工具,并点击当前图像角落的白色区域,通过刚刚清理的蒙版重新选择我们的新对象。这更精确地选择了病毒(或者更确切地说不是病毒),正如你在图 7-11 中看到的。接下来调用选择反转菜单操作,将我们的选择从非病毒改为病毒本身。
图 7-11。使用选择反转来反转选择
继续操作反转你的选择蒙版,得到一个正确选择的最终攻击病毒对象像素集合。现在,我们将使用编辑复制来复制攻击病毒像素到我们的系统剪贴板。
现在我们已经选择了攻击病毒图像对象的像素并将其复制到剪贴板,我们可以将它们粘贴到一个新文件中。当我们这样做时,GIMP 将在没有任何像素的像素位置放置一个 alpha 通道(透明)。
GIMP 2.8 还根据系统剪贴板中选择数据所使用的水平和垂直像素数,自动设置新的图像像素分辨率。在这种情况下,这是 90 乘 89,如在截图左下方带有 alpha 通道的新攻击病毒图像的窗口标题中的图 7-12 所示。在屏幕截图的右边,你还会看到新图像的 ARGB 通道,以及通过粘贴为新图像为我们创建的阿尔法通道。
图 7-12。使用粘贴为新图像创建新图像(左下)与阿尔法通道(右上)
在图 7-11 和图 7-12 中注意到同样重要的是,如果你把你的鼠标放在一个菜单项或者一个工具箱图标上,GIMP 会弹出一个工具提示,告诉你如果你选择使用它,这个工具会做什么。这是探索 GIMP 能做什么和了解它的特性的一个重要途径。
在图 7-12 中,不仅显示了我们将从下一个操作中生成的新的无标题图像,还显示了操作菜单选项(为了节省空间,我创建了一个二合一截图),您将看到编辑菜单有一个粘贴为子菜单,它本身有一个包含新图像粘贴功能的子子菜单,该功能将把剪贴板中复制的攻击病毒像素粘贴到一个全新的图像文件工具提示说:从剪贴板的内容中创建一个新图像。工具提示没有说明的是:GIMP 会在剪贴板中没有像素的像素位置自动创建 alpha 通道。
注意在图 7-12 右上角的 alpha 通道中,GIMP 在病毒所在的地方使用白色像素(开或可见),在我们最初编辑选择蒙版的地方使用黑色像素(关或透明)。还要注意,在图像通道上方,通道图标被表示为三维的 RBG 值层;这是考虑颜色通道的一个很好的方法,尽管最终是数学将这些 ARGB 值相加来定义每个像素的颜色和半透明级别。
以 GIMP XCF 本地格式 保存我们到目前为止的工作
因为我们的新图像是未命名的让我们使用文件另存为菜单序列给我们的带有 alpha 的新图像命名,并保存它,这样我们不会丢失所有的工作。我将文件命名为: attackvirusnew.xcf (GIMP 原生格式)用 attackvirusnew.xcf 放入 drawable-xhdpi ,如图图 7-13 所示。
图 7-13。将新的无标题图像保存为 attackvirusnew.xcf,这样我们的工作就被保存了
下一件事,我们想做的是把我们新掩盖的攻击病毒放回 96x96 像素图像容器的中心。我们需要在创建此图像素材的其他三个较低分辨率版本之前完成此操作,我们需要在 Android 项目资源可绘制子文件夹结构内的其他三个分辨率图像文件夹中使用这些图像素材。
使用画布大小工具将我们新蒙版的图像 重新居中
我们将使用图像画布大小菜单序列重新调整蒙版图像的中心,这将调用 GIMP 中的画布大小对话框。假设 GIMP 中新标题为 attackvirusnew 的窗口处于活动状态,让我们对 90x89 像素的图像应用画布大小图像操作,并将其转换为 96x96 像素的图像。
画布尺寸对话框如图图 7-14 所示,它允许您输入目标分辨率(画布尺寸),并通过使用对话框右侧的中心按钮来设置相对于每个 X Y 尺寸的偏移,或允许 GIMP 为您完成此操作。这就是我所做的,因为我是那种喜欢走捷径的人,因此 GIMP 在我这样做的时候在 X 和 Y 字段中分别输入了 3 个像素的值。
图 7-14。使用图像画布大小和中心功能,在 96 像素的正方形中重新定位蒙版图像
使用 GIMP 的文件导出(另存为)对话框 将新图像保存为 PNG32 文件格式
现在我们准备将我们攻击病毒的 alpha 通道版本保存为一个带索引的彩色 PNG 文件,这在 GIMP 中是使用文件导出菜单序列完成的。在 GIMP 中,使用保存会产生一个 XCF (GIMP 原生)文件格式,而导出通过其他流行格式保存。你可能更熟悉这个叫做另存为的软件操作。。。函数,在 GIMP 2 . 6 . 12 和更早的版本中是这样,在其他软件包中也是这样,所以你需要习惯 GIMP 2.8 和更高版本。
图 7-15 显示了 GIMP 中的导出图像对话框,在这里我们输入新的文件名 attackvirusalpha 和扩展名。png 来指定格式。
图 7-15。使用文件导出图像来导出带有 alpha 通道的新的 PNG8 版本的图像
在 GIMP 中,文件格式被指定为我们输入的文件名的一部分,或者如果您想查看可以导出的其他文件类型,您可以使用对话框左下角的选择文件类型(按扩展名)UI 元素。我通常使用。tif (TIFF 或标记图像文件格式)或。png,因为这些是无损格式,可以产生完美的视觉效果,但您也可以使用。jpg (JPEG,一种有损格式)甚至。tga (Targa,另一种无损格式)格式,如果你喜欢的话。
一旦我们将 attackvirusalpha 导出为 PNG8,我们需要进入我们的 OSes 文件管理器(Explorer 或类似的),并重命名一些文件,以便接下来我们可以访问 Eclipse 中图形的正确(alpha)版本。
图 7-16 中显示的是 Windows 7 资源管理器,我们已经将我们的旧版本(原始)attackvirus.png 重命名为 attackvirusold.png,然后将新的attackvirusalpha.png文件重命名为attackvirus.png**,这在我们的 Hello World 应用的 XML 标记中被引用,如截图所示。**
图 7-16。使用 Windows 资源管理器将 attackvirus 重命名为 attackvirus old,将 attackvirusalpha 重命名为 attackvirus
注意,我们的 GIMP attackvirusnew.xcf 工作文件现在也出现了;这不会影响我们在 Eclipse 中的项目,但是我们可以稍后删除这个文件,或者将它移动到我们硬盘驱动器上的另一个文件夹,比如说,一个用于原始艺术品或开发中素材的文件夹,如果您愿意的话。
使用图像调整工具 创建我们的其他分辨率密度图像版本
现在,我们的最高分辨率图像素材已经被遮罩,并包括一个 alpha 通道来定义其透明度,我们可以将图像调整到 80 像素、64 像素和 48 像素版本,这是我们的应用项目资源文件夹层次结构中的其他可绘制文件夹所需的(参见图 7-17 )。
图 7-17。使用图像缩放图像和三次插值将 96 像素图像调整为 80 像素
这个工作过程是通过图像菜单和缩放图像子菜单完成的,子菜单访问常用的缩放图像工具对话框,该对话框用于将数字图像调整到不同的分辨率。注意链图标,它允许你锁定图像纵横比,正如我们之前讨论的。
将宽度字段中的 96 替换为 80 ,然后注意,当您单击高度字段(或按回车键)时,其他字段也会自动键入 80,因为纵横比被锁定(默认)。第二组字段包含屏幕分辨率(像素密度),因此对于屏幕工作,将这些字段设置为 72 DPI(对于打印,使用 300 DPI 或更高)。
将缩放算法的插值质量下拉菜单设置为三次(在 Photoshop 中称为双三次),然后点击缩放按钮。
现在我们的图像是 80×80 像素,我们需要经历与之前完全相同的工作过程,从图 7-13 开始。将您的文件保存为 GIMP。XCF 如果你以后需要它,在 drawable-ldpi 文件夹中,然后将其导出为一个名为 attackvirusalpha 的 PNG8 ,然后进入你的文件管理器,这样你就可以将原始文件重命名为 attackvirusold ,并将新文件重命名为 attackvirus ,这样在我们刷新我们的项目视图后,正确的图像数据就可以用在你的 Android Hello World 应用中了。
对 drawable-mdpi 中的 64 像素图像素材,以及 drawable-ldpi 中的 48 像素图像素材再次进行这个工作过程,当你完成时,你将有机会熟悉这个工作过程。
现在,我们准备进入 XML 标记,将星星添加到 AttackPlanet 用户界面屏幕布局容器的背景中。值得注意的是,当您将图像缩放至 64 和 48 像素时,您应该从 96 像素版本的已保存图像开始,以便为图像大小调整算法提供最多的数据,从而获得最佳的缩放结果。
启动 Eclipse(如果尚未打开)并单击 activity_attack XML editing 选项卡,将您的 LinearLayout android:background 参数从 black (#000000)更改为@drawable/stars.png,并且不要忘记 Refresh !
每当您更改任何文件名或文件内容或添加新文件时,您必须总是右键单击您的项目文件夹并选择刷新或左键单击(选择)您的项目文件夹并点击键盘上的 F5 键。
现在让我们作为 Android 应用运行,看看我们使用深色背景和浅色文本的第二个用户界面屏幕后面的星星。
正如你将在图 7-18 中看到的,视觉效果和我们预期的一样令人难以置信,我们的星星通过图标和文本用户界面元素完美地显示出来。请注意,Android 中的文本元素有自己的 alpha 通道和不透明度设置,我们将在下一章中看到,届时我们将更详细地讨论用户界面元素(widget)图形设计技术。
图 7-18。 AttackPlanet UI 屏幕显示星星背景和白色背景上的伪装病毒
我们在 Hello World 应用的图形设计基础上取得了很大进展,现在我们一半的活动用户界面屏幕都展示了新的基于媒体的用户界面元素和背景。我们还学习了许多数字成像背后的基本原理,以及一些关于 alpha 通道和遮罩的数字成像工作流程。
摘要
在本章中,我们学习了数字图像和图形设计的基本概念,以及 Android 如何将这些概念融入其应用开发基础设施。为了在我们的 Hello World 应用中实践这些图形设计和数字成像概念,我们在一些活动中添加了 starfield 图像背景参数,并屏蔽了我们的 AttackVirus 以添加 alpha 通道,以便星星可以通过 UI 元素的透明区域无缝显示。
我们了解了 Android Drawable 类及其 direct 子类,它们定义了我们可以在 Android 应用中使用的所有各种类型的图形设计素材,包括形状、颜色、插入、图层、渐变和位图(位图图像,也称为基于像素的图像)。
我们学习了 Drawable 的更复杂的间接子类,比如动画、图像过渡、等级列表和状态列表。我们学习了 DrawableContainer ,并且我们可以创建我们自己的自定义间接多图形子类,使用那个超类作为它们的基础。
接下来,我们看了一下像素的概念,它是数字成像和数字视频的构建模块。然后,我们看了分辨率和长宽比的图像概念,以及如何使用简单的数学计算两者。
我们学习了色彩理论,以及数字成像中使用的不同色深,以及如何使用十六进制符号来表示颜色。我们学习了如何根据图像的色深计算图像中的颜色总数,以及关于索引色图像和 256 种颜色调色板的概念,调色板从真彩色图像中取样原色。
我们了解了图像通道和 RGB 颜色通道,以及第四个 alpha 通道,它保存像素的透明度值,以便图像可以通过使用层和混合模式用于合成更复杂的图像或特殊效果。我们通过Android . graphics . porter duff类了解了 Android 中的混合支持,并了解了它的主要混合模式,例如:叠加、屏幕、变暗、变亮、异或。
我们涵盖了 Android 中所有不同的图形文件格式,以及哪些格式更适合使用,为什么,以及哪些格式是无损的,哪些是有损的。我们介绍了 Android 4.0 和更高版本中新的 WebP 图像格式支持,并介绍了带有 alpha 通道图像的 PNG8 索引和 PNG24 真彩色和 PNG32 真彩色之间的区别。
最后,我们介绍了压缩概念和技术,如抖动,然后我们将学到的数字成像概念应用到 Hello World 应用中,通过将酷图标和文本与自定义背景 starfield 图像进行合成,添加更多视觉设计和专业功能。
在下一章中,我们将在本章建立的数字成像知识的基础上,添加自定义 UI 元素。****
八、Android 中的组合:高级图形用户界面设计
在本章中,我们将采用在前一章中掌握的基本数字成像概念,并构建更复杂的数字成像构造,以将更高级的基于图形设计(即基于位图)的用户界面元素添加到 Hello World 应用中。这将帮助你使你的应用在视觉上引人入胜,无论它是游戏、商业应用、实用应用,还是(希望)全新的、前所未有的东西。
我们讨论多状态用户界面元素,它改变用户在使用 UI 时看到的图像,并向最终用户提供关于他们正在触摸(或正在点击)什么的视觉反馈,以及哪个用户界面元素是最后使用的。我们将各种 UI 元素与其他图形设计元素组合在一起,创造出高级的、无缝的视觉效果;跨越几个不同标签及其参数设置的效果,但看起来像一个 UI 实体。
我们还完成了每个应用活动屏幕用户界面的设计和外观,增加了独特的(非标准)UI 元素,使我们的应用在所有视觉层次上都完全自定义。许多 Android 开发人员只实现和使用标准的用户界面小部件,因此他们的应用只具有标准的 Android 外观和感觉,而没有使用任何基于图像的视觉修改,这将使他们的 Android 应用与市场上的其他应用相去甚远。
多状态 UI 元素:正常、按下和聚焦状态
让我们将攻击星球活动用户界面屏幕上的 ImageButton 用户界面元素升级到下一个级别,并将其更改为多状态图像按钮。多态图像按钮交互改变按钮图形,这取决于用户正在做什么(或者刚刚做了什么)来与它们交互。在你的用户界面设计中加入一个交互式的用户界面元素,可以让用户对你的应用所展现的专业水平有一个全新的认识。
多状态图像按钮中的 正常状态是您第一次到达用户界面屏幕时看到的图形元素。在我们的攻击星球用户界面屏幕中,这些按钮的正常状态是我们已经有的按钮图标图形。你也可以称这个按钮状态为默认按钮图像状态。
每当用户触摸按钮时,多状态图像按钮中的 按下状态就会显示出来,这意味着用户在触摸屏上的手指触摸或导航按钮按下动作(如果用户使用导航键)。对于我们新的 AttackPlanet 多态 ImageButton 按钮按下的图像元素,让我们在图标周围添加一个金箍。通过这种方式,当用户触摸该用户界面屏幕上的攻击方法图标时,一个金环环绕该图标,显示 UI 元素正被触摸的交互反馈。
多状态按钮中的聚焦 状态发生在用户释放(停止触摸)该按钮并使用该用户界面元素时。用户界面元素被称为具有焦点,因为用户正在使用它,并且它不会失去焦点,直到用户选择另一个用户界面元素(触摸另一个 ImageButton UI 元素)或者附加到该 UI 元素的操作完成,例如我们的 Toast 消息当前在我们的 Java 代码中充当占位符。
我们利用图像按钮图标周围的银色圆圈来显示哪个按钮具有焦点,并且当前正在使用,因此哪个按钮是用户最后选择使用的。首先,我们需要为三种图像状态中的每一种创建 ImageButton 源文件,我们的正常状态已经创建好了,所以我们只需要使用 GIMP 2.8 中的一些高级多层合成技术来创建按下和聚焦的按钮状态。
创建我们的 ImageButton 多态图像源文件
让我们启动 GIMP 2.8,现在为我们的四个攻击模式图标,为 Android 要求的四个像素密度中的每一个都这样做。这是四个攻击模式图像按钮乘以四个像素密度,乘以两个按钮状态,这等于我们需要创建的三十二个不同的图像素材。如果你想做好,开发一个专业的 Android 应用无论如何都不是一件容易的事情。
添加我们已经拥有的四种像素密度的四个图像按钮,我们的四个攻击模式多状态按钮有 48 个图像。最后,为我们的退出攻击模式按钮添加另外四个像素密度素材,并且您有实施我们的攻击行星屏幕所需的 52 单独的数字图像素材,请注意,这不包括背景stars.png数字图像,这将这一个用户界面屏幕的总数带到 53 为每个屏幕密度创建这一高级用户界面屏幕设计所需的数字图像素材。我以为这是一本入门书!一旦我们读完这本书,你会发现你已经使用了数百种资源来开发你的 Android 应用!
GIMP 启动后,使用文件打开菜单序列在您的工作区文件夹中的 Android Hello_World 项目文件夹的 drawable-XHDPI 文件夹中找到您的 attackinvade 96 像素图形。
先设置好 GIMP 的工作环境,使用视图缩放
2:1 菜单序列将视图设置为 200%,,如图图 8-1 所示。
图 8-1。启动 GIMP 2.8,缩放至 200%,然后点击右边的图层标签(看起来像堆叠的白色牛皮纸)
接下来,点击右上角的图层标签(看起来像堆叠的白色牛皮纸),这样你可以看到我们的图像目前有一个名为【attackinvade.png】?? 的单层,这是我们刚刚打开的索引色 PNG8 士兵的脸图标图像。
如果您单击通道选项卡(一个堆叠的 RGB 颜色通道图标),您将看到一个包含 8 位图像数据的索引颜色通道。如果我们在图像处于索引颜色模式时添加我们的环层,环将只有几种颜色来描绘其 3D 金属外观,并且不会像我们希望的那样看起来像照片一样真实。我们需要做的是将我们的图像转换成真彩色模式,然后导入下一层图像合成功能。
在 GIMP 2.8 中,您的图像当前设置使用的色深可以在模式子菜单下的图像菜单中找到。如果您访问此图像模式菜单序列,您将看到几个关键的图像模式,包括 RGB (真彩色模式)灰度(单色模式)和索引彩色模式,您将看到这是当前选择的模式(该子菜单项旁边的点表示当前模式)。
让我们改变我们的图像的颜色模式,然后再继续我们的工作过程,这样我们就可以从我们在这个过程中可能添加的任何额外的真彩色图像层中获得最高的视觉效果。要将当前图像的颜色深度从索引(8 位)颜色模式更改为 RGB (32 位)颜色模式,选择图 8-2 所示的图像模式
RGB 菜单序列,将图像模式从 8 位更改为 32 位颜色。如果我们不这样做,我们添加的任何层将使用 256 色调色板的图像,这是从初始图像层(数据)。这将限制以后导入的任何图像(图层)使用这 256 种颜色;这将严重影响我们的视觉质量,尤其是当我们添加更多的层。
图 8-2。使用图像模式
RGB 菜单序列将图像模式从索引色转换为真彩色
一旦我们调用了图像模式
RGB 菜单序列,将我们的 GIMP 图像编辑环境切换到真彩色模式,我们可以再次单击右侧的通道选项卡,可以看到我们现在已经从一个通道(图像的 256 色索引调色板)变为一个图像编辑环境,其中我们有四个颜色通道(或三个颜色通道和一个透明通道)。这在图 8-3 中显示,由于我们的层被选中(以蓝色突出显示)用于图 8-1 中,所以我们的四个通道也被选中,以保持从层编辑模式(tab)到通道编辑模式(tab)的选择。
图 8-3。点击右边显示的通道选项卡,确认现在有红色、绿色、蓝色和阿尔法通道
图像编辑软件是模态,也就是说,无论工具、颜色和编辑模式在任何给定点被设置和激活,都会影响当你在图像上使用当前工具时会发生什么。因此,如果您处于图层模式,并且您在图层模式下编辑图像,这将影响图像中所有通道的数据。但是,如果您只想编辑图像的绿色通道上的数据,您可以切换到通道编辑模式进行编辑,方法是在使用这些编辑工具之前,单击通道选项卡和绿色通道(任何编辑工具都可以在任何模式下使用)。
这种模态软件操作在将软件特性组合到用户想要创建的任何工具集中的能力方面更加强大。因此,它产生更专业的结果,但也更难以有效地使用。在使用模态软件包的过程中,用户必须始终跟踪在任何给定时间什么模式、设置和工具当前是活动的。这些编辑模式、工具和设置实时结合,最终创建所使用的精确编辑功能。
导入我们的金环图像进行合成
现在我们准备进入正题,将我们的金箍棒图像导入到它自己的图层中,来设置我们的图像合成操作。为此,我们将使用 GIMP 的文件打开为图层……菜单序列,如图图 8-4 所示。正如工具提示告诉我们的,这将在我们的图层选项卡中打开一个图像文件作为图层。选择此菜单选项后,您会看到一个对话框,让您找到名为GoldHoop96.png的图像素材,这是一个 truecolor PNG32 文件,带有定义透明度的 alpha 通道。
图 8-4。使用导入环层,文件打开为层…菜单序列
选择此文件并打开它后,它会作为“层”标签中自己的层合并到项目中。通道选项卡显示合并的 RGBA 通道数据,如图 8-5 所示,您的图像视图显示无缝合成在士兵脸上的金箍。接下来,我们需要将这个包含士兵面部的新图层向下移动,这样面部就会移动到环的中心。我们这样做是为了让我们的士兵看起来像是在透过铁环看,这样眼睛就不会被遮住。值得注意的是,GIMP 使用 RGBA 通道排序,而 Android 使用 ARGB,也就是说,当在 Android 中以十六进制指定通道时,前两个位置是 alpha 值,而不是后两个位置,就像使用 Photoshop 或 GIMP 的人可能会简单地假设的那样。
图 8-5。导入箍层,并在 RGBA 通道中进行数据合并,如右边通道选项卡所示
通过移动图层位置将士兵的脸放在金环的中心
要将面部层向下移动到环中,选择带有面部图形的下层,如图 8-6 中的蓝色所示,然后也单击移动工具(四箭头十字,位于第二行中间)来设置模态编辑操作,以移动(移动工具模式)攻击入侵层(层编辑模式)。
图 8-6。选择图层标签和移动工具,将面向下移动到环中,这样顶部与环的内部相切
一旦你做到了这一点,你就可以用你的鼠标点击并拖动正面直接进入环的中心,然后,如果你喜欢,你可以使用键盘上的左右箭头键将图像移动到适当的位置,一次一个像素。这个微调你的图像居中操作,直到合成图像看起来像图 8-6 中的一样。
擦除金环外不需要的像素
现在,我们所要做的就是选择图 8-7 (第四排第二个图标)所示的橡皮擦工具,将 GIMP 置于擦除模式,擦除士兵脸部和衬衫在铁环外的部分。请注意,我们保持底部(面部)图层处于选中状态,因此我们的图像编辑软件模式包括:橡皮擦工具模式、 RGB 真彩色模式和图层编辑模式设置为 attackinvade.png 图像图层(仅在该图层上将橡皮擦工具编辑同时应用于所有四个通道)。
图 8-7。使用橡皮擦工具移除士兵图像中超出环圈的部分,如图所示
导出我们的攻击按下按钮图标状态
现在,我们准备好将 导出我们的 attackinvadepress 按钮图标状态到我们的 drawable-xhdpi 文件夹中,我们的三个图像状态按钮中的两个(正常和按下)将准备好用 XML 代码实现。
为此,我们使用 GIMP 的文件导出菜单序列,将文件命名为attackinvadepress.png、,最后,找到我们的workspace/Hello _ World/RES/drawable-xhdpi文件夹,我们要在其中保存这个 96 像素高分辨率(超高密度像素图像)的数字图像文件。在所有这些文件名、文件夹路径名和图像类型(。png 扩展名)信息,则可以点击导出按钮完成此操作,如图图 8-8 所示。
图 8-8。使用导出图像对话框保存真彩色。drawable-xhdpi 文件夹中名为 attackinvaderpress.png 的 png 文件
请注意,因为我们的 GIMP 颜色深度模式设置为 truecolor RGB,所以我们现在使用 alpha 通道导出 PNG32 truecolor PNG 文件,但我们的文件大小仅增加了一倍,从 8K 增加到 16K,因为我们删除(擦除)了大量图像数据,并且因为 PNG32 压缩算法非常高效。
如果可以很好地优化数据足迹,那么几乎总是希望使用 PNG32 格式而不是 PNG8 格式,因为在 PNG32 文件中,图像数据的质量水平以及 alpha 通道比在 PNG8 文件中好得多(使用了更多的颜色和透明度值)。
创建我们的 attackinvadefocus 图像按钮状态
最后,我们需要创建我们的 attackinvadefocus 图像按钮状态。这要容易得多,因为我们的第三个按钮状态的大部分图像编辑工作过程已经为我们的 attackimagepress 图像按钮状态完成了,包括改变图像模式,移动士兵的脸,以及擦除圆圈外围的像素。我们现在要做的就是加一个银箍来表示专注。
使用文件打开为图层……功能,如图图 8-4 所示,通过找到SilverHoop96.png图像,并将其导入到我们的 GIMP 2.8.4 项目中,将另一个图层添加到我们的图像合成堆栈中。
因为结果看起来很棒,如图 8-9 所示,我们可以继续再次使用我们的文件导出工作流程,如图图 8-8 所示,只是这一次,使用文件名:**attackimagefocus.png,**并使用相同的 drawable-xhdpi 文件夹位置,因为这是一个 96 像素的超高密度像素图像文件。
图 8-9。使用文件打开为层…菜单序列打开 SilverHoop.png 并将其添加到层堆栈上
创建我们的其他分辨率密度多态图像按钮图标
要创建 attackinvadepress 和 attackinvadefocus 的其他三种尺寸(80、64 和 48 像素)版本,请遵循图 7-17 中上一章概述的工作流程,使用图像缩放图像… 菜单序列和图像缩放对话框,将图像从 96 像素均匀缩放到 80 像素(之后为 96 像素到 64 像素和 96 像素到 48 像素)。确保在每个工作过程中点击 SilverHoop96 图层上的眼睛图标关闭其可见性,然后使用文件
导出将 drawable-hdpi 中的文件保存为attackinvadepress.png。
一旦完成,你可以再次点击眼睛图标,重新打开 SilverHoop96 层的可见性,并再次执行文件导出操作,在你的 drawable-hdpi 文件夹中创建一个attackinvadefocus.png。一旦你生成了这两个文件,使用编辑
撤销菜单序列回到你的 96 像素版本,并做这里概述的整个工作过程来创建 64 像素 MDPI 和 48 像素 LDPI 入侵图标素材。需要注意的是,在只有触摸屏的 Android 设备上(比如我们的 Nexus S 模拟器)不使用焦点按钮状态。这种聚焦状态仅在使用轨迹球、小键盘或键盘导航的 Android 设备上使用。一旦用户触摸了触摸屏(如果设备有触摸屏的话),Android 进入触摸模式,在这种操作模式下,不会使用聚焦按钮状态,但它仍然会安装并在不使用触摸屏的其他 Android 设备类型(如 ITV)上工作。
在 XML 中实现多态按钮:Android 的选择器标签
一旦您完成了上一节中概述的其他三个图像按钮图标(炸弹、病毒和激光)的工作过程,并创建了我们将需要在攻击星球活动屏幕中实现多态 ImageButton UI 元素的按下和聚焦状态的所有 32 个(4 x 4 x 2)图像,我们就可以开始编写实现 <选择器> 标记的 XML 文件(全部四个)。选择器标签允许 Android 从三个不同的按钮图像状态 <项目> 标签中进行选择,这些标签来自我们现在在 drawable resource 文件夹中的图像素材。请记住,我们的正常或默认状态图像已经完成,因此这个活动用户界面屏幕总共需要 48 个多态图像素材,加上 4 个 attackexit 图标的特定分辨率图像,因此总共有 52 个图像,当然不包括我们的 stars 背景图像。
创建一个新的资源类型的 Android XML 文件:Drawable
我们要做的第一件事是创建一个新的 Android XML 文件。右键单击你的 Hello_World 项目的 drawable-xhdpi 文件夹,然后选择新建 Android XML 文件创建向导对话框。该对话框如图 8-10 中的所示,并自动为您创建 XML 文件。
图 8-10。用一个<选择器>根元素创建一个新的 Android XML 文件
将资源类型设置为可提取,将项目设置为 Hello_World ,并将文件命名为:attack _ invasive。因为 Android 在 XML 和 Java 代码中不使用文件扩展名,所以确保不要将文件命名为 attackinvade,因为这是我们的 PNG8 文件名。因为 Android 文件名中允许使用下划线,所以在单词 attack 和 invasive 之间使用下划线,以模拟这些单词之间的空格字符。
接下来,找到名为选择器的根元素,并选择它作为我们的父标签(根元素)容器,最后点击完成按钮创建attack _ invasive . XML文件。在图 8-11 中需要注意的是,Android 将 attack _ invasive . XML 文件放在了一个 /res/drawable/ 文件夹中,这是作为新的 Android XML 文件操作的一部分而创建的。这是因为该文件引用了多个分辨率资源,所以它只在 /res/drawable 文件夹中出现一次,而不是在每个 drawable-dpi 文件夹中。
图 8-11。多态 ImageButton 的 XML 标记:在 attack _ invasive . XML 中的<选择器>标签内添加<项目>标签
将 Android 标签添加到 attack _ invasive . xml 多态 XML 定义中
现在我们已经在编辑窗格中打开了 attack _ invasive . XML 文件,让我们添加三个指定三种图像状态的标签。项目标签包含一个用于状态定义的参数和一个用于可绘制素材定义的参数。按下并聚焦的多状态项目标签编码如下:
<item android:state_pressed="true" android:drawable="@drawable/attackinvadepress" />
<item android:state_focused="true" android:drawable="@drawable/attackinvadefocus" />
默认或正常的按钮状态项标签没有多态参数,只有图像的可绘制素材引用参数,这个<项>标签是最后添加的,如图图 8-11 所示。
在这种情况下,选择器容器中这些标签的顺序很重要,因为这是评估这些项目的顺序,很像我们在 Java 代码中使用的 select (case)语句。
从我们的活动屏幕布局 XML 引用我们新的多态 ImageButton XML 定义
我们需要做的最后一件事是打开我们的 activity_attack.xml 用户界面屏幕布局定义的选项卡,并将 attackinvade 按钮的引用改为指向attack _ invasive(XML)而不是 attackinvade (PNG)。这就是 Android 实现 ImageButton 所经历的逻辑链,现在它在如何处理用户界面元素的使用中包括了多状态 XML 选择器逻辑。
为了实现其他三个炸弹、病毒和激光攻击模式多状态按钮,我们必须再经历三次图 8-10 和图 8-11 所示的工作过程,添加 XML <选择器>对 attack_bomb.xml、attack_virus.xml 和 attack_laser.xml 多状态图像素材定义 XML 文件的定义。
每个选择器 XML 文件必须指向 drawable 子文件夹中正确的 PNG8(正常)和 PNG32(按下和聚焦)数字图像素材文件名,然后我们可以在 activity_attack XML 文件中引用这些新的 XML 文件定义,而不是引用默认的图像状态 PNG8 文件名。
一旦我们的四个攻击模式按钮中的每一个都完成了所有这些工作过程,我们的攻击星球用户界面屏幕将完全转换为多状态 ImageButton 用户界面元素!
请记住,当您接下来使用 Nexus S 模拟器测试用户界面屏幕时,聚焦图像状态不会显示,因为我们正在模拟的 Android 设备是一种仅具有触摸屏的消费电子产品。
图 8-12 显示了一个 Eclipse IDE,在包浏览器窗格的 /res/drawable 文件夹中(用蓝色突出显示)有我们刚刚创建的 attack_bomb、attack _ invasive、attack_laser 和 attack_virus 文件的 XML 文件(还要注意这些文件的标签仍然在 Eclipse 主编辑窗格的顶部打开)。
图 8-12。activity _ attack . xml 中的新 XML 标记引用了新的多状态图像按钮选择器 XML 文件
在图 8-12 中,我们的 activity_attack 用户界面屏幕定义的 XML 标记还显示,我们已经更改了每个 ImageButton 标签的所有 android:src 参数,以指向 XML 文件名,在每个文件名中间使用下划线,而不是指向中间不使用下划线字符的 PNG8 文件名。
最后,我们需要测试应用,以及我们新的多状态 ImageButton 用户界面元素,通过作为 Android 应用运行的工作流程。启动 Nexus S 模拟器,点击《入侵星球》的士兵脸图标,当你点击并按住鼠标左键(通过鼠标按下操作)时,你会看到士兵脸移动到环的中心,如图图 8-13 所示。
图 8-13。在 Nexus S 模拟器中运行我们的应用,测试我们的攻击外观 ImageButton 多状态图像按钮
确保并测试所有四个攻击模式按钮图标,以确保您已经正确实现了所有活动 XML 标记和 XML 标记以及数字图像引用。
现在是时候将我们的配置行星用户界面屏幕提升到一个更专业的水平了,通过添加一个与空间相关的背景图像,然后改变我们的用户界面元素在新的碰撞星系图像上的合成方式,以便我们增强这个新 UI 设计的对比度(可读性)。
合成我们的 UI 元素:Alpha、Color、Gravity 和 TextStyle
让我们把我们的“配置一个星球”用户界面提升到下一个层次,因为这是关于 Android 图形设计合成的章节。首先,我们将添加一个视觉上令人印象深刻的背景,称为空间,它显示了两个碰撞的星系。接下来,我们将为用户界面屏幕元素标签添加新的合成和桌面发布(DTP)参数,以最大化它们的对比度(可见性),并使用新的背景以及新的字体、间距、重力和 alpha 效果来改善 UI 的外观。
首先,像以前一样,让我们将 space1280x720.png、space1024x600.png、800x480.png 空间和 480x320.png 空间文件复制到 XHDPI、HDPI、MDPI 和 LDPI 文件夹中,然后将它们全部重命名为space.png,这是我们稍后将在代码中引用的文件名。
修改 activity_config XML 参数,将空间背景图像与 UI 元素合成
我们要在我们的 activity_config.xml 文件中做的第一件事情是使用 @drawable/space 引用参数添加一个 android:background 参数指向我们新安装的space.png文件,如图图 8-14 所示。在添加新的数字图像背景后,如果您单击编辑窗格底部的图形布局选项卡,您将会看到我们现有的用户界面元素已经基本消失,因此我们必须对当前的 XML 标记进行一些相当剧烈的参数更改,以使我们的 UI 在这个空间背景下看起来非常专业。
图 8-14。编辑我们的 LinearLayout,添加 space.png 背景和按钮标签,添加白色 textColor 值
先从按钮标签开始,添加一个 android:textColor 参数,设置为 #FFFFFF 的白的十六进制值,如图图 8-14 所示。再一次,让我们使用图形布局选项卡作为快速简单的屏幕预览,看看我们的按钮是否变得更容易阅读。正如你在图 8-15 中看到的,这解决了按钮 UI 元素的问题,它们看起来很棒。然而, EditText 文本字段几乎不可能被识别,这将需要大量新的标签参数来使它们既可读又最终在外观上专业。
图 8-15。在图形布局编辑器中预览我们的新按钮和背景参数
使用背景色和 Alpha 透明度提高我们的编辑文本字段的对比度
为了让我们的 EditText 文本编辑字段与这个令人惊叹的碰撞星系背景一起工作,我们将不得不使用明亮的背景颜色(白色)、一些不透明度(alpha 通道)以允许星星穿过,以及一些更好的字体、对齐和编辑文本字段间距来匹配(平衡)屏幕布局对面的按钮间距。
我们想要添加的第一个 EditText 标记参数是 android:background 参数,我们将它设置为白色的十六进制颜色值(#FFFFFF ) ,这样我们就可以看到我们在这个深色背景上做了什么。为了使这个白色背景有点像半透明,让我们也添加一个 android:alpha 参数,它使用从零(透明)到一(不透明)的十进制范围来控制 UI 元素的不透明度 (alpha 通道值)。我们使用 50%的透明度设置 0.5 开始。现在我们可以看到我们的文本字段,接下来我们可以调整我们的Android:layout _ margin top参数来隔开它们。
对于第一个 EditText 标签,这相当于 13dp ,其余的需要一个额外的 10 dp ,或者一个 23dp 的最终设置,以使它们与每个按钮完全相对,正如你在图 8-16 中的 XML 标记中看到的。
图 8-16。编辑我们的 LinearLayout 容器标签和 EditText 标签及其参数
因此,现在我们的文本字段从上到下与它们各自的按钮完全隔开,我们需要从左到右隔开它们。他们正在触摸 UI 屏幕的右侧,这看起来很不专业。我们来调整一下,给 UI 屏幕设计增加一些对称性。
因为我们希望将所有 EditText 字段移动一个平均距离 5dp ,类似于屏幕另一侧按钮上的间距,所以让我们在保存它们的 LinearLayout 容器中用一个单个标签来完成,而不是添加六个标签(每个 EditText 元素标签一个)。
一旦你添加了如图图 8-16 所示的Android:Layout _ margin right = " 5dp ",点击你的图形布局标签,看看它看起来怎么样。为了更精确地了解它将如何排列,使用作为 Android 应用运行工作流程,并在 Nexus S 模拟器中查看当前的 UI 设计;你会发现它开始变得真正令人惊叹。
格式化 EditText UI 元素中的文本特征
现在我们已经将 EditText UI 容器与屏幕及其背景图像分隔开来并完美地合成在一起,让我们设置一些参数来优化包含在文本字段中的文本。对于数字数据输入文本字段,让我们指定一种等宽字体,这使得数字输入特别易读;这是通过**Android:typeface = " monospace "**参数实现的。
为了让文本在屏幕上更易读,我们还可以添加一个 android:textStyle 参数,设置字体使用粗体文本,使字符更粗,更容易在较小的显示器上阅读。最后,为了使文本在文本字段内看起来间隔均匀,并且在显示器的右侧对称,让我们使用一个中心参数将文本在每个编辑文本数据输入字段内居中。这通过使用设置为中心的 android:gravity 参数或 android:gravity="center" 标记来实现。Android gravity 参数以 Android 中的任何东西为中心,而不仅仅是文本,所以熟悉这个参数,并在其他类型的用户界面小部件和标签中寻找它,因为 gravity 可能非常有用。
现在是时候使用我们的 Run As Android Application 工作流程,并在 Nexus S 模拟器中预览所有这些用户界面元素标签参数修改和优化的结果。正如您在图 8-17 中看到的,配置活动用户界面屏幕看起来真的开始专业了。
图 8-17。在 Nexus S 模拟器中预览我们完成的配置活动屏幕
接下来,让我们通过添加自定义背景图像和我们所在星球的图像视图,将我们的 Hello World 应用主屏幕提升到下一个级别。我们将在稍后的动画章节中定制这些。最后,我们将为文本着色,以匹配星系背景图像中耀眼的等离子体光颜色。
升级我们的应用主屏幕:添加 ImageView 标签和 textColor 参数
我们需要做的第一件事是设置我们的全新主屏幕,将 galaxy480x320.png、galaxy800x480.png、galaxy1024x600.png 和 galaxy1280x720.png 的图像复制到它们各自的可绘制子文件夹中,就像我们之前做的几次一样。一旦这些文件分别位于 LDPI、MDPI、HDPI 和 XHDPI 文件夹中,只需将它们重命名为galaxy.png,我们就可以在 activity_main.xml 文件的 XML 标记中引用它们,该文件定义了我们的 Hello World 应用主屏幕用户界面设计。一旦我们做到这一点,我们就可以微调我们的 UI 元素颜色,以匹配银河图像。
因为我们想让这个新的背景图片跨越整个布局容器,我们将把参数 android:background 添加到 RelativeLayout 容器标签中,并将其设置为我们的galaxy.png文件的 @drawable/galaxy 引用路径,如图 8-18 顶部所示。
图 8-18。添加 galaxy.png 背景图片;定制 textColor 参数以匹配图像中的等离子颜色
使用图形布局选项卡预览图像,请注意,由于低对比度(可读性),文本现在需要我们的注意。让我们将新的文本颜色与背景图像右下方等离子体中突出的橙色和黄色色调相匹配。
让我们将左边的标签文本设置为橙色,以平衡背景图像右上角的橙色,并将数据文本设置为黄色,以匹配背景图像中心的黄色等离子体耀斑。为了创建匹配的橙色,通过使用两个 F 值将红色通道保持在最大强度,并通过将这两个值设置为 D ,在绿色通道中使用较小的强度(记住相等的红色和绿色值导致黄色),并通过将这两个值设置为 A ,在蓝色通道中使用更小的强度,因此明亮等离子体橙色的 #RRGGBB 值为因为我们知道相等的红色和绿色值会产生黄色,所以我们想要使用 #FFFF99 来获得一个完全饱和的黄色,类似于主屏幕背景图像中间等离子耀斑中的颜色。
更改你的 activity_main.xml 标记中所有的 android:textColor 十六进制值,如图图 8-18 所示,然后使用 Run As Android Application 工作进程,看看我们的新主屏幕此时是什么样子。正如你在图 8-19 中所看到的,新的屏幕在视觉上引人注目,看起来非常舒适,所以现在让我们添加一个行星图像,让用户也能看到他们当前所在的行星。
图 8-19。在 Nexus S 模拟器中预览我们新的 galaxy.png 背景图像和新的文本着色
要添加一个行星图像到我们的 UI 屏幕,使用 ImageView 标签和相对布局定位参数,如图图 8-20 所示。标记及其参数的 XML 标记应该如下所示:
<ImageView android:id="@+id/imageEarth"
android:src="@drawable/earth"
android:background="#00000000"
android:layout_width="wrap_width"
android:layout_height="wrap_width"
android:layout_marginLeft="12dp"
android:layout_marginTop="8dp"
android:layout_below="@+id/textView8"
android:contentDescription="@string/content_desc_earth" />
图 8-20。添加一个 ImageView 标签来保存我们的星球,并配置其背景 alpha 和边距参数
我们将参考我们的earth.png源图像,并设置一个**# 00000000**(# aarggbb)的 alpha 通道,它是 100%透明的。我们这样做是为了让背景星系图像的合成效果完全无缝。接下来我们将使用一个 android:layout_below 参数来引用它上面的 TextView,最后分别使用 12dp 和 **8dp,**的 marginLeft 和 marginTop 参数设置来完美定位我们的星球图像。
现在,让我们在 Nexus S 模拟器中,通过作为 Android 应用运行工作流程,对我们的工作进行最后的预览,注意我们现在有了当前世界的视觉图像,以及主应用主屏幕上那个世界的特征和设置,如图图 8-21 所示。
图 8-21。在 Nexus S 模拟器中预览我们的 Hello_World 应用主屏幕
这些用户界面设计的改进为我们的用户提供了关于他们正在处理的当前世界的视觉反馈,并且使应用的外观 100%更加专业。
在未来的章节中,我们将使用一些其他技术和标签参数,用动画和很酷的特殊图像效果来增强这个 UI 屏幕(以及我们的其他用户界面屏幕)。所以请注意,我们将在未来的动画和视频章节中使用类似的合成概念和技术,逻辑上需要合成,所以你很快就有机会建立在你最近的合成知识上,并获得合成经验。
因为这是最后一章专门讨论图形设计和合成,我想尽快为这个应用准备好所有的主要图像,并让它与我们将在“这个世界之外”Hello World 应用中使用的所有主要用户界面元素无缝合成。
接下来我们将最终升级我们的 Hello World 应用启动图标,因为众所周知,魔鬼在细节中,它不仅显示在 Android 设备图标启动屏幕上,还显示在应用的顶部。
自定义活动屏幕标题和应用图标:细节决定成败
我们 需要做的第一件事就是使用 Windows Explorer 文件管理实用程序,将 saturn36.png、saturn48.png、saturn72.png 和 saturn96.png 文件分别复制到 LDPI、MDPI、HDPI 和 XHDPI 可绘制文件夹中,如图图 8-22 所示。将 ic_launcher 重命名为 ic_launcher_old,将 saturn36.png 文件重命名为 helloworldicon.png**。稍后,我将向您展示如何使用应用的 AndroidManifest.xml 文件中的 XML 标记来实现定制的应用图标文件名。**
**
图 8-22。重命名 ic_launcher_old 并添加新的 helloworldicon.png 应用图标
一旦您的新自定义应用图标图像文件位于其各自的 drawable-dpi 文件夹中,接下来我们需要做的事情是在我们的资源值 /res/values/ 文件夹中的 strings.xml 文件中创建一些自定义活动用户界面屏幕标题,在 Android 中称为标签。
在 strings.xml 文件中创建活动屏幕标题常量
点击 Eclipse central 编辑窗格右上角的数字(在图 8-23 中是 14)下拉打开文件菜单。接下来,选择 strings.xml 文件进行编辑,将 app_name 文本值改为 Hello World - Home Screen 。将那个 <字符串> 标签再复制粘贴四次,这样我们就可以为四个应用活动用户界面屏幕中的每一个添加标题(标签)。
图 8-23。在 strings.xml 中添加<字符串>标签常量,用于我们的活动用户界面屏幕作为自定义标题
用 name = " Activity _ Title _ type _ planet "参数和文本值 Hello World - Screen Title 来命名每个标记常量,例如,NewPlanet 活动的<字符串>标记应该是这样的:
<string name="activity_title_new_planet">Hello World - Create a Planet</string>
由此产生的五个 <字符串> 常量标签来标注我们的 app 活动屏幕标题可以在图 8-23 中看到。
在我们的 Hello_World AndroidManifest XML 文件中配置活动屏幕标签
一旦我们在 strings.xml 文件中定义并放置了字符串常量,我们就可以从我们的 AndroidManifest.xml 文件中引用它们,我们将在这里为每个活动屏幕设置 android:label 参数,这将在每个活动的顶部安装一个定制的屏幕标题。
点击 Eclipse central 编辑窗格右上角的数字(在图 8-23 中是 14 ),下拉打开文件菜单。接下来,选择 AndroidManifest.xml 文件进行编辑,并在 <应用> 标签中更改当前的 android:icon 参数,以引用一个 @drawable/helloworldicon 引用,这是对我们新的 Saturn 图标图像 PNG 文件的引用。
接下来,在我们的每个 < activity > 标签中添加一个新的 android:label 参数,该参数引用了我们刚刚通过@string/activity_title 按名称创建的新的 < string > 标签常量,如图图 8-24 所示。
图 8-24。向我们的 AndroidManifest.xml 添加一个新的应用 android:icon filename 和 android:label 屏幕标题
对于 NewPlanet.java 活动用户界面屏幕,在应用顶部放置自定义标题的参数是**android:label = " @ string/Activity _ title _ new _ planet "**这相当简单,只要您知道 Android 希望您将所有内容放在哪里。因此,以 new planet Activity标记为例,带有 name 和 label 参数的整个< activity >标记如下所示:
<activity android:name="chapter.two.hello_world.NewPlanet"
android:label="string/activity_title_new_planet" />
现在让我们使用 EclipseRun As Android Application命令,启动 Nexus S 模拟器,看看新图标和屏幕标题在我们的应用顶部是什么样子。正如你在图 8-25 中看到的,结果是专业的,比之前的屏幕标题(Hello_World)和应用图标(Android Bot)有所改进,因为它们现在更具体地说明了我们的应用是什么以及它在做什么。
图 8-25。新的 Hello World 主屏幕,带有新的屏幕标题和应用图标
单击 Nexus S 模拟器右上角的菜单按钮,让我们看看使用新图标和屏幕标题后的活动 UI 屏幕。正如你在图 8-26 中看到的,我们的屏幕(我们已经完成的三个,我们将在未来实施的第四个 TravelPlanet 活动屏幕第十一章和第十二章涵盖数字视频)看起来很棒,并且由于对 UI 细节的关注,看起来更加定制和专业。
图 8-26。预览我们的 Hello World 应用活动屏幕,新的屏幕标题标签和图标已就位
注意,通过使用这个 android:label 参数来创建我们的活动屏幕标题,我们不必在屏幕容器的顶部使用 TextView UI 元素,因此我们只需要在两个屏幕上使用 TextView 来指示用户在到达屏幕时做什么。在 Configure Planet UI 屏幕上,我们的按钮标签要求用户采取行动,因此我们不需要在屏幕上显示任何 TextView UI 元素。
还要注意,我们的活动用户界面屏幕标题(标签)与我们的选项菜单标签非常匹配,因此在整个应用用户界面基础设施中为用户提供了出色的连续性。
现在,我们已经将基本的图形元素与应用的用户界面元素组合在一起,我们可以在接下来的几章中继续实现更高级的新媒体元素,如动画、数字音频和数字视频。
摘要
在本章中,我们仔细研究了一些更高级的用户界面元素和图像合成概念,将 Hello World 应用的用户界面设计提升到了一个新的视觉层次。我们学习了多状态用户界面元素。这些元素改变图像来表示用户界面元素的当前状态,即其默认或正常状态对其被触摸或按下状态,以及其使用中或聚焦状态。
我们学习了如何使用 GIMP 2.8.4 创建多通道图像,并学习了高级模态成像软件操作,以及数字成像颜色模式、编辑模式和工具模式。
我们使用这些模态数字成像工具和软件设置为我们的攻击星球图标创建了多状态图像按钮,并创建了 50 多个数字图像素材,用于我们 Android 应用中所有不同的图像分辨率密度级别。
我们学习了如何使用 XML <选择器> 标签及其子标签 <项目> 来定义使用 XML 标记的多态 ImageButton 用户界面元素,这样我们就不必编写任何 Java 代码来在 Android 应用中实现多态用户界面元素。
我们了解到,所有定义图像相关结构的 XML 文件,比如我们的多图像构造,都进入到根/可绘制的文件夹,然后引用位于可绘制文件夹下的可绘制的 dpi 子文件夹中通常命名的数字图像元素。
我们了解到, focus 按钮状态并不用于只有触摸屏的 Android 设备(如 Nexus S emulator)。聚焦状态用于具有轨迹球、导航键、迷你键盘或外部键盘导航硬件的 Android 设备。
我们了解到,一旦用户触摸了触摸屏(如果安卓设备有触摸屏的话),安卓操作系统就会进入触摸模式。在这种触摸操作模式下,不会使用对焦按钮状态,但仍会安装,并将在其他不使用触摸屏的 Android 设备类型(如 ITV)上工作。这对开发人员来说意味着我们仍然需要在我们的 Android 应用开发中实现和适应焦点按钮状态。
我们在活动屏幕用户界面中添加了新的背景图像,以取代黑白背景色。接下来,我们添加了新的标签和新的参数,通过颜色匹配文本、不同的字体、文本对齐、新的图像、改进的边距和基于 alpha 通道的半透明性来升级这些屏幕,以实现更专业的视觉效果。
我们通过 android:gravity 参数了解了 Android 中 gravity 的概念,该参数允许我们使用重力将用户界面元素推到或拉到某个方向。用户界面重力是一个概念,在一个关于行星和空间的应用中,这个概念对我们来说是非常合适的。
我们了解了对比度的概念,以及在颜色亮度和饱和度以及相应的背景图像暗度之间保持高对比度的必要性,并通过为我们的主屏幕开发与背景星系图像中的颜色相匹配的文本颜色,了解了更多关于 32 位**# aarggbb颜色理论的知识。我们通过两个 RR(红色)、GG(绿色)和 BB(蓝色)通道插槽调整相对的十六进制颜色通道强度**来实现这一点,这使我们可以控制每个通道的 256 种颜色强度变化,总颜色控制相当于 16,777,216 种颜色变化。
最后,我们学习了如何在我们的 AndroidManifest.xml 文件的标记开始处的 <应用> 标签内使用 android:icon 参数来实现我们自己的定制应用图标、以及我们自己的定制图标命名方案。我们还学习了如何通过在 AndroidManifest.xml 文件中使用新的 android:label 参数来实现自定义活动屏幕标题,该文件位于我们的 < activity > 标记内,靠近我们的 Hello_World Manifest XML 标记应用定义逻辑的底部。
在下一章,我们将开始为我们的 Hello World Android 应用的用户界面屏幕添加位图或基于帧的动画和图像特效,同样,我们将使用 XML 标记(大部分)来定义,甚至实现这些动画和特效元素。
一个没有大量超酷特效的太空应用算什么?我猜是卖得不太好的那种。让我们确保你不会落入那个陷阱,确保你在下一章中通过利用 Android 的内置动画类来实现光栅动画和数字成像特效,这些动画类正是为了这个目的而提供给开发者的。**
九、Android 图像动画:使用 XML 结构的基于帧的动画
在本章中,我们将通过使用 XML 标记向应用用户界面屏幕添加动画视觉元素,将我们已经给人留下深刻印象的 Hello World 应用带到新媒体认知的下一个层次。
Android 有两种不同类型的动画,第一种,我们将在本章重点介绍的,是基于帧的或光栅动画。
光栅动画使用一系列位图(称为 cels (如果你在 2D 动画行业)或帧(如果你在电影或数字视频编辑行业)。第二种类型的动画是程序或矢量动画,我们将在第十章中介绍,因为它完全不同。
在本章中,我们将介绍基于帧的动画理论,以及在从 LDPI 到 XHDPI 的所有四种不同图像像素密度分辨率下实现动画所需的 XML 逻辑结构。这将使我们的 Hello World 应用的 UI 设计达到一个新的水平。
基于帧的动画:概念和数据优化
由于基于光栅或帧的动画是由位图图像组成的,我们在过去两章中学习的关于图形设计和数字成像的许多技术概念在本章中也同样适用,并且每一个都在光栅动画中扮演一个角色,所以在本章中你也可以更多地练习这些数字成像概念。
可以使用索引色或真彩色位图图像来创建光栅动画。这些图像可以有一个 alpha 通道,因此基于帧的动画可以利用透明度,这对我们的特效来说是个好消息。我们可以用动画做同样的合成技巧,就像我们在前两章中对静态图像做的一样。
与我们的静态图像类似,我们还需要为每种屏幕密度提供密度匹配的图像,正如我们的静态图像一样,Android 会处理哪些像素密度用于哪些屏幕,只要我们的动画单元或帧支持所有不同的像素密度级别,就像我们在本书中一直虔诚地做的那样。
静态图像优化和运动图像(动画)优化之间的主要技术差异涉及动画的帧速率。帧速率通常用每秒帧或 FPS 来描述,这是一个定义一秒钟内显示多少图像的数字,相当于图像帧在屏幕上相互替换的速度。
更高(更快)的帧速率提供了更平滑的视觉外观,但需要更多的图像数据来创建,因为动画需要有更多的帧,并且每个帧都是 PNG32 图像,需要存储在应用的 drawable-dpi 文件夹中(或者是 PNG8 图像,如果可以这样优化的话)。
你在日常生活中熟悉的新媒体素材的典型帧速率是数字视频,它以 30 FPS 运行;电影胶片,以 24 FPS 运行;或者视频游戏,运行速度为 60 FPS 。值得注意的是运动错觉可以在少至 12 FPS 的时间内实现,我们将在本章稍后的动画数据足迹优化工作流程中使用这一鲜为人知的事实。
因此,我们光栅动画的数据足迹优化过程的一部分不仅涉及我们用于动画的图像帧的色深,还涉及我们将需要用来创建运动幻觉的图像帧的总数。较少的 PNG8 帧比大量的 PNG32 帧制作的动画数据更紧凑。
在本章中,我们将仅使用几帧和索引彩色图像来制作引人注目的真实照片动画,因此您可以看到如何使用很少的数据开销,让几千字节的图像数据在您的应用中创建令人信服的特殊效果。
我们将在本章介绍的另一个基本动画概念叫做循环。正如你可能知道的,动画可以播放一次,或者它们可以无缝循环并永远播放,或者它们可以通过我们的 XML 标记指定的变量循环播放一定次数。
循环有两种:无缝循环,帧循环播放,像这样 0 、 1 、 2 、 3 、 4 、 0 、 1 、 2 、 3 、 4 和乒乓循环 像这样 0 、 1 、 2 、 3 、 4 、 3 、 2 、 1 、 0 。
在这一章中,我们将会看到这两种类型的动画循环,并且我们将会看到这两种类型对于不同类型的特效是如何有用的。
用 XML 实现帧动画:动画列表标签
在我们可以用 XML 创建定义动画帧编号及其持续时间(帧速率)的动画标记之前,我们需要做的第一件事是将可绘制素材复制到每个相应的 drawable-dpi 文件夹中,在本例中,这些素材是动画的 cels 或 frames 。
找到名为:attack virus 96 frame 0到 attackvirus96frame5 的 PNG8 文件,复制到 /res/drawable-xhdpi 文件夹,最后重命名为attackvirus0.png到attackvirus5.png共六帧动画。请注意,我们在这里仅使用了总共六个帧来实现这个动画病毒细胞效果,并且我们使用了优化的索引颜色 PNG8 文件,因此我们在所有四种分辨率密度下实现该动画的总数据量仅为总数据的 120KB 或每个动画的 30KB 平均值(这又是每帧 5KB 的数据压缩平均值)。
创建一个新的资源类型的 Android XML 文件:Drawable for Frame Animation
一旦我们的源图像动画帧数据就位,我们需要创建引用这些帧的 XML 构造,并且作为单个动画素材名称(anim_virus)被引用。右击任意一个项目 /res/drawable 文件夹,从右键菜单中选择新建 Android XML 文件,打开如图图 9-1 所示的对话框。
图 9-1。创建一个新的 Android XML 文件,名为 anim_virus.xml,根元素为<动画列表>的
在 Hello_World 项目中选择 Drawable 的资源类型,并将文件命名为 anim_virus ,然后在对话框中间的选择框中选择 < animation-list > 的根元素类型。注意在**资源类型的下拉菜单中:**有几个与动画相关的选项—不要选择它们(直到下一章),因为它们用于程序(矢量)动画,而不是基于帧(光栅)的动画,后者只利用可绘制图形(位图图像)。
这里需要注意的另一件事是,因为我们现在在图 9-1 和 9-2 中涉及到它,Android 在可绘制素材文件夹中保存帧动画素材和定义,而 Android 在动画素材文件夹中保存程序动画素材和定义。
图 9-2。将<项目>标签添加到<动画列表>标签容器中,对病毒细胞图像进行 pong 动画制作
类似地,Android 中每种不同类型的动画也有不同的类和包,所以不要混淆 Android 动画包(包含用于过程或矢量动画的类)和用于基于帧或光栅动画的 AnimationDrawable 类。
如果你想知道,程序动画使用更少的图像和更多的处理能力,因此产生更小的应用大小(更好的压缩),而帧动画使用更多的图像和更少的处理能力,因为 CPU 只是从内存中获取数据并将其放在屏幕上。
最终,帧动画使您可以控制每个像素在做什么以及在哪里做,并且在效果的结果中没有任何差异,但它可能需要应用有更多的数据。正如我们将在下一章中看到的,同时使用这两种技术可以达到一个很好的平衡。
在你点击了新的 Android XML 对话框中的 Finish 按钮之后(图 9-1 ,你现在应该有一个 anim_virus.xml shell 脚本在 Eclipse 中为你打开(图 9-2 )。
这当然是由对话框助手创建的,并且包含了 <动画列表> 标签元素的框架 XML 标记,正如你可能已经猜到的,这是一个父容器,我们将用动画帧 <项目> 标签填充它。
在我们开始通过标签用动画帧填充容器之前,有一个重要的参数需要添加到我们的父标签中,这个参数叫做 android:oneshot 参数。该关键参数定义动画是无缝循环还是简单地播放一次(一个镜头)。因为我们希望我们的病毒永远脉动,将此设置为假。
添加我们的帧动画标签,指定动画帧
每个 <项> 标签指定一个帧参考和持续时间,格式如下:
<item android:drawable="@drawable/attackvirus0" android:duration="100" />
android:drawablefilename reference 允许 Android 从正确的 drawable 文件夹中获得每个帧的正确分辨率密度版本。
android:duration 参数指定在屏幕上显示该帧的时间长度,使用一个毫秒值,在本例中为 100 。
您可能想知道这个毫秒持续时间值如何转化为我们之前了解的帧速率。数学计算起来相当简单;由于每秒钟内有一千毫秒的 ??,我们可以用 1000 除以我们的每帧毫秒值,这就得到我们的每秒帧数,或 FPS 值。在这种情况下,我们使用 10 FPS 来获得足够令人信服的振动病毒细胞。
从优化的角度来看,这次我们讨论的是 CPU 处理优化,用于创建效果的帧越少(帧速率越低,或者每帧可以分配的毫秒持续时间越多)越好。从逻辑上讲,20 FPS 的 CPU 工作量是 10 FPS 的两倍。
这是因为随着时间的推移,当我们向应用添加动画素材时,我们会要求 CPU(中央处理器)执行越来越多的任务,因此我们给 CPU 单独执行这些任务的时间越多,CPU 在执行这些任务之间就有越多的时间来执行我们要求它执行的其他任务。
从我们的 attack_virus.xml 文件 引用我们的帧动画定义 XML 文件
接下来,我们需要在现有代码中引用这个新的动画病毒细胞可绘制素材,用于我们的 activity_attack.xml 用户界面屏幕布局 xml 定义。我们观察到我们的 attackvirus ImageButton 用户界面元素当前引用了 attack_virus.xml 多状态 UI 元素定义,因此这是我们新的 anim_virus.xml 文件名必须被引用的地方,以维护按钮的多状态功能。
为此,我们必须找到(并打开)用于 attack_virus.xml 标记的标签,如图图 9-3 所示,并将我们的可绘制引用更改为现在指向新的 anim_virus XML 动画定义可绘制素材。
图 9-3。将我们的多态 attack_virus.xml 的正常状态改为引用 anim_virus.xml 文件
记下图 9-3 中 /res/drawable 文件夹下的 anim_virus.xml 文件,以及现在已经就位的新Android:drawable = " @ drawable/anim _ virus "参数。如果你现在使用运行 Android 应用工作流程并进入攻击一个星球 UI 屏幕,你会注意到你必须点击病毒一次来激活(启用)它以使病毒开始活动。
这是 Android 中动画资源的默认行为,并且有一个非常合乎逻辑的原因。通常你希望当你点击一个 UI 元素时触发一个动画,我们也将在本章中设置这些动画类型之一。
因此,在默认的 Android 动画行为中,当你点击 UI 元素时,动画就会触发,并且,如果你将 oneshot 设置为 true ,你就会得到你想要的效果。在下一章中,我们将使用 LaserCannon pulse,向您展示这种类型的 XML 设置。然而,就目前而言,我们必须弄清楚如何让这种攻击病毒始终保持活动状态,即使用户没有首先点击它。
利用 Android state_enabled 参数使我们的病毒在活动屏幕启动时活跃起来
为了让我们的病毒在屏幕启动时有动画效果,我们添加了一个 Android:state _ enabled 参数到我们的默认(正常)按钮状态项目标签,在 attack_virus.xml 文件中指定,如图 9-3 中的所示。这将该按钮状态定义为在该用户界面屏幕加载时已经启用。当你再次作为 Android 应用运行时,当用户界面屏幕出现时,我们的病毒立即活跃起来。
现在,我们已经了解了如何为 ImageButton 用户界面元素实现动画源图像,让我们更进一步,为我们的应用的母星制作一个背景用户界面元素的动画(母星显示在应用的主屏幕上)。这显示了我们的星球力场设置,当它被激活的时候。
我们使用循环动画帧,而不是 pong 动画帧(我们用于动画攻击病毒细胞的来回帧类型的帧访问)来实现这一点,因此您有实现这两种类型的动画帧访问顺序的经验。
XML 中的高级帧动画:使用动画背景
为了实现我们的 Forcefield 动画,我们需要做的第一件事是确保我们的动画帧素材就位,所以让我们将 forcefield120frame0.png、forcefield160frame0.png、forcefield240frame0.png 和 forcefield320frame0.png 分别复制到 LDPI、MDPI、HDPI 和 XHDPI /res/drawable 文件夹中。我们通过 Windows 资源管理器文件实用程序来实现这一点。
一定要把每个文件名末尾编号为 0 到 6 的七帧全部复制到它们合适的分辨率密度资源文件夹中,然后简单的全部重命名:forcefield0.png到forcefield6.png,如图图 9-4 所示。一旦完成这些,我们就为 XML 和 Java 代码做好了准备。
图 9-4。将力场动画帧复制并重命名到每个 drawable-dpi 文件夹中,以准备我们的素材
现在,我们可以通过右键单击项目文件夹并选择 New Android XML File 来获得更多实现帧动画的练习,这次是循环而不是 pong。如果你忘记了这看起来像什么,参考本章开始的图 9-1 。
将文件命名为 anim_forcefield 并使用资源类型:Drawable 和项目:Hello_World 和根元素:< animation-list > 就像我们之前做的一样。
如果你想知道我们的命名惯例,以及为什么我们要复制 Android activity_name 命名惯例,这是因为当文件被 OS 或 Eclipse 排序(按字母顺序排列)时,逻辑文件被分组在一起,所以所有 attack_names 和 anim_names 以有序的方式保持分组在一起。
在 anim_forcefield.xml 文件中添加我们的动画帧标签
您现在应该在 Eclipse 中打开一个 XML 编辑窗格,其中包含新的 anim_forcefield.xml 文件,以及一个骨架 <动画列表> 容器,我们将在其中放置指定动画帧的< item >子标签。
我们要做的第一件事是添加 android:oneshot="false" 参数,这样我们的动画就会永远循环下去。注意, false 是 oneshot 的默认值,所以如果你愿意,你可以完全省略这个参数。因为我们将在本书的不同地方改变循环参数(并学习它们),所以我总是在父容器中编写这个参数,因为这是很好的编程实践。
现在我们准备为我们的动画帧添加七个标签,使用我们之前实现 pong 动画时使用的相同的 android:drawable 和 android:duration 参数。这如图 9-5 中的所示,请注意我们的帧无缝循环,从第 6 帧到第 0 帧,帧速率为 12 FPS (80 毫秒是 1 秒的 12.5 倍)。这是它的 XML 标记:
<animation-list xmlns:android=http://schemas.android.com/apk/res/android
android:oneshot="false" >
<item android:drawable="@drawable/forcefield0" android:duration="80" />
<item android:drawable="@drawable/forcefield1" android:duration="80" />
<item android:drawable="@drawable/forcefield2" android:duration="80" />
<item android:drawable="@drawable/forcefield3" android:duration="80" />
<item android:drawable="@drawable/forcefield4" android:duration="80" />
<item android:drawable="@drawable/forcefield5" android:duration="80" />
<item android:drawable="@drawable/forcefield6" android:duration="80" />
</animation-list>
图 9-5。将每个力场动画帧的<项>标签添加到父<动画列表>容器标签
接下来是在同一个用户界面元素容器中同时使用背景图像(或者,在本例中是动画)和前景图像(称为源)的新技术,在本例中是 ImageView 标记。
在单个 UI 元素中合成:同时使用源图像和背景图像
为了完成这个合成,我们将向主屏幕末尾的 ImageView 标签添加几个新参数,这是通过 activity_main.xml 文件中的 XML 定义的。在 Eclipse 中打开这个编辑标签,添加参数**Android:background = " @ drawable/anim _ forcefield "**来引用您刚刚创建的动画列表 XML,这样您就可以引用您的 force field 动画素材中的所有帧。
我们要添加的第二个参数与背景参数一样重要,因为它允许我们推出 ImageView 容器的边界,因为我们的背景动画素材比我们的星球图像大 25%(大小的 125%)。前景(源)图像资源和背景动画资源都利用阿尔法通道;这是为了让它们无缝地组合在一起。
添加 android:padding="30dp" 参数,将 30 个设备无关像素添加到 ImageView UI 元素容器内的中。由于我们将地球图片推离了 UI 屏幕的左侧,也推离了它上面的文本字段,所以让我们也从这个 ImageView 标签中移除或删除 android:marginTop 和 android:marginLeft 标签。
图 9-6 显示了我们新的 ImageView 标签及其参数,同样数量的参数(我们添加了两个,删除了两个)实现了更高级的特殊效果。如果你想调整行星和它的力场之间的空间大小,你可以改变 android:padding DP 设置,从 24dp(紧配合)到 40dp(间隔很远)。请记住,您可以使用 30dp 或 30dip 作为您的 dip 参数,Android 将接受它。
图 9-6。引用 anim_forcefield 作为 activity_main.xml 中 imageEarth ImageView 的背景图像
填充参数在 UI 元素的内部增加空间,而不是像 margin 参数那样在外部增加空间。这可以使用图形布局编辑器很好地可视化,因为 GLE 给你指南(蓝色和绿色)每个选择的用户界面元素相对于其他用户界面元素做什么。例如,在您删除边距参数之前,图形布局编辑器视图窗格显示了靠近行星图像的蓝色选择框和显示边距设置的绿色箭头,将整个容器推离父容器(屏幕边缘)和位于其上的文本元素。现在,注意图 9-7 中的 UI 容器本身已经扩展到适合我们新的背景动画和前景图像。
图 9-7。使用 android:padding 预览 imageEarth 周围的 anim_forcefield 背景图片间距
我们可以只使用一个参数设置来改变我们的效果,因为我们使用 alpha 通道来设置一切;这样,无论我们如何设置填充或动画速度,结果都是专业的。
例如,您甚至可以在 Java 代码中将这个填充值制作成动画,或者将行星周围力场的紧密程度与其他物体与行星的接近程度联系起来。
图 9-7 显示了图形布局编辑器中的 30dp 填充设置。要根据您自己的喜好设置该参数值,请在 activity_main.xml 选项卡(查看编辑窗格的底部)和图形布局编辑器选项卡之间来回切换,以查看 DIP 设置对最终用户界面元素容器填充值的结果,在本例中,我们设置该值以影响行星在力场动画中的居中方式。截至撰写本书时,该动画还不能在 GLE 播放。要观看动画,您需要使用作为 Android 应用运行的工作流程,并启动 Nexus S 模拟器。
用 Java 编写我们的 AnimationDrawable 对象来实现我们的力场动画
在我们能够启动模拟器并看到动画之前,我们需要添加一些 Java 代码,如图 9-8 所示,将我们的 ImageView 和 AnimationDrawable 类和对象添加到我们的 MainActivity.java 类中,以便 Android 知道它们在那里。记住,设计者可以用 XML 实现 UI 设计,但是程序员通常必须使用 Java 来实现它们。
图 9-8。编写一个 setStartUpScreenAnim()方法来设置我们的 AnimationDrawable 并调用它的 start()方法
的确,Android 使得实现动画 ImageButton UI 元素成为可能,而无需编写任何 Java 代码(我将在每章的开头展示最酷和最容易实现的技巧),并且只使用 XML 标记。然而,对于 ImageView UI 元素,由于它们更多地被设计为保存图像素材而不是用作 UI 元素,我们将需要编写一些 Java 代码,以确保当您的应用启动时动画正在播放。
这对我来说也很好,因为如果可以的话,我会在每一章中介绍一些 Java 编码,让这些概念在你的脑海中保持新鲜。
让我们在我们的 MainActivity.java 类中创建一个名为 setUpScreenAnim( ) 的新 Java 方法,该方法将初始化 ImageView,设置其背景图像,并调用 AnimationDrawable 类来制作我们在此设置的背景动画素材的动画。
在 Eclipse 中找到并打开 MainActivity.java 编辑选项卡,折叠 setStartUpWorldValues()、SetStartUpScreenText()、onCreateOptionsMenu()和 onOptionsItemSelected()方法,以便我们有一些空间来编写新方法。
像我们的 setStartUpScreenText()方法一样,将新方法设为 private 和 void ,并将其命名为 setStartUpScreenAnim。代码应该如下所示:
private void setStartUpScreenAnim() { animation code will go in here }
现在我们有了一个空方法,可以编写 Java 代码语句来实现 ImageView 及其 AnimationDrawable 背景元素。
我们需要做的第一件事是使用以下代码声明、命名和实例化我们的 homePlanet ImageView 用户界面元素:
ImageView homePlanet = (ImageView)findViewById(R.id.imageEarth);
下一行代码(它在图 9-8 中被注释掉了,因为我们不需要它,因为我们在 XML 中定义了一个背景)显示了如何在 Java 中设置你的背景素材。注意,因为我们在 ImageView 标签中使用了 android:background 参数,所以我们不需要在 Java 中重复这样做。然而,如果我们没有这个 XML 背景参数,我们将需要这行代码。因此,出于学习目的,我将它包含在这里:
homePlanet.setBackgroundResource(R.id.anim_forcefield); (commented out in Figure9-8)
下一行代码做了一些繁重的工作,并使用 Android AnimationDrawable 类设置了我们的 forcefield 动画,使用了以下单行代码:
AnimationDrawable forceFieldAnimation = (AnimationDrawable)homePlanet.getBackground();
最后,现在我们的 AnimationDrawable 对象名为 forceFieldAnimation 已经设置好了,使用来保存我们的动画资源。getBackground( ) 方法调用了 homePlanet 的 ImageView 对象,我们可以使用方便的**。start( )** 方法使用下面这行非常简单的代码启动动画:
forceFieldAnimation.start();
现在,我们准备在 Nexus S 模拟器中使用作为 Android 应用运行的工作流程来测试我们的应用。一旦我们这样做,我们可以看到我们的力场动画正在我们的应用主屏幕上运行,地球现在使用填充值而不是边距值与屏幕左侧及其上方的文本元素隔开。通过这种方式,我们可以远离其他 UI 元素,并使用相同的标签参数为背景动画效果腾出空间。这都显示在图 9-9 中,尽管你必须自己运行模拟器,才能看到力场动画的运行!
图 9-9。在 Nexus S 模拟器中运行我们的 Forcefield 动画 ImageView UI 元素背景
在本节中,我们仅使用 400KB 的总 PNG8 图像资源,实现了所有四种分辨率密度的七帧动画力场效果。这相当于平均每分辨率 100KB 或平均每帧 14KB。
接下来,我们将在我们的 Configure a Planet 用户界面屏幕上实现一个更加令人印象深刻的视觉特效,因此我们在应用的每个主要用户界面屏幕上添加了帧动画(或基于图像的动画)特效。
让我们来看看如何通过内置的 Android 图像过渡特效,同时动画显示图像中的所有像素。
图像过渡是数字成像特效的基础,如变形,以及数字成像交叉渐变过渡。
全屏 XML 帧动画:背景图像过渡
这次让我们使用一个不同的工作流程来创建一个图像转换 XML 定义文件,这样您就可以看到在 Eclipse 中有不止一种方法可以做到这一点。右键单击项目的 /res/drawable 文件夹,选择新建文件菜单命令序列,弹出新建文件对话框(创建一个包含任何数据类型的文件,不仅仅是 XML 数据),如图图 9-10 所示。
图 9-10。使用新建文件对话框创建 tran_stars_galaxy XML 文件
请注意,新文件对话框会自动填充我们右键单击以访问新文件菜单序列的父文件夹,因此我们现在要做的就是指定文件名。将文件命名为 tran_stars_galaxy ,代表从恒星到星系的跃迁。
为图像转换 XML 定义设置标签
当在 Eclipse 中为 tran_stars_galaxy.xml 文件打开空白编辑标签时,键入包含 < item > 标签的 < transition > 父标签,这些标签定义了作为该图像转换 xml 定义一部分的图像可绘制元素。开始的<转换>标签包含 XML 模式地址,就像所有开始的容器标签一样,如下所示:
<transition xmlns:android="http://schemas.android.com/apk/res/android" >
在这个父图像过渡容器中,放置两个 < item > 标签,第一个用于源图像,第二个用于目标图像。<项目>标签使用 android:drawable 参数以通常的方式指向可绘制图像素材,通过对源图像的 @drawable/stars 引用和对目标图像的 @drawable/galaxy 引用。
一旦你的 tran_stars_galaxy.xml 文件看起来像我们在图 9-11 中所写的标记,我们需要做的下一件事是准备我们的现有代码来实现图像转换,打开我们名为 activity_add.xml 的 xml 文件,并使用 android:id 参数将一个 ID 值赋给我们的父 RelativeLayout 布局容器标签。
图 9-11。使用<过渡>标签父容器通过<项>标签定义数字图像过渡元素
我们添加它的原因是因为我们需要在 Java 代码中引用并在以后访问这个屏幕布局容器。这是因为这个 RelativeLayout 容器保存了用户界面屏幕的背景图像。由于这是 UI 元素(RelativeLayout 屏幕布局容器的背景图像),我们试图使用 Java 中的 image transition 对象将它渐变为新图像,因此我们必须能够通过 Java 代码中的 findViewById( ) 方法来访问它。
我们在父 RelativeLayout 容器打开标签的最末端添加了Android:id = " @+id/new _ planet _ screen ",如图图 9-12 所示。注意,标记参数的顺序没有区别;我们可以把 tag ID 参数放在前面,也可以把它放在参数列表的最后。
图 9-12。使用 android:id 参数命名 RelativeLayout 容器,以便在 Java 中引用
为 Hello_World 升级 AndroidManifest XML 中的应用 API 级别支持
为了在布局容器中使用图像过渡,我们需要做的下一件事是为我们的项目设置 AndroidManifest.xml 文件,以指定 API 级别 16 (Jelly Bean)。这是一项高级功能,需要高级 API 级别的支持才能执行。
为此,在 Eclipse 中打开包含您的 AndroidManifest.xml 文件的 Hello_World Manifest 选项卡。值得注意的是,在图 9-13 中,这是 Eclipse 项目中唯一一个不会简单显示您正在编辑的确切文件名的选项卡。您可以在底部的包资源管理器中看到这一点,其中实际的文件名显示为 AndroidManifest.xml,但是 Eclipse 在编辑窗格选项卡中将其修改为 Hello_World Manifest!请不要让这迷惑你;Eclipse 只是想变得聪明和先进。不要以为可以将 AndroidManifest.xml 文件命名为 Hello_WorldManifest.xml,否则项目将无法编译。记住,Android 操作系统是硬编码的只寻找(并找到)一个名为 AndroidManifest.xml 的引导文件,所以只使用那个文件名。
**
图 9-13。将我们的 android:minSdkVersion 调整到 API 级别 16,以允许屏幕布局背景过渡
为了指定我们使用的是 API 16 的最低 API 支持级别,以及 API 16 的当前 API 支持级别,我们为 xml 标记顶部附近的 AndroidManifest.xml 文件中的 < uses-sdk > 标记调整了这些标记参数,结果显示在图 9-13 中。
将 android:minSdkVersion 参数设置为 16 的值,表示Android 4 . 1 . 2Jelly Bean API 16 级支持。保留这个标签中的另一个参数, android:targetSdkVersion 设置为 17 的当前值,或者Android 4 . 2 . 2Jelly Bean Plus API Level 17 支持。我们的货单看起来不错!
您可能想知道这是否会降低您的应用在旧平台和设备上的功能。如果你还记得的话,在第一章中我们安装了一个 API 以及其他所有的东西,这些东西都是在旧 API 级别的 API 上模拟当前 API 级别的支持。我的猜测是,通过图像过渡来混合布局容器背景在任何 API 级别上都不会太难,所以这也可以在 Android 3.x 和 4.0 平台上工作。
这也是一种令人惊叹的应用特性,如果它不能在旧平台上工作(正确实现),它就不会发生;也就是说,背景图像过渡不会发生,但是最终用户不会意识到这一点,因为没有什么会明显出错,除非该用户已经在实际上支持该背景图像过渡的平台上利用了该应用。
在我们的 Java 代码中添加一个 TransitionDrawable 对象来实现图像转换
现在我们准备好了有趣的东西!我们需要在我们的 NewPlanet.java Java 类定义中添加三行关键代码,我在 Activity onCreate()和 setContentView()方法调用之后添加了它们,因为我们正在设置对象并将它们连接在一起。稍后在 marsImage 对象的 onClick()方法中,当用户点击 Mars 时,我们将调用这个效果。
第一行代码设置了我们的 TransitionDrawable 对象,我们将把它命名为 trans ,并且我们将引用: tran_stars_galaxy.xml 文件,该文件保存了我们的< transition >定义标记。Java 代码应该是这样的,如图图 9-14 所示:
final TransitionDrawable trans = (TransitionDrawable)getResources().getDrawable(R.id.trans_stars_galaxy);
图 9-14。在我们的 NewPlanet.java 代码中实现我们的 RelativeLayout 和 TransitionDrawable 对象
我们需要使我们的对象成为 final ,因为我们从类中更深层次的 onClick()方法内部引用它,final 关键字防止 trans 对象被编辑,本质上使它成为一个对象常量,这实际上是我们希望这个过渡效果—可用—并且每次都相同,由调用它的类中的任何方法使用。
接下来,我们需要实例化我们的 RelativeLayout 容器对象,将其命名为 newPlanetScreen ,并将其引用到我们的 activity_add.xml 布局中,在这里对其进行了定义,并在最近赋予了一个 android:id 值 new_planet_screen 。
这与我们在 Java 中实例化任何用户界面元素的方式相同,通过将它声明为一个对象,并将其命名,如下所示:
RelativeLayout newPlanetScreen = (RelativeLayout)findViewById(R.id.new_planet_screen);
既然我们已经声明了一个 trans TransitionDrawable 对象和我们的newPlanetScreenrelative layout 对象,接下来我们需要做的就是将它们连接在一起,这样它们就可以作为一个团队一起工作,来创建我们想要实现的背景图像过渡效果。
这是通过将我们最近的newPlanetScreenrelative layout 容器对象的背景元素(参数)设置为我们创建的 TransitionDrawable 对象来执行我们的图像转换来完成的。记住,我们将这个对象命名为 trans ,我们将通过使用一行简单但强大的 Java 代码来完成这个任务,这些代码通过将这两个对象连接在一起(或者更准确地说,引用 newPlanetScreen 对象内部的 trans 对象)。setBackground( ) 方法调用:
newPlanetScreen.setBackground(trans);
现在我们的数字图像特效已经用 XML(在三个 XML 文件中)和 Java 实现了,并且可以用。我们的 planet onClick()方法中的 start()方法调用,其中一个方法我们已经为我们的 imageMars 用户界面对象编写了代码,因此我们现在可以通过添加这一小段代码来测试所有这些新代码:
trans.startTransition(5000);
这行代码通过 trans TransitionDrawable 对象的调用它。start( ) 方法,它启动过渡,并传递给它一个以毫秒为单位的持续时间变量,在这个特定的例子中,我们使用了 5000 毫秒(或 5 秒)来创建当前 stars.png 图像到新 galaxy.png 图像之间的缓慢交叉淡入淡出效果。
所有这些新的 Java 代码都可以在图 9-14 中看到,在类的中间(我在 Java 代码的三个核心行周围留了一些空白),以及在底部,用浅蓝色突出显示的一行。
现在,我们可以在 Package Explorer 窗格中右键单击我们的项目文件夹,并选择 Run As Android Application 命令来启动 Nexus S 模拟器,并查看我们的背景图像转换效果!
当 Nexus S 模拟器启动且应用完成加载时,单击菜单按钮并选择新行星优先菜单选项,并加载新行星活动用户界面屏幕。然后点击火星星球图像,看星星背景慢慢交叉淡化,变成星系背景图像,如图图 9-15 所示。因为到目前为止我们已经在我们的数字图像中使用了 alpha 通道来开发这个应用,最终结果是 100%专业的无缝合成,即使在淡入淡出期间,我们的六颗行星也在我们的新背景图像上。
图 9-15。Nexus S 模拟器中显示的 galaxy.png 背景图像过渡
现在我们知道了如何使用 Android 中的 AnimationDrawable 和 TransitionDrawable 类实现位图动画和图像过渡,我们可以在下一章的过程动画中将我们的应用提升到更高的视觉效果艺术水平。
使用程序动画,其中我们使用 XML 参数修改静态图像和运动动画(是的,当它通过其帧进行运动动画时),用于平移(移动)、缩放、旋转、alpha 通道(混合)等,我们可以通过特殊效果实现更大数量级的视觉效果。
所以让我们总结一下我们在这一章中学到的关于基于光栅图像的动画的知识,然后直接进入矢量动画,这样我们就可以使用这些类和技术来合成 WOW!Hello World 应用视觉效果的因素。
摘要
在这一章中,我们利用我们在过去几章中学到的数字成像概念和技术知识,通过 Android 的帧动画功能将这些技术带入第四维时间,并应用它们来进一步增强我们的 Hello World 应用。
首先,我们学习了一些数字图像动画的基本概念,它建立在我们在前两章学习的静态图像概念的基础上。我们了解到,基于图像的动画通常被称为光栅动画或帧动画,这种类型的动画由 cels 或帧组成,它们随着时间的推移快速显示,以创建运动的幻觉。
我们学习了动画的核心概念帧速率,以及如何计算任何给定动画的 FPS 或每秒帧数。我们了解到视频游戏使用最快的帧速率 60 FPS ,而数字视频(电视)使用一半的帧速率,或 30 FPS 。电影(电影)使用的帧速率为 24 FPS ,数字媒体使用的帧速率甚至更低,范围从 10 FPS 到 20 FPS ,以节省数据空间。
我们学习了如何通过优化图像的色深(使用优化的索引彩色图像),以及创建我们正在寻找的视觉特效所需的帧数,来优化我们应用的基于帧的动画的数据足迹。
我们为 Hello_World 应用实现了几个动画效果。一个六帧动画在所有四个 Android 分辨率密度目标上仅使用了 120 千字节的图像数据,或者一个紧凑的 30KB 每分辨率屏幕密度。我们的力场动画有七帧,使用了 400KB,但这是一个包含整个星球的更大的效果,仍然平均只有 100KB 的每 Android 分辨率密度的总帧数据。
我们了解了循环的概念和 android:oneshot 参数,该参数控制动画是否无缝循环,或者是否只是播放一次然后停止。我们探索了 pong 循环的概念,其中一个循环在它的组成帧之间来回循环。我们还学习了无缝循环,其中动画的循环被预先设计为无缝循环,因此不需要反转其帧方向。
然后,我们开始在我们的 Hello World Android 应用中实现基于帧的动画,使用 <动画列表> 父标签及其子标签 <项目> ,它们定义了动画帧的可绘制素材以及每个帧的持续时间。
我们学习了 Android 的 AnimationDrawable 类,以及如何通过在 Android 应用活动的 Java 代码中声明的 AnimationDrawable 对象来实现基于图像的(可绘制的)动画。我们学会了如何称呼**。我们的 AnimationDrawable 对象上的 start( )** 方法。
我们仅使用 XML 标记代码,在我们的“攻击一个星球”活动用户界面屏幕上生动地展示了我们的攻击病毒图标,并仅使用 XML 标记和参数以及零 Java 代码,将这种脉动病毒实现为 pong 循环动画用户界面元素。
我们通过用基于 XML 的帧动画定义替换 ImageButton UI 元素的源图像来实现这一点,然后我们使用一个 android:state_enabled 参数来在活动屏幕完成加载后自动生成病毒动画。
然后我们升级到更高级的 UI 技术,使用我们的背景图像板作为用户界面元素来保存我们的动画帧,并将它们与 UI 元素的前景(源)图像合成。
我们在应用主屏幕上实现了这种 UI 内合成技术,通过使用 ImageView 标签背景参数来保存无缝循环的 7 帧力场动画,我们再次使用父标签和子标签在 XML 文件中定义了该动画,从而在地球周围添加力场。
然后我们研究了 AndroidTransitionDrawable类,以及它在我们的 Android 应用中实现数字图像过渡的能力。我们完全从头开始创建了一个 XML 文件,并实现了一个 <过渡> 标签父容器,以及两个子 <项目> 标签,它们定义了我们想要在其间过渡的图像。
因为我们想变得更巧妙(阅读:高级)并在 RelativeLayout 用户界面屏幕布局容器元素的背景参数(图像容器)中使用图像过渡,我们需要在我们的 AndroidManifest.xml 文件中将我们应用的最低 API 支持升级到 Android API 级别 16 和目标 API 级别 17。然后,在我们的MainActivity.java类中,我们仅使用四行 Java 代码将所有东西连接在一起。
在下一章中,我们将通过学习程序性或矢量动画来建立我们在本章中学习的帧动画知识,这些动画可以与帧动画结构结合使用,或者与简单的静态图像结合使用,甚至与非图像 UI 元素结合使用,例如 TextViews。**