第 1 章 Android开发初体验

第 1 章 Android开发初体验

新葡京娱乐场:  就土耳其未遂政变,德国《明镜》周刊18日援引德国联邦情报局局长布鲁诺·卡尔的话报道,土耳其一直想向德国政府证明,政变的幕后主使是居伦,但迄今仍未提供充分证据。

本章将带你开发本书第一个应用,并借此学习一些Android基本概念以及构成应用的UI组件。学完本章,如果没能全部理解,也不必担心,后续章节还会涉及这些内容并有更加详细的讲解。

马上要开发的应用名叫GeoQuiz,它能给出一道道地理知识问题。用户点击TRUE或FALSE按钮来回答屏幕上的问题,GeoQuiz即时作出反馈。

图1-1显示了用户点击TRUE按钮的结果。

图1-1 你是澳洲人吗

1.1 Android开发基础

GeoQuiz应用由一个activity和一个布局(layout)组成。

  • activity是Android SDK中Activity类的一个实例,负责管理用户与应用界面的交互。

    应用的功能是通过编写Activity子类来实现的。对于简单的应用来说,一个Activity子类可能就够了,而复杂的应用则会有多个。

    GeoQuiz是个简单应用,因此它只有一个名叫QuizActivityActivity子类。QuizActivity管理着图1-1所示的用户界面。

  • 布局定义了一系列用户界面对象以及它们显示在屏幕上的位置。组成布局的定义保存在XML文件中。每个定义用来创建屏幕上的一个对象,如按钮或文本信息。

    GeoQuiz应用包含一个名叫activity_quiz.xml的布局文件。该布局文件中的XML标签定义了图1-1所示的用户界面。

QuizActivity与activity_quiz.xml文件的关系如图1-2所示。

{%}

图1-2 QuizActivity管理着activity_quiz.xml文件定义的用户界面

有了这些Android基本概念之后,我们来创建GeoQuiz应用。

1.2 创建Android项目

首先我们创建一个Android项目。Android项目包含组成一个应用的全部文件。

启动Android Studio程序,首次运行的话,会看到如图1-3所示的欢迎界面。

{%}

图1-3 欢迎使用Android Studio

在欢迎界面,选择创建Android Studio新项目选项(Start a new Android Studio project);非首次运行的话,选择File → New → New Project…菜单项。

现在,你应该打开了新建项目向导界面,如图1-4所示。在此界面的应用名称(Application name)处输入GeoQuiz。在公司域名(Company Domain)处输入android.bignerdranch.com。此时自动产生的包名称(Package name)会变为com.bignerdranch.android.geoquiz。至于项目存储位置(Project location),就看个人喜好了。

{%}

图1-4 创建新项目

注意,以上包名称遵循了“DNS反转”约定,也就是将企业组织或公司的域名反转后,在尾部附加上应用名称。遵循此约定可以保证包名称的唯一性,这样,同一设备和Google Play商店的各类应用就可以区分开来。

单击Next按钮,接下来配置应用支持哪些版本的Android设备。GeoQuiz应用只能在手机上运行,所以这里勾选Phone and Tablet选项。SDK最低版本选择API 19: Android 4.4(KitKat),如图1-5所示。第6章会介绍Android不同SDK版本的差异。

{%}

图1-5 设备支持配置

单击Next按钮继续。

在接下来的窗口中,需要为GeoQuiz应用的启动初始屏选择模板,如图1-6所示。选择Empty Activity后单击Next按钮继续。

(Android Studio更新频繁,因此新版本的向导界面可能与本书略有不同。这不是什么大问题,一般来讲,工具更新后,向导界面的配置选项应该不会有太大差别。如果大有不同,说明开发工具有了重大更新。不要担心,请访问本书论坛 forums.bignerdranch.com,我们会教你如何使用新版本的开发工具。)

{%}

图1-6 选取activity类型(空activity)

在应用向导的最后一个窗口,命名activity子类为QuizActivity,如图1-7所示。注意子类名的Activity后缀。尽管不是必需的,但最好是遵循这种规范的命名约定。

{%}

图1-7 配置activity

确认Generate Layout File选项已勾选。为体现布局与activity间的对应关系,布局名(Layout Name)会自动更新为activity_quiz。布局的命名规则是:颠倒activity子类名的单词顺序并全部转小写,然后在单词间添加下划线。对于后续章节中的所有布局以及将要学习的其他资源,都建议采用这种命名风格。

