十、构建应用主题

不管是不是图形化风格,每个应用都有一个主题。应用的主题是赋予它独特的外观和逻辑。

当一个人使用移动应用(占大多数安卓设备)时,与台式机或笔记本电脑相比,他们的行为有一些根本的区别:

  • 他们花在申请上的时间通常更少,因此耐心也更少
  • 他们通常一次只关注一个应用
  • 触摸屏设备鼓励近乎触觉的反应

安卓设备多种多样,几乎可以在所有设备上运行,包括普通的手机、平板电脑、笔记本电脑和一些台式机。安卓应用预计将在所有这些环境中运行良好,应用的主题应该仔细构建,以允许用户尽可能好地访问这些设备。

设备界面是应用主题的一部分。在台式机或笔记本电脑设备上使用鼠标时,用户可能会觉得只考虑触摸屏设计的用户界面尺寸过大(因为所有小部件都需要手指大小)。与此相反,为鼠标驱动系统设计的应用通常会包含翻转效果,这在触摸屏设备上无法正常工作。确保您的应用在所有这些不同的设备上工作的唯一方法是在构建应用的屏幕时考虑所有这些环境。

安卓本身定义了一个分类主题,尽可能地,为安卓平台构建的应用应该尝试符合或扩展这个主题,而不是重新定义它。这并不意味着您的应用必须与所有其他安卓应用的外观和行为完全相同,而是您的应用应该基于安卓制定的基本原则。

请记住,许多设备制造商为基本安卓主题定义了额外的部分,您的应用也应该这样做。

在本章中,我们将研究应用的构建,包括屏幕的设计、构造和样式。我们还将研究这个应用将如何与各种不同的设备交互,确保它看起来正确,并按照用户的期望运行。我们要构建的应用是一个计算器,既有标准计算器,也有科学计算器。该计算器将被设计成看起来更像一个物理计算器,而不是一个普通的安卓应用,并将根据运行它的设备的功能来改变它的功能。总的来说,我们将定义一个有自己一致主题的应用。

创建基本的计算器布局

为了构建这个项目,我们首先需要的是一个标准计算器的基本人像布局。这个基本布局将作为用户第一次启动应用时查看的屏幕。考虑到计算器应用的性质以及用户对它的感知,屏幕简单并且应用尽可能快地启动是非常重要的。

类型

重要的是,计算器屏幕利用功能组件占据所有可用空间,以便尽可能快地使用(按钮越大,使用越方便)。

突击测验

  1. 布局资源什么时候变成 Java 类?
    1. 当资源处理器运行时
    2. 当构建应用包时
    3. 当加载布局资源时
    4. 从不
  2. 安卓中如何引用默认没有定义的小部件?
    1. 通过使用完整的类名作为元素名
    2. 通过为 Java 包定义一个 XML 命名空间
    3. 目前这是不可能的
    4. 通过在android:package属性中指定 Java 包名
  3. View物体的默认宽度和高度是多少?
    1. 内容的大小
    2. 零乘零像素
    3. 这取决于它所处的ViewGroup
    4. 其父级的宽度和内容的高度
  4. 您将布局资源编写为 XML,它以什么格式存储?
    1. 作为原始的 XML 文本
    2. 安卓二进制 XML
    3. 布局特定的二进制格式
    4. Java 类

设计标准计算器

在开始构建计算器应用之前,最好先勾画出它的样子。这也将帮助您决定如何准确地构建屏幕。因为计算器既是一个相当古老的发明,也是人们非常熟悉的东西,所以坚持最常见的设计很重要。如果你介绍的计算器对人们来说太陌生,他们很可能没有耐心“了解”你的应用。新想法是好的(即滑动键盘),但最成功的是那些现有想法的扩展。此外,让用户清楚他们是如何工作的。下面是我们将开始构建的标准计算器屏幕的框图:

Designing a standard calculator

最大限度地利用屏幕空间很重要,所以我们会尽最大努力使按钮尽可能大。此外,我们希望将按钮稍微分开,以避免用户按下不需要的按钮。由于我们只有一个输出区域,我们将确保显示区域也足够大。

显示区域中的箭头将是一个图标,该图标将充当退格按钮,允许用户删除不想要的内容。给用户一个撤销他们所做的事情的方法总是很重要的。我们将使用一个类似于拨号器应用中使用的图标,这将保持与系统其余部分的整体一致性。这也有效地给了我们额外按钮的空间。这个用户界面不包括许多计算器的正常“记忆”功能。基本屏幕设计得尽可能简单,我们将在开发应用时引入更多功能。

行动时间——构建标准计算器

