开发你的第一个 Android 应用

发布时间:2021-08-01 04:38:50

题图 | Technology vector created by stories - www.freepik.com


以下内容节选自经典权威 Android 入门与进阶图书


《Android编程权威指南(第4版)》


顺便,广而告知下,本书电子版已经上架,一直催更电子版的同学,Finally,终于上架了!文末通过「阅读原文」去图灵社区购买。


「进入干货,长,准备好」


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


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


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



图1-1 你是澳洲人吗


1.1 Android开发基础

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


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

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

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

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

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


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



图1-2 MainActivity管理着activity_main.xml定义的用户界面


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


1.2 创建Android项目

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


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



图1-3 欢迎使用Android Studio


创建新项目之前,请先关闭Android Studio的Instant Run功能。这项功能的设计初衷是提高开发效率。代码修改后,无须生成新APK,开发人员就能立即看到变化。不过,很可惜,它的实际表现不及预期,因此建议一开始就彻底禁用这一功能。


在欢迎界面的底部,点击Configure,再选择Settings,会弹出如图1-4所示的新项目首选项界面。展开左边的Build, Execution, Deployment选项并选中Instant Run,取消勾选Enable Instant Run to hot swap code/resource changes on deploy (default enabled),然后点击OK按钮。



图1-4 新项目首选项


(如果之前用过Android Studio工具,看不到欢迎界面的话,可以通过选择Android Studio →Preferences菜单项,然后扩展Build, Execution, Deployment选项并继续上面的操作。)


回到欢迎界面,选择创建新项目选项(Start a new Android Studio project);如果并非首次运行Android Studio,请选择File → New → New Project…菜单项。


现在,你应该打开了新建项目向导界面,如图1-5所示。确认选中Phone and Tablet选项页和Empty Activity,然后点击Next按钮继续。



图1-5 选择项目模板


配置项目窗口弹出了。在此界面的应用名称(Name)处输入GeoQuiz。在包名(Package name)处输入com.bignerdranch.android.geoquiz。至于项目存储位置(Save location),就看个人喜好了。接下来开发语言选Kotlin,SDK最低版本选API 21: Android 5.0 (Lollipop)。第7章会介绍Android不同SDK版本的差异。最后,勾选Use AndroidX artifacts,完成后的界面如图1-6所示。



图1-6 配置新项目


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


本书撰写时,Android Studio新建项目默认使用Java语言。选Kotlin是让Android Studio准备好该语言相关的各种工具和依赖,以便编写和构建Kotlin应用。


一直以来,Java是Android开发唯一的官方支持语言,直到2017年5月,Android开发团队在Google I/O大会上宣布Kotlin为Android开发又一官方支持语言。如今,包括我们在内,Kotlin已成为大多数开发人员的首选语言。如果你的项目依然选用Java也没关系,本书所教概念和内容同样适用。


过去,Google一直维护着庞大的支持库,用来协助开发和解决兼容性问题。作为改进,AndroidX将这个巨型库拆分为一个个独立的开发和版本库,统称为Jetpack。勾选Use AndroidX artifacts就是让新项目能用上这些独立工具库。第4章将详细介绍AndroidX和Jetpack,本书中会用到各种各样的Jetpack库。


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


点击Finish按钮,Android Studio会完成创建并打开新项目。


1.3 Android Studio使用导航

如图1-7所示,Android Studio已在工作区窗口里打开新建项目。如果并非首次运行Android Studio,你看到的窗口配置可能稍有不同。



图1-7 新的项目窗口


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


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


工作区底部是构建工具窗口(build tool window),可以在这里看到项目的编译过程和构建状态。新建项目时,Android Studio会自动进行项目构建。可以看到,构建工具窗口显示构建已成功完成。


在项目工具窗口中,点击app旁边的展开箭头,Android Studio会自动打开activity_main.xml和MainActivity.kt文件。如图1-8所示,打开文件所在的区域叫编辑工具窗口(editor tool window),或直接叫代码编辑区(editor)。依然要提醒的是,如果并非首次运行Android Studio,代码编辑区会自动打开所建项目文件。



图1-8 编辑工具窗口


注意Activity类名的前缀,此前缀不加也可以,但这是个很好的命名约定,建议遵循。


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


1.4 用户界面设计