如果你的Android Studio版本还有其他选项,请保持默认选择不变。单击Finish按钮,Android Studio会完成创建并打开新项目。

1.3 Android Studio使用导航

如图1-8所示,Android Studio已在工作区窗口里打开新建项目。

整个工作区窗口分为不同的区域,这里统称为工具窗口(tool window)。

{%}

图1-8 新的项目窗口

左边是项目工具窗口(project tool window),通过它可以查看和管理所有与项目相关的文件。

主视图是代码编辑区(editor)。为便于开发,Android Studio已在代码编辑区打开了QuizActivity.java文件。

点击工作区窗口左边、右边以及底部标有各种名称的工具按钮区域,可显示或隐藏各类工具窗口。当然,也可以直接使用它们对应的快捷键。假如看不到某个工具按钮的话,可以点击左下角的灰色方形区域或单击View → Tool Buttons菜单项找到它。

1.4 用户界面设计

首先打开app/res/layout/activity_quiz.xml文件。如果看到的是布局预览界面,请点击底部的Text页切换显示XML代码。

当前,activity_quiz.xml文件定义了默认的activity布局。默认的XML布局文件内容经常有变,但相比代码清单1-1,一般不会有很大出入。

代码清单1-1 默认的activity布局(activity_quiz.xml)

<Relativelayout
    xmlns:android="http://www.wking-china.com/xpjylc/apk/res/android"
    xmlns:tools="http://www.wking-china.com/xpjylc/tools"
    android:id="@+id/activity_quiz"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="16dp"
    android:paddingleft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="16dp"
    tools:context="com.bignerdranch.android.geoquiz.QuizActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"/>
</Relativelayout>

应用activity的默认布局定义了两个组件(widget):RelativeLayoutTextView

组件是用户界面的构造模块。组件可以显示文字或图像,与用户交互,甚至布置屏幕上的其他组件。按钮、文本输入控件和选择框等都是组件。

Android SDK内置了多种组件,通过配置各种组件可获得所需的用户界面及行为。每一个组件都是View类或其子类(如TextViewButton)的一个具体实例。

图1-9展示了代码清单1-1中定义的RelativeLayoutTextView是如何在屏幕上显示的。

图1-9 显示在屏幕上的默认组件

不过,图1-9所示的默认组件并不是我们需要的,QuizActivity的用户界面需要下列组件:

  • 一个垂直LinearLayout组件;

  • 一个TextView组件;

  • 一个水平LinearLayout组件;

  • 两个Button组件。

图1-10展示了以上组件是如何构成QuizActivity用户界面的。

图1-10 布置并显示在屏幕上的组件

下面我们在activity_quiz.xml文件中定义这些组件。

在项目工具窗口中找到app/res/layout目录,打开activity_quiz.xml文件。对照代码清单1-2,修改文件内容。注意,需删除的XML已打上删除线,需添加的XML以粗体显示。本书统一使用这样的代码增删处理模式。

代码清单1-2 在XML文件(activity_quiz.xml)中定义组件

<RelativeLayout
    android:id="@+id/activity_quiz"
    xmlns:android="http://www.wking-china.com/xpjylc/apk/res/android"
    xmlns:tools="http://www.wking-china.com/xpjylc/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.bignerdranch.android.geoquiz.QuizActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"/>
</RelativeLayout>

<LinearLayout xmlns:android="http://www.wking-china.com/xpjylc/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="@string/question_text" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

      <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/true_button" />

      <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/false_button" />

    </LinearLayout>

</LinearLayout>

参照代码清单输入代码,暂时不理解这些代码也没关系,你会在后续学习中逐渐弄明白的。注意,开发工具无法校验布局XML内容,拼写错误早晚会出问题,应尽量避免。

可以看到,有三行以android:text开头的代码出现了错误信息。暂时忽略它们,稍后会处理。

对照图1-10所示的用户界面查看XML文件,可以看出组件与XML元素一一对应。元素的名称就是组件的类型。

各元素均有一组XML属性属性可以看作如何配置组件的指令。

为方便理解元素与属性的工作原理,接下来我们将以层级视角来研究布局。

1.4.1 视图层级结构

组件包含在视图(View)对象的层级结构中,这种结构又称作视图层级结构(view hierarchy)。图1-11展示了代码清单1-2所示的XML布局对应的视图层级结构。

图像说明文字

图1-11 布局组件的层级结构

