Android官方文档中文版

Android 简介

要了解应用的工作原理,请从应用基础知识开始入手。

要立即开始编码,请仔细阅读构建您的第一个应用

Android 提供了一个内容丰富的应用框架,支持您在 Java 语言环境中为移动设备开发创新应用和游戏。在左侧导航窗格列出的文档中,提供了有关如何使用各种 Android API 开发应用的详细信息。

如果您是 Android 应用开发新手,则需了解以下有关 Android 应用框架的基本概念,这一点至关重要:

应用提供多个入口点

Android 应用都是将各种可单独调用的不同组件加以组合开发而成。例如,组件可以是为用户界面提供一个屏幕的单个“Activity”,也可以是在后台独立执行工作的“服务”。

您可以使用 intent 从一个组件启动另一个组件。甚至,您还可以启动不同应用中的组件,例如,启动地图应用中的 Activity 以显示地址。此模式可为单个应用提供多个入口点,并使任何应用均能够像用户“默认设置”一样处理其他应用可能调用的操作。

了解详情

应用可适应不同的设备

Android 提供了一个自适应应用框架,可用以为不同的设备配置提供独特的资源。例如,您可以针对不同的屏幕尺寸创建不同的 XML 布局文件,系统将根据当前设备的屏幕尺寸确定要应用的布局。

如有任何应用功能需要相机等特定的硬件,则可在运行时查询设备功能的可用性。如有必要,您还可以声明您的应用所必需的功能,使 Google Play 商店等应用市场不得在不支持这些功能的设备上安装您的应用。

了解详情

 

构建您的第一个应用

欢迎从事 Android 应用开发!

本课将介绍如何构建您的第一个 Android 应用。您将学习如何使用 Android Studio 创建 Android 项目和运行可调试版本的应用。您还将了解一些 Android 应用设计的基础知识,包括如何构建简单的界面和处理用户输入。

在开始本课的学习之前,请下载并安装 Android Studio

创建 Android 项目

本课将向您介绍如何使用 Android Studio 创建新的 Android 项目并说明该项目中的一些文件。

  1. 在 Welcome to Android Studio 窗口中,点击 Start a new Android Studio project

    或者如果您已打开项目,请选择 File > New Project

  2. 在 New Project 屏幕中,输入以下值:
    • Application Name:“My First App”
    • Company Domain:“example.com”

    您可能需要更改项目位置,但无需更改其他选项。

  3. 点击 Next
  4. 在 Target Android Devices 屏幕中,保留默认值并点击 Next
  5. 在 Add an Activity to Mobile 屏幕中,选择 Empty Activity,然后点击 Next
  6. 在 Configure Activity 屏幕中,保留默认值并点击 Finish

经过一些处理后,Android Studio 将打开 IDE。

下面让我们花一点时间回顾一下最重要的文件。

首先,请确保已打开 Project 窗口(选择 View > Tool Windows > Project),并从窗口顶部的下拉列表中选择 Android 视图。随后,您可以看到下列文件:

app > java > com.example.myfirstapp > MainActivity.java

这是主 Activity(您的应用的入口点)。当您构建和运行应用时,系统会启动此 Activity 的实例并加载其布局。

app > res > layout > activity_main.xml

此 XML 文件会定义 Activity 界面的布局。它包含一个带有文本“Hello world!”的 TextView 元素。

app > manifests > AndroidManifest.xml

manifest 文件描述应用的基本特性并定义其每个组件。

Gradle Scripts > build.gradle

您会看到具有此名称的两个文件:一个用于项目,一个用于“应用”模块。每个模块均有自己的 build.gradle 文件,但此项目当前仅有一个模块。您将主要使用模块的 build.gradle 文件配置 Gradle 工具编译和构建您的应用的方式。如需了解有关此文件的更多信息,请参阅配置构建

要运行该应用,请继续学习下一课

运行您的应用

上一课中,您创建了一个可以显示“Hello World”的 Android 项目。现在,您可以在真实设备或模拟器上运行应用。

在真实设备上运行

按照以下步骤设置您的设备:

  1. 使用一根 USB 电缆将您的设备连接到您的开发机器。如果您是在 Windows 上开发,可能需要为您的设备安装相应的 USB 驱动程序
  2. 按照以下步骤操作,在 Developer options 中启用 USB debugging

    首先,您必须启用开发者选项:

    1. 打开 Settings 应用。
    2. (仅在 Android 8.0 或更高版本上)选择 System
    3. 滚动到底部,然后选择 About phone
    4. 滚动到底部,点按 Build number 7 次。
    5. 返回上一屏幕,在底部附近可找到 Developer options

    打开 Developer options,然后向下滚动以找到并启用 USB debugging

按照以下步骤操作,在您的设备上运行应用:

  1. 在 Android Studio 中,点击 Project 窗口中的 app 模块,然后选择 Run > Run(或点击工具栏中的 Run  )。
  2. 在 Select Deployment Target 窗口中,选择您的设备,然后点击 OK

Android Studio 会在您连接的设备上安装并启动应用。

至此,“hello world”将在您的设备上运行!要开始开发应用,请继续学习下一课

在模拟器上运行

按照以下步骤操作,在模拟器上运行应用:

  1. 在 Android Studio 中,点击 Project 窗口中的 app 模块,然后选择 Run > Run(或点击工具栏中的 Run  )。
  2. 在 Select Deployment Target 窗口中,点击 Create New Virtual Device
  3. 在 Select Hardware 屏幕中,选择电话设备(如 Pixel),然后点击 Next
  4. 在 System Image 屏幕中,选择具有最高 API 级别的版本。如果您未安装该版本,将显示一个 Download 链接,因此,请点击该链接并完成下载。
  5. 点击 Next
  6. 在 Android Virtual Device (AVD) 屏幕上,保留所有设置不变,然后点击 Finish
  7. 返回到 Select Deployment Target 对话框中,选择您刚刚创建的设备,然后点击 OK

Android Studio 会在模拟器上安装并启动应用。

至此,“hello world”将在模拟器上运行!要开始开发应用,请继续学习下一课

构建简单的界面

在本课中,您将使用 Android Studio Layout Editor 创建一个布局,其中包括一个文本框和一个按钮。在下一课中,您会将文本框的内容发送到另一个 Activity,让应用响应按钮点按操作。

图 1.最终布局的屏幕截图

Android 应用的界面使用布局ViewGroup 对象)和微件View 对象)层次结构构建。布局是一种不可见的容器,用于控制其子视图在屏幕上的位置。微件是界面组件,例如按钮和文本框。

图 2.ViewGroup 对象如何在布局中形成分支并容纳 View 对象的图解

Android 为 ViewGroup 和 View 类提供了一个 XML 词汇,因此您的大多数界面都在 XML 文件中定义。不过,本课将向您介绍如何使用 Android Studio Layout Editor 创建布局,而不是教您编写一些 XML 代码。在布局编辑器中,只需拖放视图即可构建布局,大大简化了构建工作。

打开布局编辑器

:本课假设您使用 Android Studio 3.0,并且已完成上一课创建 Android 项目的学习。

首先,请按照以下步骤设置您的工作区:

  1. 在 Android Studio 的 Project 窗口中,打开 app > res > layout > activity_main.xml
  2. 要为布局编辑器留出更多空间,请选择 View > Tool Windows > Project 以隐藏 Project 窗口,或者点击 Android Studio 左侧的 Project  )。
  3. 如果您的编辑器显示 XML 源代码,请点击窗口底部的 Design 标签。
  4. 点击 Select Design Surface  并选择 Blueprint
  5. 点击工具栏中的 Show  并确保选中 Show Constraints
  6. 确保 Autoconnect 关闭。工具栏中的提示应为 Turn On Autoconnect  (因为它现在处于关闭状态)。
  7. 点击工具栏中的 Default Margins,  然后选择 16(您稍后仍然可以调整每个视图的外边距)。
  8. 点击工具栏中的 Device in Editor,  然后选择 Pixel XL

您的编辑器现在应如图 3 所示。

图 3.显示 activity_main.xml 的布局编辑器

左下方的 Component Tree 窗口显示布局的视图层次结构。在本例中,根视图是 ConstraintLayout,仅包含一个 TextView 对象。

ConstraintLayout 是一种布局,它根据同级视图和父布局的约束条件为每个视图定义位置。这样一来,您可以创建具有扁平视图层次结构的简单布局和复杂布局。也就是说,它可以避免对嵌套布局(布局内的布局,如图 2 所示)的需求,嵌套布局会增加绘制界面所需的时间。

图 4.ConstraintLayout 内放置的两个视图的图解

例如,您可以声明以下布局(在图 4 中):

  • 视图 A 显示在父布局上方 16dp 处。
  • 视图 A 显示在父布局左侧 16dp 处。
  • 视图 B 显示在视图 A 右侧 16dp 处。
  • 视图 B 与视图 A 的顶部对齐。

在后面几部分中,您将构建一个与此布局类似的布局。

添加一个文本框

图 5.文本框被限制在父布局的顶部和左侧

  1. 首先,您需要移除布局中已有的内容。因此,请点击 Component Tree 窗口中的 TextView,然后按 Delete。
  2. 在左侧的 Palette 窗口中,点击左侧窗格中的 Text,然后将 Plain Text 拖放到设计编辑器中靠近布局顶部的位置。这是一个接受纯文本输入的 EditText 微件。
  3. 在设计编辑器中点击视图。现在,您可以在每个角上看到大小调整手柄(正方形),并在每个边上看到约束锚点(圆形)。

    为了能更好地控制,您可能需要使用工具栏中的按钮放大编辑器。

  4. 点击并按住顶边上的锚点,将其向上拖动,直至锚点吸附到布局的顶部,然后释放。这是一个约束条件 - 它指定视图应位于距离布局顶部 16dp 的位置(因为您将默认外边距设置为 16dp)。
  5. 类似地,从视图左侧创建一个约束条件,将其限制在布局左侧。