点击activity_main.xml布局文件页,会在编辑工具窗口打开布局编辑器,如图1-9所示。如果看不到布局文件,请在项目工具窗口展开app/res/layout/找到它并双击打开。如果看到的是activity_main.xml文件的XML代码,请点击底部的Design页,切换显示布局预览。



图1-9 布局编辑器


按照约定,布局文件的命名基于其关联的activity:activity_作为前缀,activity子类名的其余部分全部转小写并紧随其后,单词之间以下划线隔开。例如,当前新建项目的布局文件名为activity_main.xml,或者说你有个activity名为SplashScreenActivity,那么对应的布局就命名为activity_splash_screen。对于后续章节中的所有布局以及将要学*的其他资源,都建议采用这种命名风格。


布局编辑器展示的是文件的图形化预览界面,你可以点击底部的Text页切换显示布局的XML代码。


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



代码清单1-1 默认的activity布局(res/layout/activity_main.xml)



xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

应用activity的默认布局定义了两个视图(view):ConstraintLayoutTextView


视图是用户界面的构造模块。显示在屏幕上的一切都是视图。用户能看到并与之交互的视图称为部件(widget)。有些部件可以用来显示文字或图像,有些部件(比如按钮)可以点击以触发事件任务。


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


我们得想办法告诉部件它们在屏幕上该位于哪里。ViewGroup就是这样一种特殊的View,它包含并布置其他视图。ViewGroup视图本身不显示内容,它规划其他视图内容应该显示在哪里。ViewGroup通常又称为布局。


在当前默认布局里,ConstraintLayout这个ViewGroup布置了一个TextView部件,这是它唯一的子部件。有关布局和部件的知识,以及如何使用ConstraintLayout,第10章将详述。


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



图1-10 显示在屏幕上的默认视图


不过,图1-10所示的默认部件并不是我们需要的,MainActivity的用户界面需要以下五个部件:


一个垂直LinearLayout部件;

一个TextView部件;

一个水*LinearLayout部件;

两个Button部件。


图1-11展示了以上部件是如何构成MainActivity用户界面的。



图1-11 布置并显示在屏幕上的部件


下面我们在布局XML文件中定义这些部件。对照代码清单1-2,修改activity_main.xml文件内容。注意,需删除的XML代码已打上删除线,需添加的XML以粗体显示。本书统一使用这样的代码增删处理模式。



代码清单1-2 在XML文件中定义部件(res/layout/activity_main.xml)



xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/> android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">

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

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

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

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


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


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


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


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


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


1.4.1 视图层级结构

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



图1-12 布局部件的层级结构


从布局的视图层级结构可以看到,其根元素是一个LinearLayout部件。作为根元素,LinearLayout部件必须指定Android XML资源文件的命名空间属性。


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


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


1.4.2 部件属性

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


    android:layout_widthandroid:layout_height属性

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

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

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

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


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

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


android:orientation属性

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

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

android:text属性

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

请注意,android:text属性值不是字符串值,而是以@string/语法形式对字符串资源(string resource)的引用。

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

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

1.4.3 创建字符串资源

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


打开res/values/strings.xml文件,可以看到,项目模板已经添加了一个字符串资源。如代码清单1-3所示,添加应用布局需要的三个新字符串。



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




GeoQuiz
Canberra is the capital of Australia.
True
False

(Android Studio某些版本的strings.xml默认带有其他字符串,这些字符串可能与其他文件有关联,请勿随意删除。)


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


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


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


1.4.4 预览布局

至此,应用的界面布局已经完成,可以使用图形布局工具实时预览了。回到activity_main.xml文件,在编辑器工具窗口的底部点击Design页进行布局预览,结果如图1-13所示。



图1-13 在Design页预览activity_main.xml布局


图1-13展示了两种布局预览模式。在工具栏左上角,有个钻石按钮,我们可以通过它的下拉菜单切换显示不同的布局预览模式??设计(Design)预览或蓝图(Blueprint)预览,或者并排显示设计预览和蓝图预览。


在图1-13中,左边是设计预览模式,用来展示布局在设备上的效果,也包括主题样式;右边是蓝图预览模式,用来展示部件的尺寸以及它们之间的位置关系。