从布局的视图层级结构可以看到,其根元素是一个LinearLayout组件。作为根元素,LinearLayout组件必须指定Android XML资源文件的命名空间属性,这里是http://www.wking-china.com/xpjylc/apk/res/android

LinearLayout组件继承自ViewGroup组件(也是个View子类)。ViewGroup组件是包含并配置其他组件的特殊组件。想要以一列或一排的样式布置组件,就可以使用LinearLayout组件。其他ViewGroup子类还有FrameLayoutTableLayoutRelativeLayout

若某个组件包含在一个ViewGroup中,该组件与ViewGroup即构成父子关系。根LinearLayout有两个子组件:TextView和另一个LinearLayout。作为子组件的LinearLayout自己还有两个Button子组件。

1.4.2 组件属性

下面来看看配置组件时常用的一些属性。

  1. android:layout_widthandroid:layout_height属性

    几乎每类组件都需要android:layout_widthandroid:layout_height属性。以下是它们的两个常见属性值(二选一)。

    • match_parent:视图与其父视图大小相同。

    • wrap_content:视图将根据其显示内容自动调整大小。

    (以前还有个fill_parent属性值,等同于match_parent,现已废弃不用。)

    LinearLayout组件的高度与宽度属性值均为match_parentLinearLayout虽然是根元素,但它也有父视图——Android提供该父视图来容纳应用的整个视图层级结构。

    其他包含在界面布局中的组件,其高度与宽度属性值均被设置为wrap_content。请参照图1-10理解该属性值定义尺寸大小的作用。

    TextView组件比其包含的文字内容区域稍大一些,这主要是android:padding="24dp"属性的作用。该属性告诉组件在决定大小时,除内容本身外,还需增加额外指定量的空间。这样屏幕上显示的问题与按钮之间便会留有一定的空间,使整体显得更为美观。(不理解dp的意思?dp即density-independent pixel,指与密度无关的像素,详见第9章。)

  2. android:orientation属性

    android:orientation属性是两个LinearLayout组件都具有的属性,它决定两者的子组件是水平放置还是垂直放置。根LinearLayout是垂直的,子LinearLayout是水平的。

    子组件的定义顺序决定其在屏幕上显示的顺序。在垂直的LinearLayout中,第一个定义的子组件出现在屏幕的最上端;而在水平的LinearLayout中,第一个定义的子组件出现在屏幕的最左端。(如果设备文字从右至左显示,如阿拉伯语或者希伯来语,第一个定义的子组件则出现在屏幕的最右端。)

  3. android:text属性

    TextViewButton组件具有android:text属性。该属性指定组件要显示的文字内容。

    请注意,android:text属性值不是字符串值,而是对字符串资源(string resource)的引用。

    字符串资源包含在一个独立的名叫strings的XML文件中(strings.xml),虽然可以硬编码设置组件的文本属性值,如android:text="True",但这通常不是个好主意。比较好的做法是:将文字内容放置在独立的字符串资源XML文件中,然后引用它们。这样会方便应用的本地化(支持多国语言)。

    需要在activity_quiz.xml文件中引用的字符串资源还没添加。现在就来处理。

1.4.3 创建字符串资源

每个项目都包含一个默认字符串资源文件strings.xml。

在项目工具窗口中,找到app/res/values目录,展开目录,打开strings.xml文件。

可以看到,项目模板已经添加了一些字符串资源。如代码清单1-3所示,添加应用布局需要的三个新的字符串。

代码清单1-3 添加字符串资源(strings.xml)

<resources>
    <string name="app_name">GeoQuiz</string>
    <string name="question_text">Canberra is the capital of Australia.</string>
    <string name="true_button">True</string>
    <string name="false_button">False</string>
</resources>

(某些版本的Android Studio的strings.xml默认带有其他字符串,请勿删除它们,否则会引发与其他文件的联动错误。)

现在,在GeoQuiz项目的任何XML文件中,只要引用到@string/false_button,应用运行时,就会得到文本“False”。

保存strings.xml文件。这时,activity_quiz.xml布局缺少字符串资源的提示信息应该就消失了。(如仍有错误提示,请检查一下这两个文件,确认没有拼写错误。)

默认的字符串文件虽然已命名为strings.xml,你仍可以按个人喜好重命名。一个项目也可以有多个字符串文件。只要这些文件都放在res/values/目录下,含有一个resources根元素,以及多个string子元素,应用就能找到并正确使用它们。