结果应如图 5 中的屏幕截图所示。

添加一个按钮

图 6.按钮被限制在文本框及其基线的右侧

  1. 在 Palette 窗口中,点击左侧窗格中的 Widgets,然后将 Button 拖放到设计编辑器中靠近右侧的位置。
  2. 从按钮左侧创建一个约束条件,将其限制在文本框的右侧。
  3. 要在水平对齐中约束视图,您需要在文本基线之间创建一个约束条件。因此,请点击按钮,然后点击 Edit Baseline, 它将显示在设计编辑器中选定视图的正下方。基线锚点显示在按钮内部。点击并按住此锚点,然后将其拖动到文本框中显示的基线锚点。

结果应如图 6 中的屏幕截图所示。

:您还可以使用顶部或底部边缘创建水平对齐,不过,按钮在其图像周围包含内边距,因此如果您按照这种方式对齐这些视图,视觉对齐将是错误的。

更改界面字符串

要预览界面,请点击工具栏中的 Select Design Surface  并选择 Design。请注意,文本输入使用“Name”预填充,按钮标记为“Button”。现在,您将更改这些字符串。

  1. 打开 Project 窗口,然后打开 app > res > values > strings.xml

    这是一个字符串资源文件,您应在这个文件中指定所有界面字符串。这样您可以在一个位置管理所有界面字符串,让字符串的查找、更新和本地化变得更加容易(与您的布局或应用代码中的硬编码字符串相比)。

  2. 点击编辑器窗口顶部的 Open editor。这将打开 Translations Editor,它为添加和编辑您的默认字符串提供了一个简单的界面,并且有助于保持您的所有已翻译字符串井然有序。
  3. 图 7.用于添加新字符串的对话框

    点击 Add Key,  以文本框“提示文本”的形式创建新字符串。
    1. 为键名输入“edit_message”。
    2. 为值输入“Enter a message”。
    3. 点击 OK
  4. 再添加一个名称为“button_send”并且值为“Send”的键。

现在,您可以为每个视图设置这些字符串。点击标签栏中的 activity_main.xml 以返回布局文件,然后按照以下步骤添加字符串:

  1. 点击布局中的文本框,如果 Attributes 窗口在右侧还未显示,请点击右侧边栏上的 Attributes  。
  2. 找到 text 属性(当前设为“Name”)并删除值。
  3. 找到 hint 属性,然后点击文本框右侧的 Pick a Resource  。在出现的对话框中,双击列表中的 edit_message
  4. 现在,点击布局中的按钮,找到 text 属性,点击 Pick a Resource, 然后选择 button_send

让文本框大小灵活调整

要创建一个可以适应不同屏幕尺寸的布局,您现在将让文本框拉伸以填充剩余的所有水平空间(扣除按钮和所有外边距后的空间)。

首先,请点击工具栏中的 Show  并选择 Blueprint

图 8.点击 Center Horizontally 的结果

图 9.点击以将宽度更改为 Match Constraints

图 10.现在,文本框将拉伸以填充剩余空间

  1. 选择两个视图(点击一个,按住 Shift 并点击另一个),然后右键点击任何一个视图并选择 Chain > Create Horizontal Chain

    是两个或更多视图之间的双向约束条件,它让您可以采用一致的方式放置链接的视图。

  2. 选择按钮并打开 Attributes 窗口。使用 Attributes 窗口顶部的视图检查器将右外边距设置为 16。
  3. 现在,点击文本框以查看其属性。点击两次宽度指示器,确保将其设置为 Match Constraints,如图 9 中的标注 1 所示。

    “Match constraints”表示宽度将延长以符合水平约束条件和外边距的定义。因此,文本框将拉伸以填充水平空间(扣除按钮和所有外边距后的空间)。

现在,布局已经完成,应如图 10 所示。

如果您的布局没有按照预计的效果显示,请点击下方链接,查看您的 XML 代码应当具有的外观,并将其与您在 Text 标签中看到的代码进行比较。(如果您的属性以不同顺序显示,没有问题。)

查看最终布局 XML

如需了解有关链和您可以对 ConstraintLayout 进行的所有其他操作的详细信息,请阅读使用 ConstraintLayout 构建自适应界面

运行应用

如果已在上一课中将您的应用安装到设备上,只需点击工具栏中的 Apply Changes,  使用新布局更新应用。或者点击 Run,  安装并运行应用。

按钮仍然没有响应。要在点按按钮时启动另一个 Activity,请继续学习下一课

启动另一个 Activity

完成上一课的学习后,您已构建了一个应用,它可以显示一个 Activity(单一屏幕)并带有一个文本字段和一个按钮。在本课中,您将向 MainActivity 添加一些代码,这些代码可在用户点按 Send 时启动一个新的 Activity 来显示消息。

:本课假设您使用 Android Studio 3.0。

响应 Send 按钮

按照以下步骤操作,在 MainActivity.java 中添加一个由按钮调用的函数:

  1. 在文件 app > java > com.example.myfirstapp > MainActivity.java 中,添加 sendMessage() 函数存根,如下所示:
     
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }

        /** Called when the user taps the Send button */
        public void sendMessage(View view) {
            // Do something in response to button
        }

    }

    您可能会看到一条错误,因为 Android Studio 无法解析用作函数参数的 View 类。因此,请点击,将您的光标放置在 View 声明上,然后按 Alt + Enter(在 Mac 上,则按 Option + Return),执行快速修复。(如果出现一个菜单,请选择 Import class。)

  2. 现在,返回到 activity_main.xml 文件,从按钮调用此函数:
    1. 在布局编辑器中点击以选择按钮。
    2. 在 Attributes 窗口中,找到 onClick 属性并从下拉列表中选择 sendMessage [MainActivity]

现在,当点按按钮时,系统将调用 sendMessage() 函数。

记下此函数中的详细信息,要让系统将此函数视为与 android:onClick 属性兼容,需要这些详细信息。具体来说,函数必须声明以下内容:

  • 公共访问
  • 空返回值
  • 以 View 作为唯一参数(它是之前点击的 View 对象)

接下来,您需要填写此函数以读取文本字段的内容,并将该文本传递给另一个 Activity。

构建一个 Intent

Intent 是指在相互独立的组件(如两个 Activity)之间提供运行时绑定功能的对象。Intent 表示一个应用“执行某项操作的意向”。您可以将 intent 用于各种任务,但在本课中,您的 intent 用于启动另一个 Activity。

在 MainActivity.java 中,添加 EXTRA_MESSAGE 常量和 sendMessage() 代码,如此处所示:

 

public class MainActivity extends AppCompatActivity {
    public static final String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    /** Called when the user taps the Send button */
    public void sendMessage(View view) {
        Intent intent = new Intent(this, DisplayMessageActivity.class);
        EditText editText = (EditText) findViewById(R.id.editText);
        String message = editText.getText().toString();
        intent.putExtra(EXTRA_MESSAGE, message);
        startActivity(intent);

    }
}

Android Studio 将再次遇到 Cannot resolve symbol 错误,因此,请按 Alt + Enter(在 Mac 上,则按 Option + Return)。您的导入应按如下所示方式结束:

 

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

DisplayMessageActivity 仍有错误,但没关系;您将在下一部分中修复该错误。

下面是 sendMessage() 中的操作:

  • Intent 构造函数采用两个参数:
    • Context 是第一个参数(之所以使用 this 是因为 Activity 类是 Context 的子类)
    • 应用组件的 Class,系统应将 Intent(在本例中,为应启动的 Activity)传递至该类。
  • putExtra() 函数将 EditText 的值添加到 intent。Intent 能够以名为 extra 的键值对形式携带数据类型。您的键是一个公共常量 EXTRA_MESSAGE,因为下一个 Activity 将使用该键来检索文本值。为 intent extra 定义键时最好使用应用的软件包名称作为前缀。这可以确保在您的应用与其他应用交互时这些键始终保持唯一。
  • startActivity() 函数将启动 Intent 指定的 DisplayMessageActivity 实例。现在,您需要创建该类。

创建第二个 Activity

  1. 在 Project 窗口中,右键点击 app 文件夹并选择 New > Activity > Empty Activity
  2. 在 Configure Activity 窗口中,为 Activity Name 输入“DisplayMessageActivity”,然后点击 Finish(保留所有其他属性设置为默认值)。

Android Studio 会自动执行三项操作:

  • 创建 DisplayMessageActivity.java 文件。
  • 创建对应的 activity_display_message.xml 布局文件。
  • 在 AndroidManifest.xml 中添加必需的 <activity> 元素。

如果运行应用并在第一个 Activity 上点按按钮,将启动第二个 Activity,但它为空。这是因为第二个 Activity 使用模板提供的空布局。

添加文本视图

图 1.位于布局顶部中心的文本视图

新 Activity 包括一个空白的布局文件,因此,现在您需要在消息将要显示的位置添加一个文本视图。

  1. 打开文件 app > res > layout > activity_display_message.xml
  2. 点击工具栏中的 Turn On Autoconnect (Autoconnect 随后应启用,如图 1 所示)。
  3. 在 Palette 窗口中,点击 Text,然后将 TextView 拖动到布局中 - 将其放置在靠近布局顶部中心的位置,使其吸附到出现的垂直线上。Autoconnect 将添加左侧和右侧约束条件,在水平中心放置视图。
  4. 再从文本视图的顶部创建一个约束条件,将其限制在布局顶部,让它看起来如图 1 所示。

也可以在 Attributes 窗口中展开 textAppearance 并更改属性(例如 textSize 和 textColor),对文本样式进行一些调整。

显示消息

现在,您将修改第二个 Activity,以显示第一个 Activity 传递的消息。

  1. 在 DisplayMessageActivity.java 中,向 onCreate() 函数添加下列代码:
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_display_message);
       
        // Get the Intent that started this activity and extract the string
        Intent intent = getIntent();
        String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);

        // Capture the layout's TextView and set the string as its text
        TextView textView = findViewById(R.id.textView);
        textView.setText(message);

    }
  2. 按 Alt + Enter(在 Mac 上,则按 Option + Return)导入缺少的类。您的导入应按如下所示方式结束:
     
    import android.content.Intent;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.TextView;