计算器的第一个布局将由一系列正常的 09 按钮组成,其中一个按钮用于各种基本算术运算——加、减、乘和除。它还将有等于按钮和小数点按钮。虽然在 Java 代码中这是一个非常容易构建的屏幕,但是我们将把这个例子完全构建为一个 XML 资源。由于这个应用将在同一个屏幕上有几个不同的排列,使用没有 Java 代码的布局资源文件将使您的生活更加容易。

  1. 首先为计算器创建一个新项目:

    java android create project -n Calculator -p Calculator -k com.packtpub.calculator -a CalculatorActivity -t 3

  2. 打开标准主布局文件/res/layout/main.xml

  3. 从文件中移除生成的布局结构。
  4. 首先声明一个垂直的 LinearLayout作为根元素来消耗屏幕上所有的可用空间:

    java <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">

  5. 声明一个 RelativeLayout,用删除取消按钮组成显示,用户可以使用该按钮删除不想要的输入:

    java <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content">

  6. RelativeLayout右侧的ImageView中使用标准安卓输入删除图标:

    java <ImageView android:id="@+id/delete" android:src="@android:drawable/ic_input_delete" android:layout_centerInParent="true" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/>

  7. RelativeLayout的左侧,创建一个TextView,它将实际显示计算器的数字状态:

    java <TextView android:id="@+id/display" android:text="0" android:layout_alignParentTop="true" android:layout_toLeftOf="@id/delete" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

  8. LinearLayout中,声明一个TableLayout,用于包含简单计算器的按钮输入:

    java <TableLayout android:id="@+id/standard_functions" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_margin="0px" android:stretchColumns="0,1,2,3">

  9. TableLayout将由四个TableRow对象组成。宣布第一个没有保证金和1layout_weight:

    java <TableRow android:layout_margin="0px" android:layout_weight="1">

  10. 右上角的Button对象需要是plus标志,我们也将其作为Button ID 的名称:

    java <Button android:id="@+id/plus" android:text="+"/>

  11. 第一行接下来的三个Button对象将是数字 123 。这些都需要身份证:

    java <Button android:id="@+id/one" android:text="1"/> <Button android:id="@+id/two" android:text="2"/> <Button android:id="@+id/three" android:text="3"/>

  12. 继续按照框图中定义的顺序用按钮声明TableRow对象。

  13. 在编辑器或 IDE 中打开CalculatorActivity.java源文件。
  14. onCreate方法中,确保Activity的内容视图设置为您刚刚定义的main布局:

    java setContentView(R.layout.main);

刚刚发生了什么?

你现在应该有一个计算器的基本用户界面;虽然它看起来仍然像一个非常通用的安卓应用,但它只是一个基础级别的开始。用户界面将需要造型工作,包括着色和一些字体变化,但基本结构现在已经完成。 RelativeLayout的使用是为了确保无论屏幕大小如何,我们都可以将删除图标正确定位在TextView的右侧。

为了让按钮消耗尽可能多的可用空间,我们告诉TableLayout拉伸它的所有列。如果 TableLayout不拉伸其列,那么它将只消耗其子级所需的水平空间(实际上与wrap_content宽度相同)。虽然TableLayout也被告知要消耗所有的垂直空间,但它的孩子将根据他们需要的空间大小来调整大小,这就是为什么按钮不会占用所有可用的屏幕空间。下图是模拟器中运行的基本计算器的屏幕截图:

What just happened?

构建计算器样式

我们真的希望这个计算器看起来更像一个真正的计算器,为此,我们需要应用一些样式。计算器目前的主题完全是标准的安卓主题,虽然它看起来和安卓系统的其他部分完全一样,但它并不真正适合这个应用。我们希望设计应用的按钮和显示区域。我们将在资源文件中定义样式值,并在布局 XML 文件中与这些值相关联。

首先,我们将定义一系列九补丁图像来创建我们自己的按钮设计。为此,我们需要三个不同的图像。第一个图像是按钮的“正常”状态,第二个图像将是按钮的“按下”状态,最后是按钮的“聚焦”状态。

突击测验

  1. 九块图像边框周围的黑线是干什么用的?
    1. 向系统提示要复制图像的哪些部分
    2. 指示要缩放图像的哪些部分以及将小部件内容放在哪里
    3. 定义图像的哪些部分包含元信息
  2. 九补丁图像可以存储为什么格式?
    1. JPEG、GIF 或 PNG 图像文件
    2. 一个嵌入了 TIFF 的 XML 文件
    3. 便携式网络图形图像
  3. draw9patch应用是做什么的?
    1. 以各种形状和大小渲染九面片图像
    2. 这是一个用于绘制九幅图像的应用
    3. 将九补丁映像的元数据生成为 XML 文件

行动时间–创建按钮图像