1.4.4 预览布局

至此,应用的界面布局已经完成,可以使用图形布局工具实时预览了。首先,确认保存了所有相关文件并且无错误发生,然后回到activity_quiz.xml文件,点击代码编辑区右边的Preview打开预览工具窗口(如果还没打开的话),如图1-12所示。

图1-12 在图形布局工具中预览布局(activity_quiz.xml)

1.5 从布局XML到视图对象

知道activity_quiz.xml中的XML元素是如何转换为视图对象的吗?答案就在于QuizActivity类。

在创建GeoQuiz项目的同时,向导也创建了一个名叫QuizActivityActivity子类。QuizActivity类文件存放在项目的app/java目录下。java目录是项目全部Java源代码的存放处。

在项目工具窗口中,依次展开app/java目录与com.bignerdranch.android.geoquiz包。找到并打开QuizActivity.java文件,查看其中的代码,如代码清单1-4所示。

代码清单1-4 默认QuizActivity类文件(QuizActivity.java)

package com.bignerdranch.android.geoquiz;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class QuizActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);
    }
}

(是不是不明白AppCompatActivity的作用?它实际就是一个Activity子类,能为Android旧版本系统提供兼容支持。第13章会详细介绍AppCompatActivity。)

如果无法看到全部类包导入语句,请单击第一行导入语句左边的⊕符号来显示它们。

该Java类文件有一个Activity方法:onCreate(Bundle)

(如果你的文件还包含onCreateOptionsMenu(Menu)onOptionsItemSelected(MenuItem)方法,暂时不用理会。第13章会详细介绍它们。)

activity子类的实例创建后,onCreate(Bundle)方法会被调用。activity创建后,它需要获取并管理用户界面。要获取activity的用户界面,可调用以下Activity方法:

public void setContentView(int layoutResID)

根据传入的布局资源ID参数,该方法生成指定布局的视图并将其放置在屏幕上。布局视图生成后,布局文件包含的组件也随之以各自的属性定义完成实例化。

资源与资源ID

布局是一种资源资源是应用非代码形式的内容,如图像文件、音频文件以及XML文件等。

项目的所有资源文件都存放在目录app/res的子目录下。在项目工具窗口中可以看到,activity_quiz.xml布局资源文件存放在res/layout/目录下。strings.xml字符串资源文件存放在res/values/目录下。

可以使用资源ID在代码中获取相应的资源。activity_quiz.xml布局的资源ID为R.layout.activity_quiz。

查看GeoQuiz应用的资源ID需要切换项目视图。Android Studio默认使用Android项目视图,如图1-13所示。为让开发者专注于最常用的文件和目录,默认视图隐藏了Android项目的真实文件目录结构。

{%}

图1-13 切换项目视图

在项目工具窗口的最上部找到下拉菜单,从Android项目视图切换至Project视图。Project视图会显示出当前项目的所有文件和目录。

展开目录app/build/generated/source/r/debug,找到项目包名称并打开其中的R.java文件,即可看到GeoQuiz应用当前所有的资源。R.java文件在Android项目编译过程中自动生成,如该文件头部的警示所述,请不要修改该文件的内容。

修改布局或字符串等资源后,R.java文件不会实时刷新。Android Studio另外还存有一份代码编译用的R.java隐藏文件。当前代码编辑区打开的R.java文件仅在应用安装至设备或模拟器前产生,因此只有在Android Studio中点击运行应用时,它才会得到更新。

R.java文件通常比较大,代码清单1-5仅展示了部分内容。

代码清单1-5 GeoQuiz应用当前的资源ID(R.java)

/* AUTO-GENERATED FILE. DO NOT MODIFY.
 *
 * This class was automatically generated by the
 * aapt tool from the resource data it found. It
 * should not be modified by hand.
 */

package com.bignerdranch.android.geoquiz;

public final class R {
    public static final class anim {
        ...
    }

    ...

    public static final class id {
        ...
    }
    public static final class layout {
        ...
        public static final int activity_quiz=0x7f030017;
    }
    public static final class mipmap {
        public static final int ic_launcher=0x7f030000;
    }
    public static final class string {
        ...
        public static final int app_name=0x7f0a0010;
        public static final int false_button=0x7f0a0012;
        public static final int question_text=0x7f0a0014;
        public static final int true_button=0x7f0a0015;
    }
}