添加向上导航

您的应用中不是主入口的每个屏幕(所有不是主屏幕的屏幕)都应提供导航,以便用户在应用栏中点按“向上”按钮后可以返回应用层次结构中的逻辑父屏幕。

您需要做的全部工作是在 AndroidManifest.xml 文件中声明哪个 Activity 是逻辑父项。因此,请打开 app > manifests > AndroidManifest.xml 处的文件,找到 DisplayMessageActivity 的 <activity> 标记,然后将其替换为以下代码:

 

<activity android:name=".DisplayMessageActivity"
          android:parentActivityName=".MainActivity" >
    <!-- The meta-data tag is required if you support API level 15 and lower -->
    <meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value=".MainActivity" />
</activity>

Android 系统现在会在应用栏中自动添加“向上”按钮。

运行应用

现在,点击工具栏中的 Apply Changes,再次运行应用  。当应用打开后,在文本字段中键入一条消息,点按 Send 以在第二个 Activity 中查看显示的消息。

图 2.两个 Activity 的屏幕截图

就这么简单,您的第一个 Android 应用已成功诞生!

要继续学习 Android 应用开发的基础知识,请点击下面的链接来学习下一课。

应用基础知识

Android 应用采用 Java 编程语言编写。Android SDK 工具将您的代码 — 连同任何数据和资源文件 — 编译到一个 APK:Android 软件包,即带有 .apk 后缀的存档文件中。一个 APK 文件包含 Android 应用的所有内容,它是基于 Android 系统的设备用来安装应用的文件。

安装到设备后,每个 Android 应用都运行在自己的安全沙箱内:

  • Android 操作系统是一种多用户 Linux 系统,其中的每个应用都是一个不同的用户;
  • 默认情况下,系统会为每个应用分配一个唯一的 Linux 用户 ID(该 ID 仅由系统使用,应用并不知晓)。系统为应用中的所有文件设置权限,使得只有分配给该应用的用户 ID 才能访问这些文件;
  • 每个进程都具有自己的虚拟机 (VM),因此应用代码是在与其他应用隔离的环境中运行;
  • 默认情况下,每个应用都在其自己的 Linux 进程内运行。Android 会在需要执行任何应用组件时启动该进程,然后在不再需要该进程或系统必须为其他应用恢复内存时关闭该进程。

Android 系统可以通过这种方式实现最小权限原则。也就是说,默认情况下,每个应用都只能访问执行其工作所需的组件,而不能访问其他组件。 这样便营造出一个非常安全的环境,在这个环境中,应用无法访问系统中其未获得权限的部分。

不过,应用仍然可以通过一些途径与其他应用共享数据以及访问系统服务:

  • 可以安排两个应用共享同一 Linux 用户 ID,在这种情况下,它们能够相互访问彼此的文件。 为了节省系统资源,可以安排具有相同用户 ID 的应用在同一 Linux 进程中运行,并共享同一 VM(应用还必须使用相同的证书签署)。
  • 应用可以请求访问设备数据(如用户的联系人、短信、可装载存储装置 [SD 卡]、相机、蓝牙等)的权限。 用户必须明确授予这些权限。 如需了解详细信息,请参阅 使用系统权限

以上内容阐述了有关 Android 应用在系统内存在方式的基础知识。本文的其余部分将向您介绍以下内容:

  • 用于定义应用的核心框架组件
  • 您用来声明组件和应用必需设备功能的清单文件
  • 与应用代码分离并允许您的应用针对各种设备配置适当优化其行为的资源

应用组件

应用组件是 Android 应用的基本构建基块。每个组件都是一个不同的点,系统可以通过它进入您的应用。 并非所有组件都是用户的实际入口点,有些组件相互依赖,但每个组件都以独立实体形式存在,并发挥特定作用 — 每个组件都是唯一的构建基块,有助于定义应用的总体行为。

共有四种不同的应用组件类型。每种类型都服务于不同的目的,并且具有定义组件的创建和销毁方式的不同生命周期。

以下便是这四种应用组件类型:

Activity

Activity 表示具有用户界面的单一屏幕。例如,电子邮件应用可能具有一个显示新电子邮件列表的 Activity、一个用于撰写电子邮件的 Activity 以及一个用于阅读电子邮件的 Activity。 尽管这些 Activity 通过协作在电子邮件应用中形成了一种紧密结合的用户体验,但每一个 Activity 都独立于其他 Activity 而存在。 因此,其他应用可以启动其中任何一个 Activity(如果电子邮件应用允许)。 例如,相机应用可以启动电子邮件应用内用于撰写新电子邮件的 Activity,以便用户共享图片。

Activity 作为 Activity 的子类实现,您可以在 Activity 开发者指南中了解有关它的更多详情。

服务

服务是一种在后台运行的组件,用于执行长时间运行的操作或为远程进程执行作业。 服务不提供用户界面。 例如,当用户位于其他应用中时,服务可能在后台播放音乐或者通过网络获取数据,但不会阻断用户与 Activity 的交互。 诸如 Activity 等其他组件可以启动服务,让其运行或与其绑定以便与其进行交互。

服务作为 Service 的子类实现,您可以在服务开发者指南中了解有关它的更多详情。

内容提供程序

内容提供程序管理一组共享的应用数据。您可以将数据存储在文件系统、SQLite 数据库、网络上或您的应用可以访问的任何其他永久性存储位置。 其他应用可以通过内容提供程序查询数据,甚至修改数据(如果内容提供程序允许)。 例如,Android 系统可提供管理用户联系人信息的内容提供程序。 因此,任何具有适当权限的应用都可以查询内容提供程序的某一部分(如 ContactsContract.Data),以读取和写入有关特定人员的信息。

内容提供程序也适用于读取和写入您的应用不共享的私有数据。 例如,记事本示例应用使用内容提供程序来保存笔记。

内容提供程序作为 ContentProvider 的子类实现,并且必须实现让其他应用能够执行事务的一组标准 API。 如需了解详细信息,请参阅内容提供程序开发者指南。

广播接收器

广播接收器是一种用于响应系统范围广播通知的组件。 许多广播都是由系统发起的 — 例如,通知屏幕已关闭、电池电量不足或已拍摄照片的广播。应用也可以发起广播 — 例如,通知其他应用某些数据已下载至设备,并且可供其使用。 尽管广播接收器不会显示用户界面,但它们可以创建状态栏通知,在发生广播事件时提醒用户。 但广播接收器更常见的用途只是作为通向其他组件的“通道”,设计用于执行极少量的工作。 例如,它可能会基于事件发起一项服务来执行某项工作。

广播接收器作为 BroadcastReceiver 的子类实现,并且每条广播都作为 Intent 对象进行传递。 如需了解详细信息,请参阅 BroadcastReceiver 类。

Android 系统设计的独特之处在于,任何应用都可以启动其他应用的组件。 例如,如果您想让用户使用设备的相机拍摄照片,很可能有另一个应用可以执行该操作,那么您的应用就可以利用该应用,而不是开发一个 Activity 来自行拍摄照片。 您不需要集成甚至链接到该相机应用的代码,而是只需启动拍摄照片的相机应用中的 Activity。 完成拍摄时,系统甚至会将照片返回您的应用,以便您使用。对用户而言,就好像相机真正是您应用的组成部分。

当系统启动某个组件时,会启动该应用的进程(如果尚未运行),并实例化该组件所需的类。 例如,如果您的应用启动相机应用中拍摄照片的 Activity,则该 Activity 会在属于相机应用的进程,而不是您的应用的进程中运行。因此,与大多数其他系统上的应用不同,Android 应用并没有单一入口点(例如,没有 main() 函数)。

由于系统在单独的进程中运行每个应用,且其文件权限会限制对其他应用的访问,因此您的应用无法直接启动其他应用中的组件, 但 Android 系统却可以。因此,要想启动其他应用中的组件,您必须向系统传递一则消息,说明您想启动特定组件的 Intent。 系统随后便会为您启动该组件。

启动组件

四种组件类型中的三种 — Activity、服务和广播接收器 — 通过名为 Intent 的异步消息进行启动。Intent 会在运行时将各个组件相互绑定(您可以将 Intent 视为从其他组件请求操作的信使),无论组件属于您的应用还是其他应用。

Intent 使用 Intent 对象创建,它定义的消息用于启动特定组件或特定类型的组件 — Intent 可以是显式的,也可以是隐式的。

对于 Activity 和服务, Intent 定义要执行的操作(例如,“查看”或“发送”某个内容),并且可以指定要执行操作的数据的 URI(以及正在启动的组件可能需要了解的信息)。 例如, Intent 传达的请求可以是启动一个显示图像或打开网页的 Activity。 在某些情况下,您可以启动 Activity 来接收结果,在这种情况下,Activity 也会在 Intent 中返回结果(例如,您可以发出一个 Intent,让用户选取某位联系人并将其返回给您 — 返回 Intent 包括指向所选联系人的 URI)。

对于广播接收器, Intent 只会定义要广播的通知(例如,指示设备电池电量不足的广播只包括指示“电池电量不足”的已知操作字符串)。

Intent 不会启动另一个组件类型 - 内容提供程序,后者会在成为 ContentResolver 的请求目标时启动。 内容解析程序通过内容提供程序处理所有直接事务,使得通过提供程序执行事务的组件可以无需执行事务,而是改为在 ContentResolver 对象上调用方法。 这会在内容提供程序与请求信息的组件之间留出一个抽象层(以确保安全)。

每种类型的组件有不同的启动方法:

如需了解有关 Intent 用法的详细信息,请参阅 Intent 和 Intent 过滤器文档。 以下文档中还提供了有关启动特定组件的详细信息: Activity服务BroadcastReceiver 和内容提供程序