在设计预览模式下,你还可以查看布局在不同的设备配置下的样子。通过预览窗口上方的面板,可以指定设备类型、Android模拟器版本、设备主题以及设备使用区域,查看布局的不同渲染结果。你甚至可以模拟某个语言区域的自右到左的文字显示模式。


除了预览,你也可以直接使用布局编辑器摆放部件,布置布局。如图1-14所示,项目窗口左边有个面板,包括了Android所有的内置部件。你可以将它们从面板拖曳到视图上,或者拖到左下方的部件树上,更精准地控制如何摆放部件。



图1-14 图形化布局编辑器


图1-14展示了带布局装饰(layout decoration)的布局预览。这些装饰元素有设备状态栏、带GeoQuiz标签的应用栏,以及虚拟设备按钮栏。要添加这些装饰,点击预览窗口上方工具栏中的眼睛图标,选择Show Layout Decorations菜单项即可。


图形化布局编辑器非常有用,尤其是在使用ConstraintLayout时,后面学*第10章内容时,你将有所体会。


1.5 从布局XML到视图对象

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


在创建GeoQuiz项目的同时,向导也创建了一个名为MainActivityActivity子类。MainActivity类文件存放在项目的app/java目录下。


继续学*之前,就app/java这个目录名问题,简单说两句:这里依然使用java作为目录名是因为Android之前仅支持Java语言。新建项目时,我们虽然选了Kotlin语言(不过Kotlin可以和Java完全互操作),但Kotlin源码默认还是放在java目录里。当然,你完全可以新建一个Kotlin目录,把Kotlin代码文件都移过去。但前提是,你要明确告诉Android Studio:源码放在新文件夹里了,请帮它们添加到项目里。大多数情况下,按语言区分管理源码文件意义不大,所以绝大多数项目接受Kotlin文件存放在java目录里。


MainActivity.kt文件应该已经在编辑器窗口打开了,如果没有,在项目工具窗口中,依次展开app/java目录与com.bignerdranch.android.geoquiz包。(注意,以灰绿色显示包名的是测试包。生产包名并未加灰。)找到并打开MainActivity.kt文件,查看其中的代码,如代码清单1-4所示。



代码清单1-4 默认MainActivity类文件(MainActivity.kt)



package com.bignerdranch.android.geoquiz

import androidx.appcompat.app.AppCompatActivityimport android.os.BundleclassMainActivity:AppCompatActivity(){

override fun onCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}}

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


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


该类文件有一个Activity函数:onCreate(Bundle?)


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


Activity.setContentView(layoutResID:Int)

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


资源与资源ID

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


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


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


查看GeoQuiz应用的资源ID需要切换项目视角,你必须勇闯自动生成代码的世界??Android构建工具为你编写的代码。首先,点击Android Studio窗口顶部工具栏上的锤子按钮运行编译工具。


如图1-15所示,Android Studio默认使用Android项目视角。为让开发者专注于最常用的文件和目录,默认项目视角隐藏了Android项目的真实文件目录结构。在项目工具窗口的最上部找到下拉菜单,从Android视角切换至Project视角。Project视角会显示出当前项目的所有文件和目录。



图1-15 项目工具窗口:Android视角与Project视角


在Project视角下,逐级展开GeoQuiz目录,直至看到GeoQuiz/app/build/generated/not_namespaced_r_class_sources/debug/processDebugResources/r/,再找到项目包名以及其中的R.java文件,如图1-16所示。



图1-16 查看R.java文件


双击打开R.java文件。它是在Android项目编译过程中自动生成的,所以如该文件头部的警示所述,请不要修改该文件的内容,如代码清单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;publicfinalclass R {
publicstaticfinalclass anim {
...
}
...
publicstaticfinalclass id {
...
}
publicstaticfinalclass layout {
...
publicstaticfinalInt activity_main=0x7f030017;
}
publicstaticfinalclass mipmap {
publicstaticfinalInt ic_launcher=0x7f030000;
}
publicstaticfinalclassstring{
...
publicstaticfinalInt app_name=0x7f0a0010;
publicstaticfinalInt false_button=0x7f0a0012;
publicstaticfinalInt question_text=0x7f0a0014;
publicstaticfinalInt true_button=0x7f0a0015;
}}

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


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


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


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


setTitle(R.string.app_name)

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