可以看到R.layout.activity_quiz即来自该文件。activity_quiz是R的内部类layout里的一个整型常量名。

GeoQuiz应用需要的字符串同样具有资源ID。目前为止,我们还未在代码中引用过字符串,如果需要,可以使用以下方法:

setTitle(R.string.app_name);

Android为整个布局文件以及各个字符串生成资源ID,但activity_quiz.xml布局文件中的组件除外,因为不是所有组件都需要资源ID。在本章中,我们要在代码里与两个按钮交互,因此只需为它们生成资源ID即可。

本书主要使用Android项目视图,生成资源ID之前,记得切回。当然,如果你就喜欢使用Project视图,也没啥问题。

要为组件生成资源ID,请在定义组件时为其添加android:id属性。在activity_quiz.xml文件中,分别为两个按钮添加上android:id属性,如代码清单1-6所示。

代码清单1-6 为按钮添加资源ID(activity_quiz.xml)

<LinearLayout  ... >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="24dp"
        android:text="@string/question_text" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/true_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/true_button" />

        <Button
            android:id="@+id/false_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/false_button" />

    </LinearLayout>

</LinearLayout>

注意,android:id属性值前面有一个+标志,而android:text属性值则没有。这是因为我们在创建资源ID,而对字符串资源只是做引用

1.6 组件的实际应用

按钮有了资源ID,就可以在QuizActivity中直接获取它们。首先,在QuizActivity.java文件中增加两个成员变量。

在QuizActivity.java文件中输入代码清单1-7所示代码。(请勿使用代码自动补全功能。)

代码清单1-7 添加成员变量(QuizActivity.java)

public class QuizActivity extends AppCompatActivity {

    private Button mTrueButton;
    private Button mFalseButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);
    }
}

文件保存后,会看到两个错误提示。没关系,稍后会处理。请注意新增的两个成员(实例)变量名称的m前缀。该前缀是Android编程应遵循的命名约定,本书将始终遵循该约定。

现在,将鼠标移至代码左边的错误提示处时,会看到两条同样的错误信息:Cannot resolve symbol 'Button'。

这告诉我们,需要在QuizActivity.java文件中导入android.widget.Button类包。可在文件头部手动输入以下代码:

import android.widget.Button;

或者使用Option+Return(Alt+Enter)组合键,让Android Studio自动为你导入。代码有误时,也可以使用该组合键来修正。记得要常用。

类包导入后,刚才的错误提示应该就消失了。(如果仍然存在,请检查Java代码以及XML文件,确认是否存在输入或拼写错误。)

接下来,我们来编码使用按钮组件,这需要以下两个步骤:

  • 引用生成的视图对象;

  • 为对象设置监听器,以响应用户操作。

1.6.1 引用组件

在activity中,可调用以下Activity方法引用已生成的组件:

public View findViewById(int id)

该方法以组件的资源ID作为参数,返回一个视图对象。

在QuizActivity.java文件中,使用按钮的资源ID获取视图对象,赋值给对应的成员变量,如代码清单1-8所示。注意,赋值前,必须先将返回的View类型转换为Button

代码清单1-8 引用组件(QuizActivity.java)

public class QuizActivity extends AppCompatActivity {

    private Button mTrueButton;
    private Button mFalseButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);

        mTrueButton = (Button) findViewById(R.id.true_button);
        mFalseButton = (Button) findViewById(R.id.false_button);
    }
}

1.6.2 设置监听器

Android应用属于典型的事件驱动类型。不像命令行或脚本程序,事件驱动型应用启动后,即开始等待行为事件的发生,如用户点击某个按钮。(事件也可以由操作系统或其他应用触发,但用户触发的事件更直观,如点击按钮。)

应用等待某个特定事件的发生,也可以说应用正在“监听”特定事件。为响应某个事件而创建的对象叫作监听器(listener)。监听器会实现特定事件的监听器接口(listener interface)。

无需自己动手,Android SDK已经为各种事件内置了很多监听器接口。当前应用需要监听用户的按钮“点击”事件,因此监听器需实现View.OnClickListener接口。

首先处理TRUE按钮。在QuizActivity.java文件中,在onCreate(Bundle)方法的变量赋值语句后输入下列代码,如代码清单1-9所示。

代码清单1-9 为TRUE按钮设置监听器(QuizActivity.java)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_quiz);

        mTrueButton = (Button) findViewById(R.id.true_button);
        mTrueButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Does nothing yet, but soon!
            }
        });

        mFalseButton = (Button) findViewById(R.id.false_button);
    }
}