清单文件

在 Android 系统启动应用组件之前,系统必须通过读取应用的 AndroidManifest.xml 文件(“清单”文件)确认组件存在。 您的应用必须在此文件中声明其所有组件,该文件必须位于应用项目目录的根目录中。

除了声明应用的组件外,清单文件还有许多其他作用,如:

  • 确定应用需要的任何用户权限,如互联网访问权限或对用户联系人的读取权限
  • 根据应用使用的 API,声明应用所需的最低 API 级别
  • 声明应用使用或需要的硬件和软件功能,如相机、蓝牙服务或多点触摸屏幕
  • 应用需要链接的 API 库(Android 框架 API 除外),如 Google 地图库
  • 其他功能

声明组件

清单文件的主要任务是告知系统有关应用组件的信息。例如,清单文件可以像下面这样声明 Activity:

 

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application android:icon="@drawable/app_icon.png" ... >
        <activity android:name="com.example.project.ExampleActivity"
                  android:label="@string/example_label" ... >
        </activity>
        ...
    </application>
</manifest>

在 <application> 元素中,android:icon 属性指向标识应用的图标所对应的资源。

在 <activity> 元素中,android:name 属性指定 Activity 子类的完全限定类名,android:label 属性指定用作 Activity 的用户可见标签的字符串。

您必须通过以下方式声明所有应用组件:

您包括在源代码中,但未在清单文件中声明的 Activity、服务和内容提供程序对系统不可见,因此也永远不会运行。 不过,广播接收器可以在清单文件中声明或在代码中动态创建(如 BroadcastReceiver 对象)并通过调用 registerReceiver() 在系统中注册。

如需了解有关如何为您的应用构建清单文件的详细信息,请参阅 AndroidManifest.xml 文件文档。

声明组件功能

如上文启动组件中所述,您可以使用 Intent 来启动 Activity、服务和广播接收器。 您可以通过在 Intent 中显式命名目标组件(使用组件类名)来执行此操作。 不过,Intent 的真正强大之处在于隐式 Intent 概念。 隐式 Intent 的作用无非是描述要执行的操作类型(还可选择描述您想执行的操作所针对的数据),让系统能够在设备上找到可执行该操作的组件,并启动该组件。 如果有多个组件可以执行 Intent 所描述的操作,则由用户选择使用哪一个组件。

系统通过将接收到的 Intent 与设备上的其他应用的清单文件中提供的 Intent 过滤器进行比较来确定可以响应 Intent 的组件。

当您在应用的清单文件中声明 Activity 时,可以选择性地加入声明 Activity 功能的 Intent 过滤器,以便响应来自其他应用的 Intent。 您可以通过将 <intent-filter> 元素作为组件声明元素的子项进行添加来为您的组件声明 Intent 过滤器。

例如,如果您开发的电子邮件应用包含一个用于撰写新电子邮件的 Activity,则可以像下面这样声明一个 Intent 过滤器来响应“send” Intent(以发送新电子邮件):

 

<manifest ... >
    ...
    <application ... >
        <activity android:name="com.example.project.ComposeEmailActivity">
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <data android:type="*/*" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
    </application>
</manifest>

然后,如果另一个应用创建了一个包含ACTION_SEND 操作的 Intent,并将其传递到 startActivity(),则系统可能会启动您的 Activity,以便用户能够草拟并发送电子邮件。

如需了解有关创建 Intent 过滤器的详细信息,请参阅 Intent 和 Intent 过滤器文档。

声明应用要求

基于 Android 系统的设备多种多样,并非所有设备都提供相同的特性和功能。 为防止将您的应用安装在缺少应用所需特性的设备上,您必须通过在清单文件中声明设备和软件要求,为您的应用支持的设备类型明确定义一个配置文件。 其中的大多数声明只是为了提供信息,系统不会读取它们,但 Google Play 等外部服务会读取它们,以便当用户在其设备中搜索应用时为用户提供过滤功能。

例如,如果您的应用需要相机,并使用 Android 2.1(API 级别 7)中引入的 API,您应该像下面这样在清单文件中以要求形式声明这些信息:

 

<manifest ... >
    <uses-feature android:name="android.hardware.camera.any"
                  android:required="true" />
    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" />
    ...
</manifest>

现在,没有相机且 Android 版本低于 2.1 的设备将无法从 Google Play 安装您的应用。

不过,您也可以声明您的应用使用相机,但并不要求必须使用。 在这种情况下,您的应用必须将 required 属性设置为 "false",并在运行时检查设备是否具有相机,然后根据需要停用任何相机功能。

设备兼容性文档中提供了有关如何管理应用与不同设备兼容性的详细信息。

应用资源

Android 应用并非只包含代码 — 它还需要与源代码分离的资源,如图像、音频文件以及任何与应用的视觉呈现有关的内容。 例如,您应该通过 XML 文件定义 Activity 用户界面的动画、菜单、样式、颜色和布局。 使用应用资源能够在不修改代码的情况下轻松地更新应用的各种特性,并可通过提供备用资源集让您能够针对各种设备配置(如不同的语言和屏幕尺寸)优化您的应用。

对于您的 Android 项目中包括的每一项资源,SDK 构建工具都会定义一个唯一的整型 ID,您可以利用它来引用应用代码或 XML 中定义的其他资源中的资源。 例如,如果您的应用包含一个名为 logo.png 的图像文件(保存在 res/drawable/目录中),则 SDK 工具会生成一个名为 R.drawable.logo 的资源 ID,您可以利用它来引用该图像并将其插入您的用户界面。

提供与源代码分离的资源的其中一个最重要优点在于,您可以提供针对不同设备配置的备用资源。 例如,通过在 XML 中定义 UI 字符串,您可以将字符串翻译为其他语言,并将这些字符串保存在单独的文件中。 然后,Android 系统会根据向资源目录名称追加的语言限定符(如为法语字符串值追加 res/values-fr/)和用户的语言设置,对您的 UI 应用相应的语言字符串。

Android 支持许多不同的备用资源限定符。限定符是一种加入到资源目录名称中,用来定义这些资源适用的设备配置的简短字符串。 再举一例,您应该经常会根据设备的屏幕方向和尺寸为 Activity 创建不同的布局。 例如,当设备屏幕为纵向(长型)时,您可能想要一种垂直排列按钮的布局;但当屏幕为横向(宽型)时,应按水平方向排列按钮。 要想根据方向更改布局,您可以定义两种不同的布局,然后对每个布局的目录名称应用相应的限定符。 然后,系统会根据当前设备方向自动应用相应的布局。

如需了解有关可以在应用中包括的不同资源类型以及如何针对不同设备配置创建备用资源的详细信息,请阅读提供资源

Intent 和 Intent 过滤器

有关如何使用 Intent API 来启动应用组件(如 Activity 和服务)以及如何使您的应用组件可供其他应用使用的信息。

Activity

有关如何创建 Activity 类实例的信息,该类可在您的应用内提供一个具有用户界面的独立屏幕。

提供资源

有关如何通过适当构建 Android 应用来使应用资源与应用代码分离的信息,包括如何针对特定设备配置提供备用资源。

您可能还对以下内容感兴趣:

设备兼容性

有关 Android 在不同设备类型上工作方式的信息,并介绍了如何针对不同设备优化您的应用,或如何限制您的应用在不同设备上的可用性。

系统权限

有关 Android 如何通过一种权限系统来限制应用对特定 API 访问权限的信息,该系统要求征得用户同意,才允许您的应用使用这些 API。

提供资源

您应该始终外部化应用资源,例如图像和代码中的字符串,这样有利于您单独维护这些资源。 此外,您还应该为特定设备配置提供备用资源,方法是将它们分组到专门命名的资源目录中。 在运行时,Android 会根据当前配置使用适当的资源。例如,您可能需要根据屏幕尺寸提供不同的 UI 布局,或者根据语言设置提供不同的字符串。

外部化应用资源后,即可使用在项目 R 类中生成的资源 ID 访问这些资源。有关如何在应用中使用资源,我们将在访问资源中讨论。 本文档介绍如何对 Android 项目中的资源进行分组,以及如何为特定的设备配置提供备用资源。

分组资源类型

您应将各种资源放入项目 res/ 目录的特定子目录下。例如,以下是一个简单项目的文件层次结构:

 

MyProject/ src/ MyActivity.java res/ drawable/ graphic.png layout/ main.xml info.xml mipmap/ icon.png values/ strings.xml

正如您在此示例中所看到的那样,res/ 目录包含所有资源(在子目录下):一个图像资源、两个布局资源、启动器图标的 mipmap/ 目录以及一个字符串资源文件。资源目录名称非常重要,将在表 1 中进行介绍。

:如需了解有关使用 mipmap 文件夹的详细信息,请参阅管理项目概览

表 1. 项目 res/ 目录内支持的资源目录。

目录资源类型
animator/用于定义属性动画的 XML 文件。
anim/定义渐变动画的 XML 文件。(属性动画也可以保存在此目录中,但是为了区分这两种类型,属性动画首选 animator/目录。)
color/用于定义颜色状态列表的 XML 文件。请参阅颜色状态列表资源
drawable/

位图文件(.png.9.png.jpg.gif)或编译为以下可绘制对象资源子类型的 XML 文件:

  • 位图文件
  • 九宫格(可调整大小的位图)
  • 状态列表
  • 形状
  • 动画可绘制对象
  • 其他可绘制对象

请参阅 可绘制对象资源

mipmap/适用于不同启动器图标密度的可绘制对象文件。如需了解有关使用 mipmap/ 文件夹管理启动器图标的详细信息,请参阅管理项目概览
layout/用于定义用户界面布局的 XML 文件。 请参阅布局资源
menu/用于定义应用菜单(如选项菜单、上下文菜单或子菜单)的 XML 文件。请参阅菜单资源
raw/