为了在这一部分构建按钮图像,您需要下载“GIMP”(可在http://www.gimp.org获得)。它非常适合这种类型的图像创建或操作,并且还有一个额外的优势,那就是它是开源的。

  1. 打开“Gimp”,选择文件 | 新建创建新图像。
  2. 将宽度和高度更改为38x38像素。
  3. 打开高级选项,将填充选项改为透明度,这样就没有背景色了。
  4. 为了帮助调整大小,放大到大约 800%
  5. 选择工具箱左上角的矩形工具(默认键盘快捷键为 R )。
  6. 启用圆角选项并将其设置为5
  7. 启用固定选项,并在下拉列表中选择尺寸
  8. 输入36x36作为矩形选择的固定尺寸。
  9. 将选择框放在图像画布的中心,选择框和图像边缘之间应该有一个像素的边框。
  10. 双击工具箱中的“前景色”(默认为黑色)。
  11. 在颜色选择器的十六进制符号框中输入444444
  12. 关闭颜色选择器对话框。
  13. 在工具箱中选择铲斗填充工具(默认快捷键为 Shift-B )。
  14. 点击选择框内部,用选中的颜色填充。
  15. 使用选择菜单,点击选项,移除选择框。
  16. 选择滤镜 | 装饰 | 添加斜面
  17. 厚度选项更改为3
  18. 取消选中复印工作选项,选择确定按钮。
  19. 再次从工具箱中选择矩形工具。
  20. 取消选中圆角固定选项。
  21. Use the selection tool to select a single pixel wide vertical box on the inside of the "button" shape, being careful to only select part of the content area of the button, avoiding the beveled border space:

    Time for  – creating the button images

  22. 通过将光标放在选择框的中间,将选择水平拖到图像画布的边缘(在一个像素的边框内)。

  23. 再次双击“前景”矩形。
  24. 将颜色重置为纯黑色。
  25. 选择铲斗填充选项。
  26. 在选择框内单击,创建一个单像素宽的黑色垂直线,向下延伸到图像的左侧。
  27. 在图像的右侧创建一条类似的垂直线。
  28. 在图像的顶部和底部创建一条水平的单像素高黑线。
  29. 将您的res/drawable目录中的图像保存为button.9.png,将 PNG 选项保留为默认值。
  30. 重复这个精确的过程,将444444的前景色更改为c16400,并将新图像保存为button_focus.9.png

通过翻转工具翻转图像(默认快捷键 Shift + F ,您将创建button_down.9.png图像。

刚刚发生了什么?

虽然构建图像有很多步骤,但是使用正确的工具和一些实验,从根本上来说是非常容易创建的。如果你只需要一个简单的按钮或类似的东西,那么很值得找一些关于如何使用“GIMP”或类似工具的教程。在以下链接中有很棒的在线教程:

您在上一节中保存的图像应该类似于我为计算器应用创建的以下图像:

What just happened?

行动时间-设计计算器按钮

接下来我们需要做的是使用一个选择器列表和你刚刚创建的九个补丁图像来设计计算器按钮的样式。我们还将在资源文件中定义按钮样式,这样我们就不必为每个按钮指定所有样式。为了用我们的图像替换标准按钮,我们只需要用我们为此目的创建的背景来替换它。

  1. res/drawable目录下,新建一个名为button.xml的 XML 文件,在编辑器中打开。
  2. 将文件的根元素定义为固定大小的选择器:

    java <selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true" android:variablePadding="false">

  3. 创建按下的按钮状态,作为选择器的第一个子级:

    java <item android:state_pressed="true" android:drawable="@drawable/button_down"/>

  4. 选择器的第二子应该是聚焦状态:

    java <item android:state_focused="true" android:drawable="@drawable/button_focus"/>

  5. 最终的选择器子级是通用的,是正常状态:

    java <item android:drawable="@drawable/button"/>

  6. res/values目录下新建一个名为styles.xml的文件,在编辑器中打开。

  7. styles.xml文件的根元素应该是没有名称空间声明的资源元素(在这个文件中不需要):

    java <resources>

  8. 将文件中的第一个样式定义为CalculatorButton,其父样式为默认安卓Button小部件样式:

    java <style name="CalculatorButton" parent="@android:style/Widget.Button">

  9. 将文本大小设置为好看的大字体和浅灰色:

    java <item name="android:textSize">30sp</item> <item name="android:textColor">#ffcacaca</item>

  10. 将样式的背景指定为新的button可绘制资源:

    java <item name="android:background">@drawable/button</item>

  11. 在每个Button小部件周围创建一个两像素的边框,以创建一点点间距:

    java <item name="android:layout_margin">2dp</item>

  12. 确保Button小部件消耗掉所有可用的垂直空间:

    java <item name="android:layout_height">fill_parent</item>

  13. 在编辑器中打开main.xml布局资源。

  14. 在每个Button元素上,添加一个样式属性,为它们赋予您刚刚在styles.xml文件中定义的样式:

    java <Button style="@style/CalculatorButton" android:id="@+id/multiply" android:text="*"/>

刚刚发生了什么?

我们刚刚为计算器屏幕重新设计了 T2 对象的样式。该样式是标准安卓Button小部件的子代。新的样式主要是由背景图像改变为我们之前创建的九补丁图像驱动的。为了使用新的背景图像,我们还指定了字体颜色和大小。运行时,新的计算器用户界面将如下图所示:

What just happened?

在原始代码中,指定的按钮周围没有边距,但是在新代码中,我们在自定义样式中添加了明确的边距。我们的九补丁图像在内容区域周围没有填充。

您会注意到,我们为布局中的每个Button小部件设置了样式。如前一章所述,样式属性不是安卓资源名称空间的一部分。不幸的是,安卓目前不允许我们对某个特定类的所有小部件进行风格化。相反,我们不得不为每个小部件单独设置样式,或者用相同的样式为Activity或应用中的每个小部件设置样式。作为新的Button样式的一部分,我们将一个可抽取的资源声明为<selector>资源。与选项卡结构一样,Button对象可以被设计为针对其不同状态使用不同的可绘制资源。在这种情况下,我们为Button聚焦、按压或处于正常状态的情况指定背景图像。造型仅适用于背景图像,因为新Button对象的背景是<selector>资源。

行动时间-设置显示样式

目前,数字显示看起来真的很糟糕。这主要是因为我们只是没有对它进行任何造型,目前它只是一个普通的 TextView对象。我们希望造型包含TextView对象和ImageView。当前显示如下图所示:

Time for  – styling the display

为了修复这个显示,并使其样式与我们新的Button样式保持一致,我们将创建两种不同的样式。一种是在TextViewImageView对象周围创建边框和背景,另一种是用更合适的字体为TextView小部件设置样式。

  1. 创建一个名为display_background.xml的新的可绘制资源文件,并在编辑器或 IDE 中打开它。
  2. 显示背景的根部需要是矩形:

    java <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">

  3. 声明一些填充来插入文本和图像:

    java <padding android:top="5sp" android:bottom="5sp" android:left="15sp" android:right="15sp"/>

  4. 为矩形创建纯灰色背景颜色:

    java <solid android:color="#ffcccccc"/>

  5. 指定笔画大小并将其颜色设置为白色:

    java <stroke android:width="2px" android:color="#ffffffff"/>

  6. 在编辑器或 IDE 中打开res/values/styles.xml文件。

  7. 为显示包装器添加一个新的<style>项目,并将没有父样式的新样式CalculatorDisplay命名为:

    java <style name="CalculatorDisplay"> Set the background as the display_background:<item name="android:background"> @drawable/display_background </item>

  8. 在显示包装下方创建一个小边距:

    java <item name="android:layout_marginBottom">25sp</item>

  9. 在显示屏上方添加一些填充:

    java <item name="android:layout_marginTop">50sp</item>

  10. 用名称CalculatorTextDisplay开始一个新的<style>元素,父样式应该是标准的TextView样式:

    java <style name="CalculatorTextDisplay" parent="@android:style/TextAppearance">

  11. 在新样式中,将字体设置为45像素,黑色等宽字体:

    java <item name="android:typeface">monospace</item> <item name="android:textSize">45sp</item> <item name="android:textColor">#ff000000</item>

  12. 计算器显示的文本应该是右对齐的,因此我们还将指定应用于TextView :

    java <item name="android:gravity">right</item>

    的重力 13. 在编辑器或 IDE 中打开res/layout/main.xml文件。 14. 指定RelativeLayout的样式为CalculatorDisplay :

    java <RelativeLayout style="@style/CalculatorDisplay" android:layout_width="fill_parent" android:layout_height="wrap_content">

  13. 为显示设置TextView的样式:

    java <TextView android:id="@+id/display" style="@style/CalculatorTextDisplay" android:text="0" android:layout_alignParentTop="true" android:layout_toLeftOf="@id/delete" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

刚刚发生了什么?

新样式适用于环绕TextView对象和ImageView对象的 RelativeLayout。通过设置RelativeLayout的样式,您可以有效地将TextViewImageView作为一个小部件连接在一起。如果你看下面的截图,你会发现这是如何为你的用户工作的:

What just happened?

TextView对象上方和下方的边距会缩小按钮可以使用的可用空间。在一个长的垂直空间中,按钮 通常会变长,看起来不相称,所以通过给显示区域添加边距,我们有助于使按钮保持更方形的形状。

加油英雄——添加计算器逻辑

现在,我们已经为一个简单的计算器提供了一个很好的用户界面。然而,它只不过是一个好看的用户界面。接下来要做的就是给作品增加一些逻辑。

以下是拥有功能计算器需要完成的步骤:

  1. 实现OnClickListener界面,并将其注册到用户界面上的每个Button小部件。
  2. 创建一个新的Calculator类来处理实际计算并存储计算器的非用户界面状态。
  3. 使用StringBuilder类实现当前输入值的构造和显示。
  4. 使用double数据类型实现基本计算,以适应带小数位的数字。

突击测验

  1. 从布局中选择资源字符串时,如何选择该字符串?
    1. 直接从根values串资源
    2. 从与布局在同一目录中的strings.xml文件
    3. 从与当前配置最匹配的values目录中,并包含一个带有请求名称的字符串
    4. 从与选择布局资源文件的目录具有相同选择器的values目录
  2. 放置样式资源的正确文件名是什么?
    1. values目录中的任何文件
    2. styles.xml
    3. values.xml
    4. theme.xml
  3. Java 代码中的资源选择与 XML 资源文件中的资源选择有何不同?
    1. Java 资源选择更快
    2. XML 资源只能引用与自身具有相同配置限定符集的其他资源
    3. 没有显著差异
    4. XML 资源只能引用所有资源类型的子集。

科学的景观布局

计算器的科学布局不仅仅是增加按钮,因为我们希望在设备处于横向时使用这种布局。这意味着我们的垂直空间明显减少,而标准布局会消耗大量垂直空间。为了构建这个新的用户界面,我们不仅要定义一个新的布局资源,还要为新的布局添加样式。

科学的布局还在其新按钮上使用了更复杂的文本。一些数学函数,如平方根或反余弦,有一个特定的符号应该使用。在这些情况下,我们需要使用 HTML 样式或特殊字符。幸运的是,安卓在功能和字体渲染上都完全支持 UTF 8 字符集,使得这个过程变得更加容易。

为科学布局定义字符串资源

对于科学函数,我们将把每个函数的字符串内容定义为资源字符串。这部分是为了使它们成为资源选择过程的独立部分(这总是被推荐的),但也是为了允许我们利用自动的 HTML 处理。如果您在字符串资源中使用了 HTML,如果使用Resources.getText方法、而不是通常的 Resources.getString方法访问,该 HTML 将自动被资源处理器解析。这正是TextView类加载其字符串资源的方式,使得将文本内容放入values资源文件更有吸引力。

以下是我在values目录下strings.xml文件的内容。您会注意到,HTML 标记是 HTML 3.2,而不是基于 HTML 4 的。这是因为安卓Html类不处理 HTML 4 标记,而Html类实际上是用来加载和字符串包含标记的资源。在名为strings.xmlres/values目录中创建新的资源文件,并将以下代码片段复制到新文件中:

<resources>
    <string name="inverse">1/x</string>
    <string name="square">
        x<sup><font size="10">2</font></sup>
    </string>
    <string name="cube">
        x<sup><font size="10">3</font></sup>
    </string>
    <string name="pow">
        y<sup><font size="10">x</font></sup>
    </string>
    <string name="percent">%</string>

    <string name="cos">cos</string>
    <string name="sin">sin</string>
    <string name="tan">tan</string>
    <string name="log2">
        log<sub><font size="10">2</font></sub>
    </string>
    <string name="log10">
        log<sub><font size="10">10</font></sub>
    </string>

    <string name="acos">
        cos<sup><font size="10">-1</font></sup>
    </string>
    <string name="asin">
        sin<sup><font size="10">-1</font></sup>
    </string>
    <string name="atan">
        tan<sup><font size="10">-1</font></sup>
    </string>
    <string name="log">log</string>
    <string name="log1p">log1p</string>

    <string name="e"><i>e</i></string>
 <string name="pi">π</string>
    <string name="random">rnd</string>
 <string name="sqrt"></string>
    <string name="hyp">hyp</string>
</resources>

pisqrt字符串值中的 unicode 十六进制值用于引用小写希腊圆周率符号的 unicode 字符和标准平方根符号。

造型科学布局

标准计算器布局中使用的样式不适用于科学布局。为了更改科学布局的样式,您可以将新样式添加到新的values目录中,用于景观布局。将以下代码片段复制到名为res/values-land/styles.xml的新文件中:

<resources>
    <style name="CalculatorDisplay">
        <item name="android:background">
            @drawable/display_background
        </item>
    </style>

    <style name="ScientificButton" parent="style/CalculatorButton">
        <item name="android:textSize">12sp</item>
    </style>
</resources>

前面代码片段中的第一个样式资源用于计算器的显示区域。和标准计算器一样,我们使用本章前面写的display_background形状。我们还为科学按钮定义了新的样式。科学按钮将与标准计算器按钮完全相同,只是字体小得多。由于科学按钮比标准按钮多得多,较小的字体使我们能够在屏幕上舒适地容纳更多的科学按钮。

构建科学布局

科学布局由屏幕右侧的标准计算器按钮组成,屏幕左侧有 20 个附加按钮。附加按钮代表数学函数和常数,大部分可以在java.lang.Mathjava.lang.StrictMath类中找到。下图说明了我们希望如何布局科学计算器:

Building the scientific layout

计算器显示屏上横向布局的新样式的效果将“移除”显示屏和按钮之间的边距。由于横向布局的垂直空间较少,这种填充只不过是浪费空间,为了保持合理的大小,应该将空间用于按钮。

行动时间——为科学布局编码

景观布局分为不同的子布局,以维护两个独立功能区的标识:科学功能和标准功能。用它们自己的标识值来维护它们,可以更容易地从 Java 代码中检测可用的功能。Java 代码不是根据配置来决定可用的功能,它可以使用findViewById并测试null来检查科学功能是否可用。这与 JavaScript 中的“能力测试”没有什么不同(与检查相反)。

  1. 新建一个名为res/layout-land的资源目录。
  2. 在名为main.xmllayout-land目录中创建新的布局资源 XML 文件,并在编辑器或 IDE 中打开该文件。
  3. 将新布局的根元素声明为垂直的LinearLayout,消耗所有可用的屏幕空间:

    java <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">

  4. 新布局的第一个元素是一个RelativeLayout元素,用于包装用作计算器显示的TextView和【T2:

    java <RelativeLayout style="@style/CalculatorDisplay" android:layout_width="fill_parent" android:layout_height="wrap_content">

  5. 将标准计算器布局(res/layout/main.xml)中的TextViewImageView元素复制为先前声明的RelativeLayout的两个子元素:

    java <ImageView android:id="@+id/delete" android:src="@android:drawable/ic_input_delete" android:layout_centerInParent="true" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <TextView android:id="@+id/display" style="@style/CalculatorTextDisplay" android:text="0" android:layout_alignParentTop="true" android:layout_toLeftOf="@id/delete" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

  6. 根的第二子元素LinearLayout是水平方向的LinearLayout,消耗屏幕空间的剩余部分:

    java <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent">

  7. 在新的LinearLayout子里面,声明一个新的TableLayout来填充科学按钮:

    java <TableLayout android:id="@+id/scientific_functions" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginRight="10dip">

  8. scientific_functions TableLayout内创建一个TableRow元素,包含第一行科学Button元素:

    java <TableRow android:layout_margin="0px" android:layout_weight="1">

  9. 将前五个科学功能声明为新的TableRow内的Button元素。Button标识应该与用作Button标签的资源字符串的名称相同:

    java <Button style="@style/ScientificButton" android:id="@+id/inverse" android:text="@string/inverse"/>

  10. 第一排科学Button小部件包含inversesquarecubepowpercent

  11. 用第二行包含cossintanlog2log10的科学Button小部件创建一个TableRow
  12. 第三个TableRow中的第三个科学Button小部件应该是acosasinatanloglog1p
  13. 第四个也是最后一个Button小部件TableRow应该是epirandomsqrthyp
  14. 这就是所有的科学函数,现在在标准函数的子元素LinearLayout中创建另一个TableLayout:

    java <TableLayout android:id="@+id/standard_functions" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_margin="0px" android:stretchColumns="0,1,2,3">

  15. res/layout/main.xmlstandard_functions TableLayout的内容复制到新的TableLayout元素中。

刚刚发生了什么?

在前面的布局中,我们重用了我们在标准计算器布局中创建的许多基础,增加了一个新的TableLayout结构来包含科学功能。新的TableLayout的尺寸为wrap_content的宽度,并且将仅消耗容纳所有Button部件所需的水平空间。这两个TableLayout元素之间的另一个主要区别是科学表不会扩展它的列,因为这实际上等于将其大小调整为fill_parent,并且没有为标准函数留出空间。

您还会注意到,在用于创建科学Button标签的字符串资源中,那些使用 HTML 标记的资源不使用 XML 转义实体(例如&lt;&gt;)。这是资源编译器的主要指示符,表明字符串资源包含标记,应该以不同的方式使用。这种用法要求放入字符串资源中的所有 HTML 标记必须既符合 HTML 3.2 规范,又保持有效的 XML 内容。

为了测试新的横向布局,您需要定义一个横向屏幕大小的仿真器设备,或者在物理设备上运行应用。在模拟器中创建一个虚拟设备可以通过安卓软件开发工具包安装的工具目录中的安卓应用来完成,该工具与创建框架项目的工具相同。以下是运行在物理安卓设备上的新布局的截图:

What just happened?

拥有一个围棋英雄——在现有布局中使用包含

前面的布局有几个可以重用的标准布局元素。这是将这些元素提取到它们自己的布局文件中,然后利用include元素将它们放入两个特定布局资源中的好时机。布局包括的信息可以在第 5 章发展非线性布局中找到。

  1. 创建一个display.xml布局资源来包含带有计算器显示的RelativeLayout,并将其包含在main.xml布局资源文件中的适当位置。
  2. 创建一个standard_buttons.xml布局资源来包含名为standard_functionsTableLayout,并将其包含在main.xml布局资源文件中的适当位置。

处理活动重启

当设备改变方向时,屏幕上的 CalculatorActivity对象以新的方向重新启动。在这个应用中,重启会导致一个严重的问题:计算器的状态丢失。正如第 4 章利用活动和意图中所讨论的,在安卓系统中,有时你需要控制你的应用状态——关机前保存它,当Activity再次启动时恢复它。

您需要覆盖 Activity.onSaveInstanceState方法,将计算器的当前状态存储在提供的Bundle中。当由于配置改变而重新启动时,这个 Bundle对象将通过onCreate方法提供给你。在您的onCreate方法中,在从中恢复保存参数之前,检查以确保所提供的Bundle对象是非空的。

加油英雄——实现科学计算逻辑

计算器目前应该能够通过标准计算按钮运行。然而,新的科学功能没有任何支持结构。此外,如果您重新定位设备以在科学布局和标准布局之间进行更改,任何“正在进行”的计算都将丢失。

为使科学计算按预期运行,需要完成的步骤如下:

  1. 执行onSaveInstanceState将计算状态保存到提供的Bundle对象中。
  2. 执行onCreate方法,从其提供的Bundle对象中恢复保存的状态(假设给出了一个)。
  3. 将使科学的Button小部件按预期运行所需的功能添加到您之前编写的Calculator类中。

支持硬件键盘

我们在这里开发的计算器现在是一个很棒的屏幕安卓计算器 应用,具有您期望的简单和科学的功能。然而,如果一个设备有硬件键盘,用户可能会期望能够使用它,而目前他们不能。此外,如果设备没有触摸屏,点击屏幕上的按钮将很快变得令人沮丧。我们需要为应用实现硬件键盘支持。

实现硬件键盘处理代码只有在你完成了“玩英雄”部分并构建了一个Calculator类来执行所需功能的情况下才对你有用。为了处理硬件键盘事件,您将使用 KeyEvent.Callback界面中声明的方法。Activity类已经实现了KeyEvent.Callback接口,并为所有方法提供了默认处理。对于这些关键事件的处理,我们只需要覆盖 onKeyDown方法。

对于这个onKeyDown实现,最好通过检查KeyEvent的标志来确保按键事件来自硬件键盘。在自己处理之前,把它传递给你的父类也是一个好主意。最后,如果你在安卓 2.0 (API-Level 5)或更高版本上工作,你应该在处理它之前检查KeyEvent没有被取消(这也是KeyEvent标志之一)。以下是我实现onKeyDown方法的代码片段:

@Override
public boolean onKeyDown(
        final int keyCode,
        final KeyEvent event) {

    super.onKeyDown(keyCode, event);

    boolean handled = false;

    if((event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) == 0) {
 switch(keyCode) {
 case KeyEvent.KEYCODE_0:
 calculator.zero();
 handled = true;
 break;
 case KeyEvent.KEYCODE_1:
 calculator.one();
 handled = true;
 break;
 // Cases for each of the handles keys
 }

        display.setText(calculator.getCurrentDisplay());
    }

    return handled;
}

前面的代码片段为硬件键盘上可以按下的每个不同的键调用一个方法。

如果你的安卓设备没有硬件键盘,你可以使用模拟器测试这段代码——你的电脑键盘和模拟器显示屏右侧的屏幕键盘都被模拟器归类为硬件键盘。

添加显示动画

目前,该应用具备了一个伟大的计算器应用的所有素质。然而,显示器目前只是一个简单的TextView对象。为了提高用户体验,当计算器操作改变时,或者按下“等于”Button时,我们应该使用一个ViewSwitcher对象将TextView换出。

行动时间-动画显示

为了给计算器显示构建一个漂亮的滑出-滑入动画,我们需要定义我们自己的动画,并将它们绑定到一个ViewSwitcher对象。这还需要我们对 Java 代码进行更改,以便处理新的机制。由于我们不希望每次输入新的数字时视图都有动画效果,我们将直接更改当前屏幕上的TextView

  1. 在名为slide_out_top.xmlres/anim目录中创建新的 XML 资源文件,并在编辑器或 IDE 中打开它。
  2. 将从0%100%的 y 平移动画声明为动画资源中的唯一元素:

    java <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromYDelta="0%" android:toYDelta="-100%" android:duration="300"/>

  3. 在名为slide_in_bottom.xmlres/anim目录中创建新的 XML 资源文件,并在编辑器或 IDE 中打开该文件。

  4. 将从100%0%的 y 平移动画声明为动画资源中的唯一元素:

    java <translate xmlns:android="http://schemas.android.com/apk/res/android" android:fromYDelta="100%" android:toYDelta="0%" android:duration="300"/>

  5. 在 IDE 的编辑器中打开您的display.xml文件,或者打开这两个main.xml文件,您应该打开其中的哪一个将取决于您是否完成了“开始英雄-布局包括”。

  6. 在用于显示的RelativeLayout中,使用两个新的动画资源将名为displayTextView替换为ViewSwitcher元素:

    java <ViewSwitcher android:id="@+id/display" android:inAnimation="@anim/slide_in_bottom" android:outAnimation="@anim/slide_out_top" android:layout_alignParentTop="true" android:layout_toLeftOf="@id/delete" android:layout_width="fill_parent" android:layout_height="wrap_content">

  7. 作为ViewSwitcher的子元素,用CalculatorTextDisplay样式声明两个TextView元素:

    java <TextView style="@style/CalculatorTextDisplay" android:text="0" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

  8. 两个TextView元素将彼此相同。

刚刚发生了什么?

ViewSwitcher用于显示将导致任何现有的 Java 代码崩溃,因为 Java 代码将期望对象是某种类型的TextView。相反,您需要做的是使用ViewSwitcher.getCurrentView更新显示,而不是使用ViewSwitcher本身。

当使用一个操作Button时,例如,乘或等于Button,您将想要将下一个显示内容放在ViewSwitcher.getNextView小部件上,然后调用 ViewSwitcher.showNext()方法。随着新内容从显示屏底部出现,数字向上消失的动画是一个简单但解释性的动画。它在计算器应用中也非常常用,这意味着用户通常会感到舒适。

在这个应用中,动画与其说有用,不如说更吸引人。但是,如果您在计算器中实现了历史堆栈,当用户按下“后退”Button时,动画可能会反转。计算器中的历史堆栈是一种非常有用的结构,因为它允许一次又一次地运行相同计算的微小变化。

有一个围棋英雄——四舍五入

这个计算器应用在这一点上是相当完整的。它已经被设计好了,并且有一些令人赏心悦目的东西和预期的功能。然而,它确实有一些警告——科学的计算布局在小屏幕设备上不能很好地工作。以下截图是在小屏幕手机上以科学布局运行的应用:

Have a go hero – rounding off

上图还演示了一些设备如何应用主题。为了确保应用在所有设备上都能正常工作:

  1. 为小屏幕设备定义一个新的values目录。
  2. 在目录中创建新的styles.xml文件,其样式的边距和填充比默认值少。
  3. 在横向小屏幕设备上时,减小display字体的大小。

大多数成功的安卓应用项目都会遵循这种舍入过程。这是一个在各种不同的仿真器配置和设备上试用应用的问题,然后利用资源加载器来确保应用在尽可能多的设备上运行良好。

总结

创建应用主题是一个新应用成功的关键部分,无论是运行在安卓、桌面还是网络上。我们已经探索了如何利用安卓提供的各种工具来保持应用的一致性,以保持它的用户友好性。

应用的主题和外观远远超出了简单的样式。你个人越是使用你的应用,你就越会看到颜色略有不同的地方,或者过渡动画会有所不同。每一个微小的差异都让应用变得真正用户友好,因为它让应用感觉完美。

虽然运行在数百种截然不同的设备上,但安卓让开发人员可以轻松地让他们的应用保持运行,就好像它们是专门为硬件而构建的一样。资源加载器系统是安卓系统中最关键的结构之一,如果不利用它,可能会对应用造成自杀。

我强烈建议您熟悉现有的安卓应用,以及其他移动设备上的应用。知道如何驱动一个像样的图像处理应用也很有帮助。在你开始构建屏幕之前,先画一个图表,在你开始编码之前,铅笔和纸通常是了解用户界面的最好方法。

仔细考虑在哪里可以使用现有的安卓图标和样式,以及在哪里想要替换或扩展它们。您总是希望保持应用的一致性,但是添加一些华而不实的吸引眼球的东西通常会让应用脱颖而出。

结合了 XML 资源和 Java 语言,Android 是一个非常引人注目的设计和编码平台。它被广泛部署,并拥有出色的开发人员支持。有数十家硬件制造商生产各种形状和大小的安卓设备,还有数千名开发人员制作应用。

在本书中,我们致力于利用安卓平台来构建以用户为中心、易于使用且外观美观的应用。安卓平台和安卓市场允许被俘虏的观众和新想法的大量曝光。从这里开始,您应该能够将自己独特的想法和工作添加到安卓生态系统中。做过的事总能做得更好,没做过的事都有人等着。无论你是团队的一员,还是晚上在阁楼上埋头做下一件大事,一个成功应用的关键是一个出色的用户界面。