(如果遇到View cannot be resolved to a type的错误提示,请使用Option+Return(Alt+Enter)快捷键导入View类。)

在代码清单1-9中,我们设置了一个监听器。按钮mTrueButton被点击后,监听器会立即通知我们。传入setOnClickListener(OnClickListener)方法的参数是一个监听器。它是一个实现了OnClickListener接口的对象。

使用匿名内部类

这里,一个匿名内部类(anonymous inner class)实现了OnClickListener接口。语法看上去稍显复杂,不过有个助记小技巧:最外层一对括号内的全部代码就是传入setOnClickListener(OnClickListener)方法的参数。

mTrueButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // Does nothing yet, but soon!
    }
});

本书所有的监听器都以匿名内部类来实现。这样做有两大好处。第一,使用匿名内部类,可以相对集中地实现监听器方法,一眼可见;第二,事件监听器一般只在一个地方使用,使用匿名内部类,就不用去创建繁琐的命名类了。

匿名内部类实现了OnClickListener接口,因此它也必须实现该接口唯一的onClick(View)方法。onClick(View)现在是个空方法。虽然必须实现onClick(View)方法,但具体如何实现取决于使用者,因此即使是个空方法,编译器也可以编译通过。

(如果匿名内部类、监听器、接口等概念已忘得差不多了,现在就该去复习一下,或找本参考手册备查。)

参照代码清单1-10为FALSE按钮设置类似的事件监听器。

代码清单1-10 为FALSE按钮设置监听器(QuizActivity.java)

    mTrueButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Does nothing yet, but soon!
        }
    });

    mFalseButton = (Button) findViewById(R.id.false_button);
    mFalseButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Does nothing yet, but soon!
        }
    });
}

1.7 创建提示消息

接下来要实现的是,分别点击两个按钮,弹出我们称之为toast的提示消息。Android的toast是用来通知用户的简短弹出消息,用户无需输入什么,也不用做任何干预操作。这里,我们要用toast来反馈答案,如图1-14所示。

图1-14 toast消息反馈

首先回到strings.xml文件,如代码清单1-11所示,为toast添加消息显示用的字符串资源。

代码清单1-11 增加toast字符串(strings.xml)

<resources>
    <string name="app_name">GeoQuiz</string>
    <string name="question_text">Canberra is the capital of Australia.</string>
    <string name="true_button">True</string>
    <string name="false_button">False</string>
    <string name="correct_toast">Correct!</string>
    <string name="incorrect_toast">Incorrect!</string>
</resources>

调用Toast类的以下方法,可创建toast:

public static Toast makeText(Context context, int resId, int duration)

该方法的Context参数通常是Activity的一个实例(Activity本身就是Context的子类)。第二个参数是toast要显示字符串消息的资源ID。Toast类必须借助Context才能找到并使用字符串资源ID。第三个参数通常是两个Toast常量中的一个,用来指定toast消息的停留时间。

创建toast后,可调用Toast.show()方法在屏幕上显示toast消息。

QuizActivity代码里,分别调用makeText(...)方法,如代码清单1-12所示。在添加makeText(...)时,可利用Android Studio的代码自动补全功能,让代码输入更轻松。

使用代码自动补全

代码自动补全功能可以节约大量开发时间,越早掌握受益越多。

参照代码清单1-12,依次输入代码。当输入到Toast类后的点号时,Android Studio会弹出一个窗口,窗口内显示了建议使用的Toast类的常量与方法。

要选择需要的建议方法,使用上下键。(如果不想使用代码自动补全功能,请不要按Tab键、Return/Enter键,或使用鼠标点击弹出窗口,只管继续输入代码直至完成。)

在建议列表里,选择makeText(Context context, int resID, int duration)方法,代码自动补全功能会自动添加完整的方法调用。

完成makeText方法的全部参数设置,完成后的代码如代码清单1-12所示。

代码清单1-12 创建提示消息(QuizActivity.java)

mTrueButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(QuizActivity.this,
                       R.string.correct_toast,
                       Toast.LENGTH_SHORT).show();
            // Does nothing yet, but soon!
    }
});
mFalseButton = (Button) find ViewById(R.id.false_button);
mFalseButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(QuizActivity.this,
                       R.string.incorrect_toast,
                       Toast.LENGTH_SHORT).show();
            // Does nothing yet, but soon!
    }
});