要以原始形式保存的任意文件。要使用原始 InputStream 打开这些资源,请使用资源 ID(即 R.raw.filename)调用 Resources.openRawResource()

但是,如需访问原始文件名和文件层次结构,则可以考虑将某些资源保存在 assets/ 目录下(而不是 res/raw/)。assets/ 中的文件没有资源 ID,因此您只能使用 AssetManager 读取这些文件。

values/

包含字符串、整型数和颜色等简单值的 XML 文件。

其他 res/ 子目录中的 XML 资源文件是根据 XML 文件名定义单个资源,而 values/ 目录中的文件可描述多个资源。对于此目录中的文件,<resources> 元素的每个子元素均定义一个资源。例如,<string> 元素创建 R.string 资源,<color> 元素创建 R.color 资源。

由于每个资源均用其自己的 XML 元素定义,因此您可以根据自己的需要命名文件,并将不同的资源类型放在一个文件中。但是,为了清晰起见,您可能需要将独特的资源类型放在不同的文件中。 例如,对于可在此目录中创建的资源,下面给出了相应的文件名约定:

请参阅字符串资源样式资源更多资源类型

xml/可以在运行时通过调用 Resources.getXML() 读取的任意 XML 文件。各种 XML 配置文件(如可搜索配置)都必须保存在此处。

注意:切勿将资源文件直接保存在 res/ 目录内,这会导致出现编译错误。

如需了解有关某些资源类型的详细信息,请参阅资源类型文档。

保存在表 1 中定义的子目录下的资源是“默认”资源。即,这些资源定义应用的默认设计和内容。但是,采用 Android 技术的不同设备类型可能需要不同类型的资源。例如,如果设备的屏幕尺寸大于标准屏幕,则应提供不同的布局资源,以充分利用额外的屏幕空间。 或者,如果设备的语言设置不同,则应提供不同的字符串资源,以转换用户界面中的文本。 要为不同的设备配置提供这些不同资源,除了默认资源以外,您还需要提供备用资源。

提供备用资源

图 1. 两种不同的设备,均使用不同的布局资源。

几乎每个应用都应提供备用资源以支持特定的设备配置。 例如,对于不同的屏幕密度和语言,您应分别包括备用可绘制对象资源和备用字符串资源。 在运行时,Android 会检测当前设备配置并为应用加载合适的资源。

为一组资源指定特定于配置的备用资源:

  1. 在 res/ 中创建一个以 <resources_name>-<config_qualifier> 形式命名的新目录。
    • <resources_name> 是相应默认资源的目录名称(如表 1 中所定义)。
    • <qualifier> 是指定要使用这些资源的各个配置的名称(如表 2 中所定义)。

    您可以追加多个 <qualifier>。以短划线将其分隔。

    注意:追加多个限定符时,必须按照表 2 中列出的相同顺序放置它们。如果限定符的顺序错误,则该资源将被忽略。

  2. 将相应的备用资源保存在此新目录下。这些资源文件的名称必须与默认资源文件完全一样。

例如,以下是一些默认资源和备用资源:

 

res/ drawable/ icon.png background.png drawable-hdpi/ icon.png background.png

hdpi 限定符表示该目录中的资源适用于屏幕密度较高的设备。其中每个可绘制对象目录中的图像已针对特定的屏幕密度调整大小,但是文件名完全相同。 这样一来,用于引用 icon.png 或 background.png 图像的资源 ID 始终相同,但是 Android 会通过将设备配置信息与资源目录名称中的限定符进行比较,选择最符合当前设备的各个资源版本。

Android 支持若干配置限定符,您可以通过使用短划线分隔每个限定符,向一个目录名称添加多个限定符。表 2 按优先顺序列出了有效的配置限定符;如果对资源目录使用多个限定符,则必须按照表中列出的顺序将它们添加到目录名称。

表 2. 配置限定符名称。

配置限定符值说明
MCC 和 MNC示例:
mcc310
mcc310-mnc004
mcc208-mnc00
等等

移动国家代码 (MCC),(可选)后跟设备 SIM 卡中的移动网络代码 (MNC)。例如,mcc310 是指美国的任一运营商,mcc310-mnc004 是指美国的 Verizon 公司,mcc208-mnc00 是指法国的 Orange 公司。

如果设备使用无线电连接(GSM 手机),则 MCC 和 MNC 值来自 SIM 卡。

也可以单独使用 MCC(例如,将国家/地区特定的合法资源包括在应用中)。如果只需根据语言指定,则改用“语言和区域”限定符(稍后进行介绍)。 如果决定使用 MCC 和 MNC 限定符,请谨慎执行此操作并测试限定符是否按预期工作。

另请参阅配置字段 mcc 和 mnc,这两个字段分别表示当前的移动国家代码和移动网络代码。

语言和区域示例:
en
fr
en-rUS
fr-rFR
fr-rCA
等等

语言通过由两个字母组成的 ISO 639-1 语言代码定义,可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前带小写字母“r”)。

这些代码不区分大小写;r 前缀用于区分区域码。 不能单独指定区域。

如果用户更改系统设置中的语言,它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更

有关针对其他语言本地化应用的完整指南,请参阅本地化

另请参阅 locale 配置字段,该字段表示当前的语言区域。

布局方向ldrtl
ldltr

应用的布局方向。ldrtl 是指“布局方向从右到左”。ldltr 是指“布局方向从左到右”,这是默认的隐式值。

它适用于布局、图片或值等任何资源。

例如,若要针对阿拉伯语提供某种特定布局,并针对任何其他“从右到左”语言(如波斯语或希伯来语)提供某种通用布局,则可编码如下:

 
res/ layout/ main.xml (Default layout) layout-ar/ main.xml (Specific layout for Arabic) layout-ldrtl/ main.xml (Any "right-to-left" language, except for Arabic, because the "ar" language qualifier has a higher precedence.)

:要为应用启用从右到左的布局功能,必须将 supportsRtl 设置为 "true",并将 targetSdkVersion 设置为 17 或更高版本。

此项为 API 级别 17 中新增配置。

smallestWidthsw<N>dp

示例:
sw320dp
sw600dp
sw720dp
等等

屏幕的基本尺寸,由可用屏幕区域的最小尺寸指定。 具体来说,设备的 smallestWidth 是屏幕可用高度和宽度的最小尺寸(您也可以将其视为屏幕的“最小可能宽度”)。无论屏幕的当前方向如何,您均可使用此限定符确保应用 UI 的可用宽度至少为 <N>dp。

例如,如果布局要求屏幕区域的最小尺寸始终至少为 600dp,则可使用此限定符创建布局资源 res/layout-sw600dp/。仅当可用屏幕的最小尺寸至少为 600dp 时,系统才会使用这些资源,而不考虑 600dp 所代表的边是用户所认为的高度还是宽度。smallestWidth 是设备的固定屏幕尺寸特性;设备的 smallestWidth 不会随屏幕方向的变化而改变

设备的 smallestWidth 将屏幕装饰元素和系统 UI 考虑在内。例如,如果设备的屏幕上有一些永久性 UI 元素占据沿 smallestWidth 轴的空间,则系统会声明 smallestWidth 小于实际屏幕尺寸,因为这些屏幕像素不适用于您的 UI。 因此,使用的值应该是布局所需要的实际最小尺寸(通常,无论屏幕的当前方向如何,此值都是布局支持的“最小宽度”)。

以下是一些可用于普通屏幕尺寸的值:

  • 320,适用于屏幕配置如下的设备:
    • 240x320 ldpi(QVGA 手机)
    • 320x480 mdpi(手机)
    • 480x800 hdpi(高密度手机)
  • 480,适用于 480x800 mdpi 之类的屏幕(平板电脑/手机)。
  • 600,适用于 600x1024 mdpi 之类的屏幕(7 英寸平板电脑)。
  • 720,适用于 720x1280 mdpi 之类的屏幕(10 英寸平板电脑)。

应用为多个资源目录提供不同的 smallestWidth 限定符值时,系统会使用最接近(但未超出)设备 smallestWidth 的值。

此项为 API 级别 13 中新增配置。

另请参阅 android:requiresSmallestWidthDp 属性和 smallestScreenWidthDp 配置字段,前者声明与应用兼容的最小 smallestWidth;后者存放设备的 smallestWidth 值。

如需了解有关设计不同屏幕和使用此限定符的详细信息,请参阅支持多种屏幕开发者指南。

可用宽度w<N>dp

示例:
w720dp
w1024dp
等等

指定资源应该使用的最小可用屏幕宽度,以 dp 为单位,由 <N> 值定义。在横向和纵向之间切换时,为了匹配当前实际宽度,此配置值也会随之发生变化。

应用为多个资源目录提供不同的此配置值时,系统会使用最接近(但未超出)设备当前屏幕宽度的值。 此处的值考虑到了屏幕装饰元素,因此如果设备显示屏的左边缘或右边缘上有一些永久性 UI 元素,考虑到这些 UI 元素,它会使用小于实际屏幕尺寸的宽度值,这样会减少应用的可用空间。

此项为 API 级别 13 中新增配置。

另请参阅 screenWidthDp 配置字段,该字段存放当前屏幕宽度。

如需了解有关设计不同屏幕和使用此限定符的详细信息,请参阅支持多种屏幕开发者指南。

可用高度h<N>dp

示例:
h720dp
h1024dp
等等

指定资源应该使用的最小可用屏幕高度,以“dp”为单位,由 <N> 值定义。 在横向和纵向之间切换时,为了匹配当前实际高度,此配置值也会随之发生变化。

应用为多个资源目录提供不同的此配置值时,系统会使用最接近(但未超出)设备当前屏幕高度的值。 此处的值考虑到了屏幕装饰元素,因此如果设备显示屏的上边缘或下边缘有一些永久性 UI 元素,考虑到这些 UI 元素,同时为减少应用的可用空间,它会使用小于实际屏幕尺寸的高度值。 非固定的屏幕装饰元素(例如,全屏时可隐藏的手机状态栏)并不在考虑范围内,标题栏或操作栏等窗口装饰也不在考虑范围内,因此应用必须准备好处理稍小于其所指定值的空间。