要为部件生成资源ID,请在定义部件时为其添加android:id属性。如代码清单1-6所示,在activity_main.xml文件中,分别为两个按钮添加android:id属性(需要从布局预览模式切换至XML代码模式)。



代码清单1-6 为按钮添加资源ID(res/layout/activity_main.xml)





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

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

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

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


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


继续学*之前,关闭R.java文件,从Project视角切回至Android视角。本书主要使用Android视角,当然,如果你就喜欢使用Project视角,也没有问题。


1.6 部件的实际应用

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


引用生成的视图对象;

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


1.6.1 引用部件

既然按钮有了资源ID,我们就可以在MainActivity中引用它们了。在MainActivity.kt文件中输入代码清单1-7所示的代码(不要使用代码自动补全功能,直接手动输入)。保存文件时,会看到代码错误提示,不用理会,稍后会修复。



代码清单1-7 通过资源ID访问视图对象(MainActivity.kt)



classMainActivity:AppCompatActivity(){

private lateinit var trueButton:Button
private lateinit var falseButton:Button

override fun onCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

trueButton = findViewById(R.id.true_button)
falseButton = findViewById(R.id.false_button)
}}

在activity中,可以调用Activity.findViewById(Int)函数引用已生成的部件。该函数以部件的资源ID作为参数,返回一个视图对象。不过,这里直接返回的不是View视图,而是其已做类型转换后的Button子类。


在上述代码中,我们使用按钮的资源ID获取视图对象,赋值给对应的视图属性。既然只有在onCreate(...)函数里调用setContentView(...)函数后,视图对象才会实例化到内存里,那么在属性声明时,我们就得使用lateinit修饰符。这实际是告诉编译器,在使用属性内容时,我们会保证提供非空的View值。然后,在onCreate(...)中,找到视图对象并赋值给对应的视图属性。第3章还会深入学*onCreate(...)函数和activity生命周期的知识。


现在让我们来修正前面的代码错误。将鼠标移动到红色的错误指示处,可以看到两个相同的错误提示:Unresolved reference: Button。


这实际是告诉你,要在MainActivity.kt文件中导入android.widget.Button类。你可以在Kotlin文件的头部手动输入import android.widget.Button,也可以使用Option+Return(或Alt+Enter)快捷键,让Android Studio自动为你导入。可以看到,文件顶部有了新的类导入语句。当代码遇到类引用相关问题时,这种快速导入方法往往很有用,建议经常采用。


现在,代码错误提示应该消失了(如果仍然有错误,记得检查代码或XML文件,确认无输入错误)。代码错误解决了,接下来是时候让应用支持交互了。


1.6.2 设置监听器

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


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


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


首先处理TRUE按钮。在MainActivity.kt文件中,在onCreate(Bundle?)函数的变量赋值语句后输入代码清单1-8所示的代码。



代码清单1-8 为TRUE按钮设置监听器(MainActivity.kt)



override fun onCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

trueButton = findViewById(R.id.true_button)
falseButton = findViewById(R.id.false_button)

trueButton.setOnClickListener { view:View->
// Do something in response to the click here
}}

(如果遇到Unresolved reference: View错误提示,请使用Option+Return(Alt+Enter)快捷键导入View类。)


在代码清单1-8中,我们设置了一个监听器。按钮trueButton被点击后,监听器会立即通知我们。Android框架定义了View.OnClickListener这样只有一个onClick(View)单方法的Java接口。在Java世界里,这种带有单一抽象方法(single abstract method)的接口设计模式很常见,它有个专门的名字叫SAM。


作为和Java互操作实现的一部分,Kotlin对此模式设计有特别支持。你只需编写一个函数字面量(function literal),让Kotlin负责将其转换为实现这种SAM接口的对象。这种内部转换又叫作SAM转换(SAM conversion)。


这里,点击监听器是使用lambda表达式实现的。参照代码清单1-9为FALSE按钮设置类似的事件监听器。



代码清单1-9 为FALSE按钮设置监听器(MainActivity.kt)



override fun onCreate(savedInstanceState:Bundle?){
...
trueButton.setOnClickListener { view:View->
// Do something in response to the click here
}

falseButton.setOnClickListener { view:View->
// Do something in response to the click here
}}

1.7 创建提示消息

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



图1-17 toast消息反馈


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



