结束 Android 开发(入门)课程 的第一部分《布局和交互》后,来到第二部分《多屏幕应用》,这部分课程要完成一个 Miwok Dictionary 的多屏幕应用,这个应用由 Numbers、Colors、Family Members、Phrases 四个类别的 English 与 Miwok 对照列表以及对应图片和音频文件内容组成,每个列表有一个屏幕 (Activity)。
从现在开始,学员不再是初学者,所以课程的难度开始增加,进度也逐渐加快了。不过练习的机会也变多了,保证了学员能跟上节奏。
Miwok App 分五节课完成,每个课程的进度分配如下:
- 使 App 能切换显示多个屏幕,每个屏幕暂时保持空白,主要处理用户的触摸事件;
- 使 App 显示 English 与 Miwok 对照列表,主要涉及数组 Array 和列表 List 这两个存储数据的 Java 数据结构;
- 改进 App 列表,在单词旁显示图片,微调排版;
- 在 App 列表添加播放音频按钮,播放由 Andrea Delgado-Olsen 录制的 Miwok mp3 文件;
- 利用四个 fragment 将四个屏幕整合到一个 Activity 。
这是第二部分《多屏幕应用》的第一节课,导师是 Katherine Kuan 和 Jessica Lin。
关键词:Implicit Intent & Explicit Intent、AndroidManifest.xml、Event Listeners、Abstract Method、Interface
Activity
通常与他人协作时,无需自己从头开始新建项目,可以从 Github 下载已有项目 zip,解压后在 Android Studio 选择 "Import Project…" 导入工程;或者直接在 Android Studio 初始界面选择 "Check out projects from version control" 导入带版本控制的工程。
作为一名开发者,要学会阅读别人的项目代码,可以通过描绘图表来了解大致的项目结构。对于一些大工程,可以关注一些关键文件,例如 App 的主屏幕 app/src/main/java/MainActivity.java,资源文件 app/src/main/res,包括
- layout 目录下保存的布局 XML 文件;
- mipmap 目录下保存的 App icon 图标图片文件;
- values 目录下保存的 colors 颜色声明 XML 文件、dimens 尺寸 (dp & sp) 声明 XML 文件、strings 字符串声明 XML 文件、styles 主题样式定义 XML 文件。
来看看 MainActivity.java 的文件结构,下面是一个新工程的 MainActivity.java 文件,可分为五部分理解。
package com.example.android.miwok;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the content of the activity to use the activity_main.xml layout file
setContentView(R.layout.activity_main);
}
}
- 第一行,文件包名,由 Java 关键词
package
开头,后面跟着新建项目时指定的唯一包名,用于 Android 设备 和 Play Store 识别 App 的唯一标识。 - 第二部分,import statement,引用 Activity 需要的由 Android 团队提供的 Android 框架代码,主要是 Java Class,随着代码的增多,import 语句也会增多,Android Studio 可开启自动添加 import 声明功能。
- 第三部分,Class statement,
public class MainActivity
代表 MainActivity 的公共类,在声明内的代码包含 MainActivity 类的定义;extends AppCompatActivity
代表这个类继承了 Super Class(超级类)AppCompatActivity 的所有行为,包括在设备上显示窗口和应用栏。 - 第四部分,Method override,即 MainActivity 改写了 AppCompatActivity 的 onCreate method,自定义 App 初始化后执行的代码,一般 App 都会出现这个 override,这也是 Android Studio 自动生成的原因。
- 第五部分,将一个 XML 布局资源文件设置为内容视图。
Android Manifest
在 Android Studio 新建 Activity,可以右键 app/Activity/Empty Activity,输入名称,点击完成。成功新建 Activity 后会自动在 AndroidManifest.xml 添加一个新的 Activity XML 元素。
来看看 AndroidManifest.xml 的文件结构。
每个 App 都有 AndroidManifest.xml 文件,由 Android Studio 自动生成并根据项目进度自动更新,它相当于一个 App 的目录,存储了关于 App 的重要信息,例如
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
设备在安装 App 后,在运行任何代码之前,会首先查看 AndroidManifest.xml 文件。
在 MainActivity 部分,可以看到
,这表示 MainActivity 能够接受和响应 intent 请求,中间包含的两条语句
表示 App 启动时进入的是 MainActivity,相当于 App 启动时 Android 系统向 App 发送了一个 intent,由 MainActivity 接受和响应。
新添加的 Activity 紧接着出现在 MainActivity 后面,支持修改 Activity 的属性,例如 Google 搜索 "Android Manifest Activity tag" 可以找到 Android
Intent
两个 Activity 之间可以通过 intent 联系,这里对之前 Just Java App 对邮件的 intent 作详细介绍,intent 可分为 Explicit intent(显性 intent)和 Implicit intent(隐性 intent)。
- Implicit intent 不指定响应 Activity,通常用于外部 App。
- Explicit intent 须指定响应 Activity,App 内使用,不能用来 intent 外部 App,因为设备上不一定有指定的 App。
两者的代码也有区别。
Implicit intent 代码包含 Action,Data URL,以及帮助 Android 判断什么 App 适合处理此 intent 的 Category,components,extras 可选数据,还有最后 resolveActivity 的判断语句来处理设备上没有 App 响应 intent 的情况。例如
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Explicit intent 代码则较简单,说明 context 和 component(通常是 Class 或 Activity)即可,也可以包含可选的 Data URL。例如
// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
Intent downloadIntent = new Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
Explicit intent 和 Implicit intent 代码的共同点是都包含下面两条语句
Intent downloadIntent = new Intent(this, DownloadService.class);
startService(downloadIntent);
即对象的声明(隐形 intent 需要操作字符串,显性 intent 需要提供 context 和 component(实例化))以及 startService 语句。
Event Listener
当用户点击屏幕时,会由 XML 的 View(onClick 是 View 的属性,Button、TextView、ImageView 都属于 View 的子类,所以 TextView 或 ImageView 也可以使用 onClick 属性)执行 onClick 指定函数,这种行为实际上是 Android 为 Event Listeners(事件监听器)做的快捷方式。事实上,用户交互会触发 Event Listeners,随后事件监听器再识别用户交互的 View。因此,要实现不修改 XML 而仅在 Java 端实现捕捉和响应触摸、长按、拖动等用户交互,可以通过直接操作 Event Listeners 做到。例如要监听触摸事件,可以利用 onClickListener 实现,然后与相关的 View 连接(onClick)进行更改。
首先来看 Event Listeners 作为一种 Interface(接口)的定义:接口只有 Abstract methods(抽象 method,指定返回值、名称、输入参数,分号结束,大括号中间的代码实现由开发者自由发挥的 method)。类似的,还有 Abstract Class(抽象类:部分实现的类,包括状态 state,一些完全实现的 method,一些抽象 method)。如下图所示,从 methods 实现程度来看,“完全实现的类”到“抽象类”再到“接口”是一个连续过程,像 TextView 这种完全实现的类,Android 设计成完全的标准化;到 ViewGroups 这种抽象类,包含了 TextView 等标准化的类,也包含 LinearLayout 等子类是抽象的;再到 onClickListener 这种接口,只规范了抽象的 method,具体实现是开放的(回调函数)。
设置 Event Listeners 的具体操作(代替 XML 的 android:onClick 及其 Java 对应的函数)。
- 定义 Event Listeners 并具化抽象 methods;(引用 OnClickLitstener)
In NumbersActivity.java
public class NumbersClickListner implements OnClickLitstener {
@override
Pubilc void onClick(View view) {
Toast.makeText(view.getContext(), “Open the list of numbers”, Toast.LENTH_SHORT).show();
}
}
- 通过构造函数为 Event Listeners 创建一个新的对象实例
In MainActivity.java
NumbersClickListener clickListener = new NumbersClickListener();
- 为 Event Listeners 连接要操作的 View
In MainActivity.java
Button buttonView = findViewById(R.id.button);
buttonView.setOnClickListener(clickListener);
上述代码可以简化为如下。注意括号配对。
In MainActivity.java
Button buttonView = findViewById(R.id.button);
buttonView.setOnClickListener(new NumbersClickListener(){
@Override
pubilc void onClick(View view) {
Toast.makeText(view.getContext(), "Open the list of numbers", Toast.LENTH_SHORT).show();
}
});
这里直接操作 Event Listener 实现了用户点击按钮时出现一条 "Open the list of numbers" 的 Toast 信息,没有通过 Button 的 android:onClick 属性实现。
完成第一节课后,我做了第四个实战项目:NotEasyMusic 音乐应用结构,项目托管在我的 GitHub 上。主要应用了这节课学习的 OnClickListeners 和 Explicit Intent,详细介绍我写在 GitHub 的 README 上。App 目前的效果如下:
目前它只是一个应用结构,没有实现任何音乐播放器的功能,很多地方都是硬编码,但随着课程深入,我会逐步完善这个 App。
这次项目提交上去后,导师依旧给出很棒的建议,例如在局部变量加上 private 防止外部类修改变量,还有编写函数来批量处理多次的 intent 和 onClickListener。我按照导师给出的建议修改了代码,记录都可以在我的 GitHub 上找到。