此项为 API 级别 13 中新增配置。

另请参阅 screenHeightDp 配置字段,该字段存放当前屏幕宽度。

如需了解有关设计不同屏幕和使用此限定符的详细信息,请参阅支持多种屏幕开发者指南。

屏幕尺寸small
normal
large
xlarge
  • small:尺寸类似于低密度 QVGA 屏幕的屏幕。小屏幕的最小布局尺寸约为 320x426 dp 单位。例如,QVGA 低密度屏幕和 VGA 高密度屏幕。
  • normal:尺寸类似于中等密度 HVGA 屏幕的屏幕。标准屏幕的最小布局尺寸约为 320x470 dp 单位。例如,WQVGA 低密度屏幕、HVGA 中等密度屏幕、WVGA 高密度屏幕。
  • large:尺寸类似于中等密度 VGA 屏幕的屏幕。 大屏幕的最小布局尺寸约为 480x640 dp 单位。 例如,VGA 和 WVGA 中等密度屏幕。
  • xlarge:明显大于传统中等密度 HVGA 屏幕的屏幕。超大屏幕的最小布局尺寸约为 720x960 dp 单位。在大多数情况下,屏幕超大的设备体积过大,不能放进口袋,最常见的是平板式设备。 API 级别 9 中的新增配置。

:使用尺寸限定符并不表示资源仅适用于该尺寸的屏幕。 如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能使用其中最匹配的资源。

注意:如果所有资源均使用大于当前屏幕的尺寸限定符,则系统会使用这些资源,并且应用在运行时将会崩溃(例如,如果所有布局资源均用 xlarge 限定符标记,但设备是标准尺寸的屏幕)。

此项为 API 级别 4 中新增配置。

如需了解详细信息,请参阅支持多种屏幕

另请参阅 screenLayout 配置字段,该字段表示屏幕是小尺寸、标准尺寸还是大尺寸。

屏幕纵横比long
notlong
  • long:宽屏,如 WQVGA、WVGA、FWVGA
  • notlong:非宽屏,如 QVGA、HVGA 和 VGA

此项为 API 级别 4 中新增配置。

它完全基于屏幕的纵横比(宽屏较宽),而与屏幕方向无关。

另请参阅 screenLayout 配置字段,该字段指示屏幕是否为宽屏。

圆形屏幕round
notround
  • round:圆形屏幕,例如圆形可穿戴式设备
  • notround:方形屏幕,例如手机或平板电脑

此项为 API 级别 23 中新增配置。

另请参阅 isScreenRound() 配置方法,其指示屏幕是否为宽屏。

屏幕方向port
land
  • port:设备处于纵向(垂直)
  • land:设备处于横向(水平)

如果用户旋转屏幕,它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更

另请参阅 orientation 配置字段,该字段指示当前的设备方向。

UI 模式car
desk
television
appliancewatch
  • car:设备正在车载手机座上显示
  • desk:设备正在桌面手机座上显示
  • television:设备正在电视上显示,为用户提供“十英尺”体验,其 UI 位于远离用户的大屏幕上,主要面向方向键或其他非指针式交互
  • appliance:设备用作不带显示屏的装置
  • watch:设备配有显示屏,戴在手腕上

此项为 API 级别 8 中新增配置,API 13 中新增电视配置,API 20 中新增手表配置。

如需了解应用在设备插入手机座或从中移除时的响应方式,请阅读确定并监控插接状态和类型

如果用户将设备放入手机座中,它有可能在应用生命周期中发生改变。 可以使用 UiModeManager启用或禁用其中某些模式。如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更

夜间模式night
notnight
  • night:夜间
  • notnight:白天

此项为 API 级别 8 中新增配置。

如果夜间模式停留在自动模式(默认),它有可能在应用生命周期中发生改变。在这种情况下,该模式会根据当天的时间进行调整。 可以使用 UiModeManager 启用或禁用此模式。如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更

屏幕像素密度 (dpi)ldpi
mdpi
hdpi
xhdpi
xxhdpi
xxxhdpi
nodpi
tvdpi
anydpi
  • ldpi:低密度屏幕;约为 120dpi。
  • mdpi:中等密度(传统 HVGA)屏幕;约为 160dpi。
  • hdpi:高密度屏幕;约为 240dpi。
  • xhdpi:超高密度屏幕;约为 320dpi。此项为 API 级别 8 中新增配置
  • xxhdpi:超超高密度屏幕;约为 480dpi。此项为 API 级别 16 中新增配置
  • xxxhdpi:超超超高密度屏幕使用(仅限启动器图标,请参阅“支持多种屏幕”中的注释);约为 640dpi。 此项为 API 级别 18 中新增配置
  • nodpi:它可用于您不希望缩放以匹配设备密度的位图资源。
  • tvdpi:密度介于 mdpi 和 hdpi 之间的屏幕;约为 213dpi。它并不是“主要”密度组, 主要用于电视,而大多数应用都不需要它。对于大多数应用而言,提供 mdpi 和 hdpi 资源便已足够,系统将根据需要对其进行缩放。此项为 API 级别 13 中新增配置
  • anydpi:此限定符适合所有屏幕密度,其优先级高于其他限定符。 这对于矢量可绘制对象很有用。 此项为 API 级别 21 中新增配置

六个主要密度之间的缩放比为 3:4:6:8:12:16(忽略 tvdpi 密度)。因此,9x9 (ldpi) 位图相当于 12x12 (mdpi)、18x18 (hdpi)、24x24 (xhdpi) 位图,依此类推。

如果您认为图像资源在电视或其他某些设备上呈现的效果不够好,而想尝试使用 tvdpi 资源,则缩放比例为 1.33*mdpi。例如,mdpi 屏幕的 100px x 100px 图像应该相当于 tvdpi 的133px x 133px。

:使用密度限定符并不表示资源仅适用于该密度的屏幕。 如果没有为备用资源提供最符合当前设备配置的限定符,则系统可能使用其中最匹配的资源。

如需了解有关如何处理不同屏幕密度以及 Android 如何缩放位图以适应当前密度的详细信息,请参阅支持多种屏幕

触摸屏类型notouch
finger
  • notouch:设备没有触摸屏。
  • finger:设备有一个专供用户通过手指直接与其交互的触摸屏。

另请参阅 touchscreen 配置字段,该字段指示设备上的触摸屏类型。

键盘可用性keysexposed
keyshidden
keyssoft
  • keysexposed:设备具有可用的键盘。如果设备启用了软键盘(不无可能),那么即使硬键盘没有展示给用户,哪怕设备没有硬键盘,也可以使用此限定符。 如果没有提供或已经禁用软键盘,则只有在显示硬键盘时才会使用此限定符。
  • keyshidden:设备具有可用的硬键盘,但它处于隐藏状态,且设备没有启用软键盘。
  • keyssoft:设备已经启用软键盘(无论是否可见)。

如果提供了 keysexposed 资源,但未提供 keyssoft 资源,那么只要系统已经启用软键盘,就会使用 keysexposed 资源,而不考虑键盘是否可见。

如果用户打开硬键盘,它有可能在应用生命周期中发生改变。 如需了解这会在运行期间给应用带来哪些影响,请参阅处理运行时变更

另请参阅配置字段 hardKeyboardHidden 和 keyboardHidden,这两个字段分别指示硬键盘的可见性和任何一种键盘(包括软键盘)的可见性。

主要文本输入法nokeys
qwerty
12key
  • nokeys:设备没有用于文本输入的硬按键。
  • qwerty:设备具有标准硬键盘(无论是否对用户可见)。
  • 12key:设备具有 12 键硬键盘(无论是否对用户可见)。

另请参阅 keyboard 配置字段,该字段指示可用的主要文本输入法。

平台版本(API 级别)示例:
v3
v4
v7
等等

设备支持的 API 级别。例如,v1 对应于 API 级别 1(带有 Android 1.0 或更高版本系统的设备),v4 对应于 API 级别 4(带有 Android 1.6 或更高版本系统的设备)。如需了解有关这些值的详细信息,请参阅 Android API 级别文档。

:有些配置限定符是从 Android 1.0 才开始添加,因此并非所有版本的 Android 系统都支持所有限定符。使用新限定符会隐式添加平台版本限定符,因此较旧版本系统的设备必然会忽略它。 例如,使用 w600dp 限定符会自动包括 v13 限定符,因为可用宽度限定符是 API 级别 13 中的新增配置。为了避免出现任何问题,请始终包含一组默认资源(一组“不带限定符”的资源)。 如需了解详细信息,请参阅利用资源提供最佳设备兼容性部分。

限定符命名规则

以下是一些关于使用配置限定符名称的规则:

  • 您可以为单组资源指定多个限定符,并使用短划线分隔。例如,drawable-en-rUS-land 适用于横排美国英语设备。
  • 这些限定符必须遵循表 2 中列出的顺序。例如:
    • 错误:drawable-hdpi-port/
    • 正确:drawable-port-hdpi/
  • 不能嵌套备用资源目录。例如,您不能拥有 res/drawable/drawable-en/
  • 值不区分大小写。在处理之前,资源编译器会将目录名称转换为小写,以避免不区分大小写的文件系统出现问题。 名称中使用的任何大写字母只是为了便于认读。
  • 对于每种限定符类型,仅支持一个值。例如,若要对西班牙语和法语使用相同的可绘制对象文件,则您肯定不能拥有名为 drawable-rES-rFR/ 的目录,而是需要两个包含相应文件的资源目录,如 drawable-rES/ 和 drawable-rFR/。然而,实际上您无需将相同的文件都复制到这两个位置。相反,您可以创建指向资源的别名。 请参阅下面的创建别名资源