代码清单1-10 增加toast字符串(res/values/strings.xml)




GeoQuiz
Canberra is the capital of Australia.
True
False
Correct!
Incorrect!

接下来更新监听器代码以创建并展示toast消息。输入代码时可利用Android Studio的代码自动补全功能,这可以节省大量时间,所以越早熟悉它的使用越好。


参照代码清单1-11,在MainActivity.kt文件中依次输入代码。当输入到Toast类后的点号时,Android Studio会弹出一个窗口,给出建议使用的Toast类的常量与函数。


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


在建议列表里,选择makeText(context: Context, resId: Int, duration: Int),代码自动补全功能会自动添加完整的函数调用。


完成makeText(...)函数的全部参数设置,完成后的代码如代码清单1-11所示。



代码清单1-11 创建提示消息(MainActivity.kt)



override fun onCreate(savedInstanceState:Bundle?){
...
trueButton.setOnClickListener { view:View->
// Do something in response to the click here
Toast.makeText(
this,
R.string.correct_toast,
Toast.LENGTH_SHORT)
.show()
}

falseButton.setOnClickListener { view:View->
// Do something in response to the click here
Toast.makeText(
this,
R.string.incorrect_toast,
Toast.LENGTH_SHORT)
.show()
}}

为了创建toast,我们调用了Toast.makeText(Context!, Int, Int)静态函数。该函数会创建并配置Toast对象。该函数的Context参数通常是Activity的一个实例(Activity本身就是Context的子类)。这里,我们传入MainActivity作为Context值参。


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


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


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


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


1.8 使用模拟器运行应用

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


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


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



图1-18 选择虚拟设备


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



图1-19 选择系统镜像


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



图1-20 模拟器参数调整


AVD创建成功后,就可以用它运行GeoQuiz应用了。点击Android Studio工具栏上的Run按钮,或使用Control+R快捷键。在随后出现的Select Deployment Target 对话框里,选中刚才配置的虚拟设备后点击OK按钮,Android Studio会启动它,安装应用包(APK)并运行应用。


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


假如启动时或在点击按钮时GeoQuiz应用崩溃,可以在Android的LogCat工具窗口中看到有用的诊断信息。(如果LogCat没有自动打开,可点击Android Studio窗口底部的Logcat按钮打开它。)在LogCat工具窗口的搜索对话框中输入MainActivity可过滤日志信息。如图1-21所示,查看日志,可看到抢眼的红色1异常信息。


1本书彩图可到图灵社区本书页面“随书下载”处查看。??编者注



图1-21 NullPointerException异常示例


将你输入的代码与书中的代码做一下比较,找出错误并修正,然后尝试重新运行应用(第3章和第5章还会深入介绍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开发文档。)


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



图1-22 activity_main.xml中的视图实例化


(除了在XML文件中定义视图外,也可以在activity里使用代码创建视图类。不过,从设*嵌壤纯矗τ谜瓜植阌肼呒惴掷胗泻芏嗪么Γ渲凶钪饕囊坏闶强梢岳肧DK内置的设备配置变更,这一点将在第3章中详细讲解。)


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


Android编译工具

当前,我们看到的项目编译都是在Android Studio里执行的。编译功能已整合到IDE中,IDE负责调用aapt2等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开发者文档。


节选自


经典权威Android入门与进阶图书


《Android编程权威指南(第4版)》



作者:Kristin Marsicano,Brian Gardner,Bill Phillips,Chris Stewart


译者:王明发?


| 图书特色


实战项目引导,全面覆盖 Android 开发知识点

使用 Kotlin 编写,兼容 Android 5.0 至 11.0

?随书附赠 Android 开发速查表,随用随查

业内专家张鸿洋、张涛、丰生强、丁志虎、2BAB 推荐阅读


Big Nerd Ranch 是美国一家专业的移动开发技术培训机构。本书主要以其 Android 训练营教学课程为基础,融合了几位作者多年的心得体会,是一本完全面向实战的 Android 编程权威指南。全书共 32 章,详细介绍了 7 个 Android 应用的开发过程。通过这些精心设计的应用,读者可掌握很多重要的理论知识和开 发技巧,获得宝贵的开发经验。