makeText(...)里,传入QuizActivity实例作为Context的参数值。注意此处应输入的参数是QuizActivity.this,不要想当然地直接输入this。因为匿名类的使用,这里的this指的是监听器View.OnClickListener

使用代码自动补全功能,自己也就不用导入Toast类了,因为Android Studio会自动导入相关类。

好了,现在可以运行应用了。

1.8 使用模拟器运行应用

运行Android应用需使用硬件设备或虚拟设备(virtual device)。包含在开发工具中的Android设备模拟器可提供多种虚拟设备。

要创建Android虚拟设备(AVD),在Android Studio中,选择Tools → Android → AVD Manager菜单项。AVD管理器窗口弹出时,点击窗口左下角的+Create Virtual Device…按钮。

在随后弹出的对话框中,可以看到有很多配置虚拟设备的选项。对于首个虚拟设备,我们选择模拟运行Nexus 5X设备,如图1-15所示。点击Next继续。

{%}

图1-15 创建新的AVD

如图1-16所示,接下来选择模拟器的系统镜像。选择x86 Nougat模拟器后点击Next按钮继续。(在点击Next按钮之前,可能需要下载模拟器组件。按照提示操作即可。)

{%}

图1-16 选择系统镜像

最后,可以对模拟器的各项参数做最终修改并确认,如图1-17所示。当然,如果有需要,也可以事后再编辑修改模拟器的各项参数。现在,为模拟器取个便于识别的名称,然后点击Finish按钮完成虚拟设备的创建。

{%}

图1-17 模拟器参数调整

AVD创建成功后,我们用它运行GeoQuiz应用。点击Android Studio工具栏上的Run按钮,或者使用Control+R快捷键。Android Studio会自动找到新建的虚拟设备,安装应用包(APK),然后启动并运行应用。

模拟器的启动过程比较耗时,请耐心等待。等设备启动完成,应用运行后,就可以在应用界面点击按钮,让toast告诉你答案了。

假如启动时或在点击按钮时,GeoQuiz应用崩溃,可以在Android DDMS工具窗口的LogCat视图中看到有用的诊断信息。(如果LogCat没有自动打开,可点击Android Studio窗口底部的Android Monitor按钮打开它。)查看日志,可看到抢眼的红色异常信息,如图1-18所示。

{%}

图1-18 第21行代码处发生了NullPointerException异常

将输入的代码与书中的代码作一下比较,找出错误并修改,尝试重新运行应用(接下来的两章还会深入介绍LogCat和代码调试的知识)。

最好不要关掉模拟器,这样就不必在反复运行调试应用时,浪费时间等待AVD启动了。

单击AVD模拟器上的后退按钮可以停止应用。这个后退按钮的形状像一个指向左侧的三角形(在较早版本的Android中,它像一个U型箭头)。需要调试变更时,再通过Android Studio重新运行应用。

模拟器虽然好用,但在实体设备上测试应用能获得更准确的结果。在第2章中,我们会在实体设备上运行GeoQuiz应用,还会为GeoQuiz应用添加更多地理知识问题。

1.9 深入学习:Android编译过程

学习到这里,你可能迫切想知道Android是如何编译的。你已经知道在项目文件发生变化时, Android Studio无需指示便会自动进行编译。在整个编译过程中,Android开发工具将资源文件、代码以及AndroidManifest.xml文件(包含应用的元数据)编译生成.apk文件。.apk文件要在模拟器上运行,还需以debug key签名。(分发.apk应用给用户时,应用必须以release key签名。更多有关编译过程的信息,可参考Android开发文档网页developer.android.com/tools/publishing/preparing.html。)

那么,应用的activity_quiz.xml布局文件的内容该如何转变为View对象呢?作为编译过程的一部分,aapt(Android Asset Packaging Tool)将布局文件资源编译压缩紧凑后,打包到.apk文件中。然后,在QuizActivity类的onCreate(Bundle)方法调用setContentView(...)方法时,QuizActivity使用LayoutInflater类实例化布局文件中定义的每一个View对象,如图1-19所示。

{%}

图1-19 activity_quiz.xml中的视图实例化

(除了在XML文件中定义视图外,也可以在activity里使用代码创建视图类。不过,从设计角度来看,应用展现层与逻辑层分离好处多多,其中最主要的一点是可以利用SDK内置的设备配置改变,这一点将在第3章中详细讲解。)