将备用资源保存到以这些限定符命名的目录中之后,Android 会根据当前设备配置在应用中自动应用这些资源。 每次请求资源时,Android 都会检查备用资源目录是否包含所请求的资源文件,然后查找最佳匹配资源(下文进行介绍)。 如果没有与特定设备配置匹配的备用资源,则 Android 会使用相应的默认资源(一组用于不含配置限定符的特定资源类型的资源)。

创建别名资源

如果您想将某一资源用于多种设备配置(但是不想作为默认资源提供),则无需将同一资源放入多个备用资源目录中。 相反,您可以(在某些情况下)创建备用资源,充当保存在默认资源目录下的资源的别名。

:并非所有资源都会提供相应机制让您创建指向其他资源的别名。 特别是,xml/ 目录中的动画资源、菜单资源、原始资源以及其他未指定资源均不提供此功能。

例如,假设您有一个应用图标 icon.png,并且需要不同语言区域的独特版本。 但是,加拿大英语和加拿大法语这两种语言区域需要使用同一版本。 您可能会认为需要将相同的图像复制到加拿大英语和加拿大法语对应的资源目录中,但事实并非如此。 相反,您可以将用于二者的图像另存为 icon_ca.png(除 icon.png 以外的任何名称),并将其放入默认 res/drawable/ 目录中。然后,在 res/drawable-en-rCA/ 和 res/drawable-fr-rCA/ 中创建 icon.xml 文件,使用 <bitmap> 元素引用 icon_ca.png 资源。这样,您只需存储 PNG 文件的一个版本和两个指向该版本的小型 XML 文件。(XML 文件示例如下。)

可绘制对象

要创建指向现有可绘制对象的别名,请使用 <bitmap> 元素。例如:

 

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/icon_ca" />

如果将此文件另存为 icon.xml(例如,在备用资源目录中,另存为 res/drawable-en-rCA/),则会编译到可作为R.drawable.icon 引用的资源中,但实际上它是 R.drawable.icon_ca 资源(保存在 res/drawable/ 中)的别名。

布局

要创建指向现有布局的别名,请使用包装在 <merge> 中的 <include> 元素。例如:

 

<?xml version="1.0" encoding="utf-8"?>
<merge>
    <include layout="@layout/main_ltr"/>
</merge>

如果将此文件另存为 main.xml,则会编译到可作为 R.layout.main 引用的资源中,但实际上它是 R.layout.main_ltr 资源的别名。

字符串和其他简单值

要创建指向现有字符串的别名,只需将所需字符串的资源 ID 用作新字符串的值即可。例如:

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello</string>
    <string name="hi">@string/hello</string>
</resources>

R.string.hi 资源现在是 R.string.hello 的别名。

其他简单值的原理相同。 例如,颜色:

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="red">#f00</color>
    <color name="highlight">@color/red</color>
</resources>

利用资源提供最佳设备兼容性

要使应用支持多种设备配置,则务必为应用使用的每种资源类型提供默认资源,这一点非常重要。

例如,如果应用支持多种语言,请始终包含不带语言和区域限定符的 values/ 目录(用于保存字符串)。相反,如果您将所有字符串放入带有语言和区域限定符的目录中,则在语言设置不支持您的字符串的设备上运行应用时,应用将会崩溃。 但是,只要提供默认 values/ 资源,应用就会正常运行(即使用户不理解该语言,这也总比崩溃要好)。

同样,如果您根据屏幕方向提供不同的布局资源,则应选择一个方向作为默认方向。 例如,不要在 layout-land/ 和 layout-port/ 中分别提供横向和纵向的布局资源,而是保留其中之一作为默认设置,例如:layout/ 用于横向,layout-port/ 用于纵向。

提供默认资源至关重要,这不仅仅因为应用可能在超出预期的配置上运行,也因为新版 Android 有时会添加旧版本不支持的配置限定符。若要使用新的资源限定符,又希望维持对旧版 Android 的代码兼容性,则当旧版 Android 运行应用时,如果不提供默认资源,应用将会崩溃,这是因为它无法使用以新限定符命名的资源。例如,如果将 minSdkVersion 设置为 4,并使用夜间模式night 或 notnight,API 级别 8 中新增配置)限定所有可绘制对象资源,则 API 级别 4 设备无法访问可绘制对象资源,而且会崩溃。在这种情况下,您可能希望 notnight 成为默认资源,为此,您应排除该限定符,使可绘制对象资源位于 drawable/ 或 drawable-night/ 中。

因此,为了提供最佳设备兼容性,请始终为应用正确运行所必需的资源提供默认资源。 然后,使用配置限定符为特定的设备配置创建备用资源。

这条规则有一个例外:如果应用的 minSdkVersion 为 4 或更高版本,则在提供带屏幕密度限定符的备用可绘制对象资源时,不需要默认可绘制对象资源。 即使没有默认可绘制对象资源,Android 也可以从备用屏幕密度中找到最佳匹配项并根据需要缩放位图。 但是,为了在所有类型的设备上提供最佳体验,您应该为所有三种类型的密度提供备用可绘制对象。

Android 如何查找最佳匹配资源

当您请求要为其提供备用资源的资源时,Android 会根据当前的设备配置选择要在运行时使用的备用资源。为演示 Android 如何选择备用资源,假设以下可绘制对象目录分别包含相同图像的不同版本:

 

drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/

同时,假设设备配置如下:

语言区域 = en-GB 
屏幕方向 = port 
屏幕像素密度 = hdpi 
触摸屏类型 = notouch 
主要文本输入法 = 12key

通过将设备配置与可用的备用资源进行比较,Android 从 drawable-en-port 中选择可绘制对象。

系统使用以下逻辑决定要使用的资源:

图 2. Android 如何查找最佳匹配资源的流程图。

 
  1. 淘汰与设备配置冲突的资源文件。

    drawable-fr-rCA/ 目录与 en-GB 语言区域冲突,因而被淘汰。

     
    drawable/ drawable-en/ drawable-fr-rCA/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/

    例外:屏幕像素密度是唯一一个未因冲突而被淘汰的限定符。 尽管设备的屏幕密度为 hdpi,但是 drawable-port-ldpi/ 未被淘汰,因为此时每个屏幕密度均视为匹配。如需了解详细信息,请参阅支持多种屏幕文档。

  2. 选择列表(表 2)中(下一个)优先级最高的限定符。(先从 MCC 开始,然后下移。)
  3. 是否有资源目录包括此限定符?
    • 若无,请返回到第 2 步,看看下一个限定符。(在该示例中,除非达到语言限定符,否则答案始终为“否”。)
    • 若有,请继续执行第 4 步。
  4. 淘汰不含此限定符的资源目录。在该示例中,系统会淘汰所有不含语言限定符的目录。
  5. drawable/ drawable-en/ drawable-en-port/ drawable-en-notouch-12key/ drawable-port-ldpi/ drawable-port-notouch-12key/

    例外:如果涉及的限定符是屏幕像素密度,则 Android 会选择最接近设备屏幕密度的选项。通常,Android 倾向于缩小大型原始图像,而不是放大小型原始图像。请参阅支持多种屏幕

  6. 返回并重复第 2 步、第 3 步和第 4 步,直到只剩下一个目录为止。在此示例中,屏幕方向是下一个判断是否匹配的限定符。因此,未指定屏幕方向的资源被淘汰:
     
    drawable-en/ drawable-en-port/ drawable-en-notouch-12key/

    剩下的目录是 drawable-en-port

尽管对所请求的每个资源均执行此程序,但是系统仍会对某些方面做进一步优化。 例如,系统一旦知道设备配置,即会淘汰可能永远无法匹配的备用资源。 比如说,如果配置语言是英语(“en”),则系统绝不会将语言限定符设置为非英语的任何资源目录包含在选中的资源池中(不过,仍会将不带语言限定符的资源目录包含在该池中)。

根据屏幕尺寸限定符选择资源时,如果没有更好的匹配资源,则系统将使用专为小于当前屏幕的屏幕而设计的资源(例如,如有必要,大尺寸屏幕将使用标准尺寸的屏幕资源)。 但是,如果唯一可用的资源大于当前屏幕,则系统不会使用这些资源,并且如果没有其他资源与设备配置匹配,应用将会崩溃(例如,如果所有布局资源均用 xlarge 限定符标记,但设备是标准尺寸的屏幕)。

:限定符的优先顺序(表 2 中)比与设备完全匹配的限定符数量更加重要。例如,在上面的第 4 步中,列表剩下的最后选项包括三个与设备完全匹配的限定符(方向、触摸屏类型和输入法),而 drawable-en 只有一个匹配参数(语言)。但是,语言的优先顺序高于其他两个限定符,因此 drawable-port-notouch-12key 被淘汰。

如需了解有关如何在应用中使用资源的更多信息,请转至访问资源

处理运行时变更

有些设备配置可能会在运行时发生变化(例如屏幕方向、键盘可用性及语言)。 发生这种变化时,Android 会重启正在运行的 Activity(先后调用 onDestroy() 和 onCreate())。重启行为旨在通过利用与新设备配置匹配的备用资源自动重新加载您的应用,来帮助它适应新配置。

要妥善处理重启行为,Activity 必须通过常规的Activity 生命周期恢复其以前的状态,在 Activity 生命周期中,Android 会在销毁 Activity 之前调用 onSaveInstanceState(),以便您保存有关应用状态的数据。 然后,您可以在 onCreate()或 onRestoreInstanceState() 期间恢复 Activity 状态。

要测试应用能否在保持应用状态完好的情况下自行重启,您应该在应用中执行各种任务时调用配置变更(例如,更改屏幕方向)。 您的应用应该能够在不丢失用户数据或状态的情况下随时重启,以便处理如下事件:配置发生变化,或者用户收到来电并在应用进程被销毁很久之后返回到应用。 要了解如何恢复 Activity 状态,请阅读 Activity 生命周期