第 4 版较之前版本做了重大更新,每一章的内容都做了修改。开发语言从 Java 换成了 Kotlin。全面引入了 Android Jetpack 组件库并开始使用第三方库。


京东传送门


相关文档

  • 无翼鸟常识变换口哨
  • 演讲比赛主持人串词
  • 生产把颜色做错了怎么办
  • 二年级小学生学画画美术作品
  • 笔记本无线网络为什么老是掉线
  • 糖尿病吃什么养胃
  • 深入理解二叉树(超详细)
  • 诗词里表达爱的句子
  • 每天都有黑眼圈怎么回事
  • 社区道德讲堂活动总结
  • 同学对老师感谢的话语
  • InvokeHelper函数的用法
  • 办公室文员年终个人工作总结三篇
  • 小学四年级语文上册复习试题
  • 前端向后端传值(下拉框的值)
  • 2015中考高分经验分享:注重改善课堂效率
  • 九年级班主任个人德育工作总结
  • 世界最记仇的5种狗 二哈上榜,吉娃娃脾气最大
  • 2018上半年湖北软考时间:5月26日、27日:湖北2018下半年软考
  • NDK编译多个cpp文件 报Please include it in the appropriate build file(build.gradle,CMakeLists.txt or Android
  • 618淘宝什么时候开始
  • Android 实现全局dialog
  • CentOS7下python环境基础配置
  • 文字控的爱情伤感语录
  • Qt之打开文件、图片、载入文件
  • 怀孕后期要多走走吗怀孕后期要多走还是少走
  • 个性空间说说素材
  • 高考数学学习态度
  • C++程序设计之四书五经(上篇)
  • 九年化学上册教材知识点分析
  • 猜你喜欢

  • 民宗局度上半年工作总结及下半年工作计划
  • 数学知识点苏教版五年级下册《等式的性质》word教案之一-总结
  • 关于数学老师的工作总结
  • 第十一章第2节常数项级数审敛法42720
  • 法律英语课件LegalEnglishcasebrief案例摘要
  • 【教育资料】【教学设计】《伸出你的手 》(人民出版社道德与法治九年级上册)学习专用
  • 高考化学知识点120个
  • 派出所实*个人总结
  • 莱芜市奇迈经贸有限公司企业信用报告-天眼查
  • 四川省国家税务局关于实行个体工商户按季申报缴纳增值税消费税和
  • 2019年森林运动会作文500字
  • 最新-审计局局长在全市工作会议上的发言材料 精品
  • 经典唯美的爱情语录短句
  • 成都蜀浩电力实业有限责任公司(企业信用报告)- 天眼查
  • 工程造价审计风险与防范对策的论述
  • mac下hadoop 2.6.0编译native library
  • 6_BA和NAA对诱导彩叶草芽和生根的影响
  • 国家气象中心远程视频会议系统Video矩阵切换器采购需求
  • 某污水处理厂化验室安全管理制度
  • 宝宝不爱吃饭 饿一饿真的有效?
  • 项目经理考试培训笔记---再次整理
  • 甘肃腊八节的*俗
  • 猛虎中心小学主动参与式三步教学设计
  • 初中数学思想方法的教学研究
  • 广西师范学院08专实*计划
  • 普通高校大学生音乐鉴赏能力的培养
  • 2019年高三关于国庆趣事的作文400字
  • 中国萤石矿山生产企业召开座谈会应对金融危机
  • 2018最新心得体会范本模板-小学语文课改心得体会
  • 基于物联网的电子商务
  • 随笔花瓶 初中作文【1200字】
  • 血性山谷观后感
  • 项目的自查报告范文
  • 部分网站为什么上不去_逆冬:网站基础决定排名高度、快排被识别整站降权实战解决方法...
  • 四川化工职业技术学院毕业证样本学位证样本历任校(院)长学校代码
  • 人死后去阴间的全过程是怎么样的
  • 七年级我的好朋友作文400字汇编六篇
  • 苏教版小学五年级语文下册期末测试题
  • 合生创展2019年广州珠江帝景新品品牌提升的方案-文档资料
  • 交口县温泉乡蒲田种养殖专业合作社(企业信用报告)- 天眼查
  • 读了任正非谈管理八、九章
  • 【最新推荐】酒店接待简洁辞职报告-实用word文档 (1页)
  • 电脑版