有关XML不同属性的工作原理以及视图如何显示在屏幕上等更多信息,请参见第9章。

Android编译工具

当前,我们看到的项目编译都是在Android Studio里执行的。编译功能已整合到IDE中,IDE负责调用aapt等Android标准编译工具,但编译过程本身仍由Android Studio管理。

有时,出于某种原因,可能需要脱离Android Studio编译代码。最简单的方法是使用命令行编译工具。现代Android编译系统使用Gradle编译工具。

(注意,能读懂本小节内容并按步骤操作是最好的。如果看不懂,甚至不知道为什么要手工编译代码,或者是无法正确使用命令行,也不必太在意,请继续学习下一章内容。如何以及为什么要使用命令行工具,不在本书的讨论范围之内。)

要从命令行使用Gradle,请切换到项目目录并执行以下命令:

$ ./gradlew tasks

如果是Windows系统,执行以下命令:

> gradlew.bat tasks

执行以上命令会显示一系列可用任务。你需要的任务是installDebug,因此,再执行以下命令:

$ ./gradlew installDebug

如果是Windows系统,执行以下命令:

> gradlew.bat installDebug

以上命令将把应用安装到当前连接的设备上,但不会运行它。要运行应用,需要在设备上手动启动。

1.10 关于挑战练习

本书大部分章末都安排有挑战练习,需要你独立完成。有些很简单,就是练习所学知识。有些较难,需要较强的问题解决能力。

希望你一定完成这些练习。攻克它们不仅可以巩固所学知识,树立信心,还可以让自己从被动学习快速成长为自主开发的Android程序员。

尝试完成挑战练习时,若一时陷入困境,可休息休息,理理头绪,重新再来。如果仍然无法解决,可访问本书论坛 ,参考其他读者发布的解决方案。当然你也可以自己发布问题和答案并与其他读者一起交流学习。

为避免搞乱当前项目,建议你在Android Studio中先复制当前项目,然后在复制的项目上做练习。

在你的计算机上,通过文件浏览器找到项目文件的根目录,复制一份GeoQuiz文件并重命名为GeoQuiz Challenge。回到Android Studio中,选择File → Import Project....菜单项,通过导入功能找到GeoQuiz Challenge并导入。这样,复制项目就在新窗口中打开了。开始挑战吧!

1.11 挑战练习:定制toast消息

这个练习需要你定制toast消息,改在屏幕顶部而不是底部显示弹出消息。这需要使用Toast类的setGravity方法,并使用Gravity.TOP重力值。具体如何使用,请参考Android开发者文档。该方法所在网页为 developer.android.com/reference/android/widget/Toast.html#setGravity(int, int, int)。

目录

  • 版权声明
  • 献词
  • 致谢
  • 如何学习Android开发
  • 开发必备工具
  • 第 1 章 Android开发初体验
  • 第 2 章 Android与MVC设计模式
  • 第 3 章 activity的生命周期
  • 第 4 章 Android应用的调试
  • 第 5 章 第二个activity
  • 第 6 章 Android SDK版本与兼容
  • 第 7 章 UI fragment与fragment管理器
  • 第 8 章 使用RecyclerView显示列表
  • 第 9 章 使用布局与组件创建用户界面
  • 第 10 章 使用fragment argument
  • 第 11 章 使用ViewPager
  • 第 12 章 对话框
  • 第 13 章 工具栏
  • 第 14 章 SQLite数据库
  • 第 15 章 隐式intent
  • 第 16 章 使用intent拍照
  • 第 17 章 双版面主从用户界面
  • 第 18 章 应用本地化
  • 第 19 章 Android辅助功能
  • 第 20 章 数据绑定与MVVM
  • 第 21 章 音频播放与单元测试
  • 第 22 章 样式与主题
  • 第 23 章 XML drawable
  • 第 24 章 深入学习intent和任务
  • 第 25 章 HTTP与后台任务
  • 第 26 章 Looper、Handler和HandlerThread
  • 第 27 章 搜索
  • 第 28 章 后台服务
  • 第 29 章 broadcast intent
  • 第 30 章 网页浏览
  • 第 31 章 定制视图与触摸事件
  • 第 32 章 属性动画
  • 第 33 章 地理位置和Play服务
  • 第 34 章 使用地图
  • 第 35 章 material design
  • 第 36 章 编后语
合作: 葡京官网 棋牌评测 新葡京娱乐场