但是,您可能会遇到这种情况:重启应用并恢复大量数据不仅成本高昂,而且给用户留下糟糕的使用体验。 在这种情况下,您有两个其他选择:

  1. 在配置变更期间保留对象

    允许 Activity 在配置变更时重启,但是要将有状态对象传递给 Activity 的新实例。

  2. 自行处理配置变更

    阻止系统在某些配置变更期间重启 Activity,但要在配置确实发生变化时接收回调,这样,您就能够根据需要手动更新 Activity。

在配置变更期间保留对象

如果重启 Activity 需要恢复大量数据、重新建立网络连接或执行其他密集操作,那么因配置变更而引起的完全重启可能会给用户留下应用运行缓慢的体验。 此外,依靠系统通过onSaveInstanceState() 回调为您保存的 Bundle,可能无法完全恢复 Activity 状态,因为它并非设计用于携带大型对象(例如位图),而且其中的数据必须先序列化,再进行反序列化,这可能会消耗大量内存并使得配置变更速度缓慢。 在这种情况下,如果 Activity 因配置变更而重启,则可通过保留Fragment 来减轻重新初始化 Activity 的负担。此片段可能包含对您要保留的有状态对象的引用。

当 Android 系统因配置变更而关闭 Activity 时,不会销毁您已标记为要保留的 Activity 的片段。 您可以将此类片段添加到 Activity 以保留有状态的对象。

要在运行时配置变更期间将有状态的对象保留在片段中,请执行以下操作:

  1. 扩展 Fragment 类并声明对有状态对象的引用。
  2. 在创建片段后调用 setRetainInstance(boolean)
  3. 将片段添加到 Activity。
  4. 重启 Activity 后,使用 FragmentManager 检索片段。

例如,按如下方式定义片段:

 

public class RetainedFragment extends Fragment {

    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

注意:尽管您可以存储任何对象,但是切勿传递与 Activity 绑定的对象,例如,DrawableAdapterView 或其他任何与 Context 关联的对象。否则,它将泄漏原始 Activity 实例的所有视图和资源。 (泄漏资源意味着应用将继续持有这些资源,但是无法对其进行垃圾回收,因此可能会丢失大量内存。)

然后,使用 FragmentManager 将片段添加到 Activity。在运行时配置变更期间再次启动 Activity 时,您可以获得片段中的数据对象。 例如,按如下方式定义 Activity:

 

public class MyActivity extends Activity {

    private RetainedFragment dataFragment;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);

        // create the fragment and data the first time
        if (dataFragment == null) {
            // add the fragment
            dataFragment = new DataFragment();
            fm.beginTransaction().add(dataFragment, “data”).commit();
            // load the data from the web
            dataFragment.setData(loadMyData());
        }

        // the data is available in dataFragment.getData()
        ...
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // store the data in the fragment
        dataFragment.setData(collectMyLoadedData());
    }
}

在此示例中,onCreate() 添加了一个片段或恢复了对它的引用。此外,onCreate() 还将有状态的对象存储在片段实例内部。onDestroy() 对所保留的片段实例内的有状态对象进行更新。

自行处理配置变更

如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免重启,则可声明 Activity 将自行处理配置变更,这样可以阻止系统重启 Activity。

注:自行处理配置变更可能导致备用资源的使用更为困难,因为系统不会为您自动应用这些资源。 只能在您必须避免 Activity 因配置变更而重启这一万般无奈的情况下,才考虑采用自行处理配置变更这种方法,而且对于大多数应用并不建议使用此方法。

要声明由 Activity 处理配置变更,请在清单文件中编辑相应的 <activity> 元素,以包含 android:configChanges 属性以及代表要处理的配置的值。android:configChanges 属性的文档中列出了该属性的可能值(最常用的值包括 "orientation" 和 "keyboardHidden",分别用于避免因屏幕方向和可用键盘改变而导致重启)。您可以在该属性中声明多个配置值,方法是用管道 | 字符分隔这些配置值。

例如,以下清单文件代码声明的 Activity 可同时处理屏幕方向变更和键盘可用性变更:

 

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

现在,当其中一个配置发生变化时,MyActivity 不会重启。相反,MyActivity 会收到对 onConfigurationChanged() 的调用。向此方法传递 Configuration 对象指定新设备配置。您可以通过读取Configuration 中的字段,确定新配置,然后通过更新界面中使用的资源进行适当的更改。调用此方法时,Activity 的Resources 对象会相应地进行更新,以根据新配置返回资源,这样,您就能够在系统不重启 Activity 的情况下轻松重置 UI 的元素。

注意:从 Android 3.2(API 级别 13)开始,当设备在纵向和横向之间切换时,“屏幕尺寸”也会发生变化。因此,在开发针对 API 级别 13 或更高版本(正如 minSdkVersion 和 targetSdkVersion 属性中所声明)的应用时,若要避免由于设备方向改变而导致运行时重启,则除了 "orientation" 值以外,您还必须添加 "screenSize" 值。 也就是说,您必须声明 android:configChanges="orientation|screenSize"。但是,如果您的应用面向 API 级别 12 或更低版本,则 Activity 始终会自行处理此配置变更(即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重启 Activity)。

例如,以下 onConfigurationChanged() 实现检查当前设备方向:

 

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

Configuration 对象代表所有当前配置,而不仅仅是已经变更的配置。大多数时候,您并不在意配置具体发生了哪些变更,而且您可以轻松地重新分配所有资源,为您正在处理的配置提供备用资源。 例如,由于 Resources 对象现已更新,因此您可以通过 setImageResource() 重置任何 ImageView,并且使用适合于新配置的资源(如提供资源中所述)。

请注意,Configuration 字段中的值是与 Configuration 类中的特定常量匹配的整型数。有关要对每个字段使用哪些常量的文档,请参阅 Configuration 参考文档中的相应字段。

请谨记:在声明由 Activity 处理配置变更时,您有责任重置要为其提供备用资源的所有元素。 如果您声明由 Activity 处理方向变更,而且有些图像应该在横向和纵向之间切换,则必须在 onConfigurationChanged() 期间将每个资源重新分配给每个元素。

如果无需基于这些配置变更更新应用,则可不用实现 onConfigurationChanged()。在这种情况下,仍将使用在配置变更之前用到的所有资源,只是您无需重启 Activity。 但是,应用应该始终能够在保持之前状态完好的情况下关闭和重启,因此您不得试图通过此方法来逃避在正常 Activity 生命周期期间保持您的应用状态。 这不仅仅是因为还存在其他一些无法禁止重启应用的配置变更,还因为有些事件必须由您处理,例如用户离开应用,而在用户返回应用之前该应用已被销毁。

如需了解有关您可以在 Activity 中处理哪些配置变更的详细信息,请参阅 android:configChanges 文档和 Configuration 类。

内联复杂的XML资源

某些资源类型是由XML文件表示的多个复杂资源的组合。一个例子是动画矢量drawable,它是一个可绘制的资源,封装了矢量drawable和动画。这需要使用至少三个XML文件。

res/drawable/avd.xml

 

<?xml version = “1.0” encoding = “utf-8” ?> <animated-vector xmlns:android = “http://schemas.android.com/apk/res/android” android:drawable = “@ drawable / vectordrawable” > <target android:name = “rotationGroup” android:animation = “@ anim / rotation” /> </ animated-vector>

   
   
       
       

res/drawable/vectordrawable.xml

 

<?xml version = “1.0” encoding = “utf-8” ?> <vector xmlns:android = “http://schemas.android.com/apk/res/android” android:height = “64dp” android:width = “ 64dp“ android:viewportHeight = ”600“ android:viewportWidth = ”600“ > <group android:name = ”rotationGroup“ android:pivotX = ”300.0“ android:pivotY = ”300.0“ android:rotation =

   
   
   
   

  
       
       
       
       
       
            = “#000000” android:pathData = “M300,70 l 0,-70 70,70 0,0 -70,70z” /> </ group> </ vector>
           

  

res/anim/rotation.xml

 

<?xml version = “1.0” encoding = “utf-8” ?> <objectAnimator xmlns:android = “http://schemas.android.com/apk/android” android:duration = “6000” android:propertyName = “rotation” android:valueFrom = “0” android:valueTo = “360” />

   
   
   

  

这里有很多文件只是为了制作一个动画矢量可绘制!如果矢量drawable和动画在其他地方重复使用,这是实现动画矢量drawable的最佳方式。如果它们只用于这个动画矢量可绘制,那么有一种更紧凑的方式来实现它们。

使用AAPT的内联资源格式,您可以在同一XML文件中定义所有三个资源。由于我们正在制作一个动画矢量可绘制,我们将文件放在下面res/drawable/

res/drawable/avd.xml

 

<?xml version = “1.0” encoding = “utf-8” ?> <animated-vector xmlns:android = “http://schemas.android.com/apk/res/android” xmlns:aapt = “http:// schemas .android.com / aapt“ > <aapt:attr name = ”android:drawable“ > <vector android:height = ”64dp“ android:width = ”64dp“ android:viewportHeight = ”600“ android:viewportWidth = ”600“ > <group android:

   

   
       
           
           
           
           

          
               
                android:pivotX = “300.0” android:pivotY = “300.0” android:rotation = “45.0” > <path android:fillColor = “#000000” android:pathData = “M300,70 l 0,-70 70,70 0, 0 -70,70z“ /> </ group> </ vector> </ aapt:attr> <target android:name = ”rotationGroup“ > <aapt:attr name = ”android:animation“ > <objectAnimator android:duration = “6000” android:propertyName = “rotation” android:valueFrom
               
               
               
                   
                   

          
       
   

   
       
           
               
               
                = “0” android:valueTo = “360” /> </ aapt:attr> </ target> </ animated-vector>

              
       
   

XML标记<aapt:attr >告诉AAPT标记的子项应被视为资源并提取到其自己的资源文件中。属性名称中的值指定在父标记中使用内联资源的位置。

AAPT将为所有内联资源生成资源文件和名称。使用此内联格式构建的应用程序与所有Android版本兼容。

  • 29
    点赞
  • 220
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值