結(jié)束 Android 開發(fā)(入門)課程 的第一部分《布局和交互》后,來到第二部分《多屏幕應(yīng)用》集乔,這部分課程要完成一個 Miwok Dictionary 的多屏幕應(yīng)用,這個應(yīng)用由 Numbers箱锐、Colors辫呻、Family Members、Phrases 四個類別的 English 與 Miwok 對照列表以及對應(yīng)圖片和音頻文件內(nèi)容組成蚯根,每個列表有一個屏幕 (Activity)后众。
從現(xiàn)在開始,學(xué)員不再是初學(xué)者颅拦,所以課程的難度開始增加蒂誉,進度也逐漸加快了。不過練習(xí)的機會也變多了矩距,保證了學(xué)員能跟上節(jié)奏拗盒。
Miwok App 分五節(jié)課完成,每個課程的進度分配如下:
- 使 App 能切換顯示多個屏幕锥债,每個屏幕暫時保持空白陡蝇,主要處理用戶的觸摸事件痊臭;
- 使 App 顯示 English 與 Miwok 對照列表,主要涉及數(shù)組 Array 和列表 List 這兩個存儲數(shù)據(jù)的 Java 數(shù)據(jù)結(jié)構(gòu)登夫;
- 改進 App 列表广匙,在單詞旁顯示圖片,微調(diào)排版恼策;
- 在 App 列表添加播放音頻按鈕鸦致,播放由 Andrea Delgado-Olsen 錄制的 Miwok mp3 文件;
- 利用四個 fragment 將四個屏幕整合到一個 Activity 涣楷。
這是第二部分《多屏幕應(yīng)用》的第一節(jié)課分唾,導(dǎo)師是 Katherine Kuan 和 Jessica Lin。
關(guān)鍵詞:Implicit Intent & Explicit Intent狮斗、AndroidManifest.xml绽乔、Event Listeners、Abstract Method碳褒、Interface
Activity
通常與他人協(xié)作時折砸,無需自己從頭開始新建項目,可以從 Github 下載已有項目 zip沙峻,解壓后在 Android Studio 選擇 "Import Project…" 導(dǎo)入工程睦授;或者直接在 Android Studio 初始界面選擇 "Check out projects from version control" 導(dǎo)入帶版本控制的工程。
作為一名開發(fā)者摔寨,要學(xué)會閱讀別人的項目代碼去枷,可以通過描繪圖表來了解大致的項目結(jié)構(gòu)。對于一些大工程祷肯,可以關(guān)注一些關(guān)鍵文件沉填,例如 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 的文件結(jié)構(gòu)关摇,下面是一個新工程的 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 關(guān)鍵詞
package
開頭脂凶,后面跟著新建項目時指定的唯一包名宪睹,用于 Android 設(shè)備 和 Play Store 識別 App 的唯一標識愁茁。 - 第二部分,import statement亭病,引用 Activity 需要的由 Android 團隊提供的 Android 框架代碼鹅很,主要是 Java Class,隨著代碼的增多罪帖,import 語句也會增多促煮,Android Studio 可開啟自動添加 import 聲明功能。
- 第三部分整袁,Class statement菠齿,
public class MainActivity
代表 MainActivity 的公共類,在聲明內(nèi)的代碼包含 MainActivity 類的定義葬项;extends AppCompatActivity
代表這個類繼承了 Super Class(超級類)AppCompatActivity 的所有行為泞当,包括在設(shè)備上顯示窗口和應(yīng)用欄。 - 第四部分民珍,Method override,即 MainActivity 改寫了 AppCompatActivity 的 onCreate method盗飒,自定義 App 初始化后執(zhí)行的代碼嚷量,一般 App 都會出現(xiàn)這個 override,這也是 Android Studio 自動生成的原因逆趣。
- 第五部分蝶溶,將一個 XML 布局資源文件設(shè)置為內(nèi)容視圖。
Android Manifest
在 Android Studio 新建 Activity宣渗,可以右鍵 app/Activity/Empty Activity抖所,輸入名稱,點擊完成痕囱。成功新建 Activity 后會自動在 AndroidManifest.xml 添加一個新的 Activity XML 元素田轧。
來看看 AndroidManifest.xml 的文件結(jié)構(gòu)。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.miwok">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".NumbersActivity"
android:label="@string/category_numbers" />
</application>
</manifest>
每個 App 都有 AndroidManifest.xml 文件鞍恢,由 Android Studio 自動生成并根據(jù)項目進度自動更新傻粘,它相當于一個 App 的目錄,存儲了關(guān)于 App 的重要信息帮掉,例如
<!-- 保存 App 的 icon 圖標 -->
android:icon="@mipmap/ic_launcher"
<!-- 保存 App 在應(yīng)用欄顯示的名稱 -->
android:label="@string/app_name"
<!-- 保存 App 的主題樣式路徑 -->
android:theme="@style/AppTheme"
設(shè)備在安裝 App 后弦悉,在運行任何代碼之前,會首先查看 AndroidManifest.xml 文件蟆炊。
在 MainActivity 部分稽莉,可以看到 <intent-filter>
,這表示 MainActivity 能夠接受和響應(yīng) intent 請求涩搓,中間包含的兩條語句
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
表示 App 啟動時進入的是 MainActivity污秆,相當于 App 啟動時 Android 系統(tǒng)向 App 發(fā)送了一個 intent劈猪,由 MainActivity 接受和響應(yīng)。
新添加的 Activity 緊接著出現(xiàn)在 MainActivity 后面混狠,支持修改 Activity 的屬性岸霹,例如 Google 搜索 "Android Manifest Activity tag" 可以找到 Android <activity> 文檔,其中 android:label 屬性可以修改 Activity 在應(yīng)用欄顯示的標簽名将饺。
Intent
兩個 Activity 之間可以通過 intent 聯(lián)系贡避,這里對之前 Just Java App 對郵件的 intent 作詳細介紹,intent 可分為 Explicit intent(顯性 intent)和 Implicit intent(隱性 intent)予弧。
- Implicit intent 不指定響應(yīng) Activity刮吧,通常用于外部 App。
- Explicit intent 須指定響應(yīng) Activity掖蛤,App 內(nèi)使用杀捻,不能用來 intent 外部 App,因為設(shè)備上不一定有指定的 App蚓庭。
兩者的代碼也有區(qū)別致讥。
Implicit intent 代碼包含 Action,Data URL器赞,以及幫助 Android 判斷什么 App 適合處理此 intent 的 Category垢袱,components,extras 可選數(shù)據(jù)港柜,還有最后 resolveActivity 的判斷語句來處理設(shè)備上沒有 App 響應(yīng) 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 屬性)執(zhí)行 onClick 指定函數(shù)奢啥,這種行為實際上是 Android 為 Event Listeners(事件監(jiān)聽器)做的快捷方式秸仙。事實上,用戶交互會觸發(fā) Event Listeners桩盲,隨后事件監(jiān)聽器再識別用戶交互的 View寂纪。因此,要實現(xiàn)不修改 XML 而僅在 Java 端實現(xiàn)捕捉和響應(yīng)觸摸、長按捞蛋、拖動等用戶交互孝冒,可以通過直接操作 Event Listeners 做到。例如要監(jiān)聽觸摸事件拟杉,可以利用 onClickListener 實現(xiàn)庄涡,然后與相關(guān)的 View 連接(onClick)進行更改。
首先來看 Event Listeners 作為一種 Interface(接口)的定義:接口只有 Abstract methods(抽象 method搬设,指定返回值穴店、名稱、輸入?yún)?shù)拿穴,分號結(jié)束泣洞,大括號中間的代碼實現(xiàn)由開發(fā)者自由發(fā)揮的 method)。類似的默色,還有 Abstract Class(抽象類:部分實現(xiàn)的類球凰,包括狀態(tài) state,一些完全實現(xiàn)的 method腿宰,一些抽象 method)呕诉。如下圖所示,從 methods 實現(xiàn)程度來看吃度,“完全實現(xiàn)的類”到“抽象類”再到“接口”是一個連續(xù)過程义钉,像 TextView 這種完全實現(xiàn)的類,Android 設(shè)計成完全的標準化规肴;到 ViewGroups 這種抽象類,包含了 TextView 等標準化的類夜畴,也包含 LinearLayout 等子類是抽象的拖刃;再到 onClickListener 這種接口,只規(guī)范了抽象的 method贪绘,具體實現(xiàn)是開放的(回調(diào)函數(shù))兑牡。
設(shè)置 Event Listeners 的具體操作(代替 XML 的 android:onClick 及其 Java 對應(yīng)的函數(shù))。
- 定義 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();
}
}
- 通過構(gòu)造函數(shù)為 Event Listeners 創(chuàng)建一個新的對象實例
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 實現(xiàn)了用戶點擊按鈕時出現(xiàn)一條 "Open the list of numbers" 的 Toast 信息菱涤,沒有通過 Button 的 android:onClick 屬性實現(xiàn)苞也。
完成第一節(jié)課后,我做了第四個實戰(zhàn)項目:NotEasyMusic 音樂應(yīng)用結(jié)構(gòu)粘秆,項目托管在我的 GitHub 上如迟。主要應(yīng)用了這節(jié)課學(xué)習(xí)的 OnClickListeners 和 Explicit Intent,詳細介紹我寫在 GitHub 的 README 上。App 目前的效果如下:
目前它只是一個應(yīng)用結(jié)構(gòu)殷勘,沒有實現(xiàn)任何音樂播放器的功能此再,很多地方都是硬編碼,但隨著課程深入玲销,我會逐步完善這個 App输拇。
這次項目提交上去后,導(dǎo)師依舊給出很棒的建議贤斜,例如在局部變量加上 private 防止外部類修改變量策吠,還有編寫函數(shù)來批量處理多次的 intent 和 onClickListener。我按照導(dǎo)師給出的建議修改了代碼蠢古,記錄都可以在我的 GitHub 上找到奴曙。