[Google Course] Android Basics in Kotlin(第8篇) — Activity and Intent

Phoebe Huang
16 min readSep 26, 2021

--

相信各位Android 開發者在一開始學習Android 的時候第一個接觸到的元件就是Activity了,Activity 是一個UI元件,提供一個單一視窗的進入點來與使用者互動,像是一個E-mail 應用程式會有一個 Activity 來顯示新信件的清單,還會有另一個Activity 來負責發送編輯新信件
也因為每一個 Activity 都有獨立的互動任務,因此我們才能夠在Email 應用程式允許下,透過另一個App 開啟發送編輯新信件的Activity

而當我們需要透過一個App 開啟另一個 App的時候,我們就會使用到Intent,Intent 又分為顯性(explicit)和隱性(implicit)

Explicit Intent : 當我們明確指定我們要開啟哪一個APP的Activity,那我們就是在使用 Explicit Intent

val intent = Intent(context, WordActivity::class.java)
intent.putExtra("Key", "Data")
context.startActivity(intent)

Implicit Intent : 隱性 Intent比較抽象一點,我們可以透過隱性 Intent 要求系統執行某種種類的動作,像是開啟瀏覽器,系統會負責去決定如何完成
又或者系統會跳出一個選單提示使用者自己選擇要使用哪一個瀏覽器,因此透過隱性 Intent 我們在程式中並不用決定要開啟 chrome 或是 firefox 的 Activity (以下是其中一種 action)

val queryUrl: Uri = Uri.parse("${URL}${item}")
val intent = Intent(Intent.ACTION_VIEW, queryUrl)
context.startActivity(intent)

除了 Intent.ACTION_VIEW ,還有很多其他種 action,也可以前往官方網站查看:

● CATEGORY_APP_MAPS — 開啟地圖相關的 APP
● CATEGORY_APP_EMAIL — 開啟Email 相關的APP
● CATEGORY_APP_GALLERY — 開啟相簿相關的APP
● ACTION_DIAL — 初始化撥打電話

圖片來源 : Google

在這篇教學裡,我們會製作一個單字APP來學習這兩種 Intent :

圖片來源 : Google & Phoebe

如果只想知道 顯性跳轉隱性跳轉 做法,可以直接跳到連結地方唷!!!

而這個 APP 的流程很簡單,首先第一個頁面呈現的是所有英文字母的列表,按下後透過顯性跳轉到第二個頁面,第二個頁面顯示的是英文字母的相關單字列表,單字列表按下去後透過隱性跳轉到瀏覽器並傳送我們要搜尋的網址:https://www.google.com/search?q=about,讓瀏覽器進入網址,那就讓我們開始吧!!

為了怕有些人沒有看之前的文章,文章中會使用到的編譯環境與參考內容也在這邊列出給大家:
Kotlin 線上編譯環境
Android Studio 安裝
Google Course 的 Android Basics in Kotlin

[建立專案]

建立新的一個專案叫做Word,先加入我們需要的字串資源以及啟用viewBinding :

● 在 res — > values 新增 arrays.xml 資源檔,加入陣列字串資源:

到我的Github arrays.xml 資源檔複製內容,裡面是我們的APP 需要的單字分類陣列,貼上在APP 新增的 arrays.xml

● 在strings.xml 加入以下字串資源:

<string name="word_prefix">Words That Start With</string>
<string name="action_switch_layout">Switch Layout</string>
<string name="look_up_word">Look up word in a Browser Search</string>
<string name="look_up_words">Show Stored Words</string>

● 開啟APP 層的 build.gradle ,在andoird{…} 內加入以下區塊:

buildFeatures {
viewBinding = true
}

[MainActivity]

接著我們就可以開始來設計我們的第一個頁面,開啟 MainActivity ,加入 viewBinding 的類別變數以及 RecyclerView 來製作我們的字母列表清單:

// activity_main.xml 透過 viewBinding build 完之後的類別名稱
private lateinit var binding: ActivityMainBinding
private lateinit var recyclerView: RecyclerView

在onCreate 裡面建立需要的物件實體以及設定 UI:

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

recyclerView = binding.recyclerView
recyclerView.layoutManager = LinearLayoutManager(this)

上面的RecycleView 就是我們在 Android Basics in Kotlin(第6篇) ,而當我們使用Android 清單元件時,就會使用到我們的Adapter 適應器,一個用來將我們的資料在RecycleView做一個相對應的呈現的工具
還記得我們在第6篇說到使用Adapter 有幾個步驟一定要完成:

● 在res — > layout 新增 RecyclerView 單一 Item 的 Layout,取名為 item_view.xml,這次我們裡面只需要一個 Button:

<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:padding="8dp" />

● 新增一個類別 LetterAdapter,並且繼承RecyclerView.Adapter<LetterAdapter.LetterViewHolder>(),透過LetterViewHolder取得 UI 內的元件
因為我們的RecyclerView 是固定顯示 A-Z字母,因此我們使用Kotlin List 的語法取得 A-Z 的排序清單:

class LetterAdapter : RecyclerView.Adapter<LetterAdapter.LetterViewHolder>() {
private val list = ('A').rangeTo('Z').toList()

class LetterViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val button = view.findViewById<Button>(R.id.button_item)
}
}

繼承及實做Adapter 的抽象介面:
1. getItemCount(): Int 取的我們的資料清單長度

override fun getItemCount(): Int {
return list.size
}

2. onCreateViewHolder(parent: ViewGroup, viewType: Int): LetterViewHolder 用來綁定我們的LetterAdapter 每一個清單元件是要使用哪一個xml 的 layout

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LetterViewHolder {
val layout = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_view, parent, false)
return LetterViewHolder(layout)
}

3. onBindViewHolder(holder: LetterViewHolder, position: Int) 當使用者點選的清單選項時,APP 呼叫此事件函式,我們可以在這邊完成按下按鈕後要做的事情 — >跳轉到下一個頁面:

override fun onBindViewHolder(holder: LetterViewHolder, position: Int) {
val item = list.get(position)
holder.button.text = item.toString()

holder.button.setOnClickListener {
/// TODO 顯性跳


}
}

4. 在MainActivity 的 onCreate 幫 recyclerView 設定 Adapter :

recyclerView.adapter = LetterAdapter()

[顯性跳轉]

在MainActivity 的 LetterAdapter 的 onBindViewHolder 內幫 button 設定按下之後的動作,實作顯性跳轉:

val context = holder.view.context
val intent = Intent(context, WordActivity::class.java)
intent.putExtra("letter", holder.button.text.toString())
context.startActivity(intent)

[WordActivity]

來到了第二個頁面Word,當使用者在第一個頁面按下字母後,第二個頁式顯示的是我們事先準備好的此字母的單字表,因此這邊我們新增一個WordActivity 以及 WordAdapter 的類別來做頁面設計

先看 WordActivity:

class WordActivity : AppCompatActivity() {
companion object {
const val SEARCH_PREFIX = "https://www.google.com/search?q="
}

private lateinit var binding: ActivityWordBinding
private lateinit var recyclerView: RecyclerView


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityWordBinding.inflate(layoutInflater)
setContentView(binding.root)

val letterId = intent?.extras?.getString("letter").toString()
title = getString(R.string.word_prefix) + " " + letterId


recyclerView = binding.recyclerView
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = WordAdapter(letterId, this)

}
}

上方程式碼跟MainActivity 不同的地方有兩點,一是我們設計了一個companion object 程式區塊,二是我們透過顯性跳轉接收了intent 的一個參數 “letter”

[companion object]

companion object 此區塊可以視為 Kotlin 中在類別內的靜態成員或方法,因為在Kotlin 中並沒有所謂的 static 關鍵字,因此若我們有靜態成員或方法的需求,就得建立 companion object 並提供名稱,可以把他想成類似 c/c++ 的struct ,但 companion object 本身就跟著類別一起建立了,不須特別宣告變數來使用

[WordAdapter]

現在要來完成我們第二個 Adapter ,因為一樣都是按鍵的清單,我們一樣可以使用item_view.xml來當作清單裡面項目的UI

● 新增一個類別 WordAdapter,並且繼承RecyclerView.Adapter<WordAdapter.WordViewHolder>(),透過WordViewHolder取得 UI 內的元件

因為我們的WordActivity 的 RecyclerView 是顯示由第一個頁面傳來的字母的單字表,也就是說我們清單內容是會變動的,而這個變動的會在我們進入WordActivity 時就知道,因此我們在創建 WordAdapter 物件實體時可以帶入一個 letterId 的參數

且因為我們需要取得res 中 arrays.xml 的資源,因此我們也需要帶入 context 作為參數:

class WordAdapter (private val letterId: String, context: Context) : RecyclerView.Adapter<WordAdapter.WordViewHolder>() {

private val filteredWords: List<String>

init {
val words = context.resources.getStringArray(R.array.words).toList()

filteredWords = words
.filter { it.startsWith(letterId, ignoreCase = true) }
.shuffled()
.sorted()
}


class WordViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val button = view.findViewById<Button>(R.id.button_item)
}
}

而在Kotlin 中 init{…} 程式區塊就相當於 Java 中建構子會進入的區塊,因此我們在init 區塊對 letterId 做一些處理,取得 arrays.xml 中的陣列並對 letterId 做過濾的字母指定給 filteredWords

繼承及實做Adapter 的抽象介面:
1. getItemCount(): Int 取的我們的資料清單長度

override fun getItemCount(): Int {
return filteredWords.size
}

2. onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder用來綁定我們的WordAdapter 每一個清單元件是要使用哪一個xml 的 layout

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WordViewHolder {
val layout = LayoutInflater
.from(parent.context)
.inflate(R.layout.item_view, parent, false)
return WordViewHolder(layout)
}

3. onBindViewHolder(holder: WordViewHolder, position: Int) 當使用者點選的清單選項時,APP 呼叫此事件函式,我們可以在這邊完成按下按鈕後要做的事情 →跳轉到下一個頁面:

override fun onBindViewHolder(holder: WordViewHolder, position: Int) {

val item = filteredWords[position]
val context = holder.view.context
holder.button.text = item

holder.button.setOnClickListener {
/// TODO 隱性

}
}

4. 在WordActivity 的 onCreate 幫 recyclerView 設定 Adapter :

recyclerView.adapter = WordAdapter(letterId, this)

[隱性跳轉]

在WordActivity 的 WordAdapter 的 onBindViewHolder 內幫 button 設定按下之後的動作,實作隱性跳轉:

val queryUrl: Uri = Uri.parse("${WordActivity.SEARCH_PREFIX}${item}")
val intent = Intent(Intent.ACTION_VIEW, queryUrl)
context.startActivity(intent)

這樣就完成我們的APP 了!!!!

[QA]

Explicit Intent 適合使用在什麼場景中?
Implicit Intent 適合使用在什麼場景中?
● 舉出五種 Implicit Intent的 action
● intent
?.extras?.getString(“letter”).toString()此段程式碼是做什麼事情?
companion object 程式區塊相當於 JAVA 的什麼關鍵字?
Explicit Intent 的寫法?
Implicit Intent 的寫法?
● 複習一下 adapter 製作的五大步驟 ?

喜歡我的文章的人也記得幫我按個拍手、分享,覺得很不錯的可以幫我拍個50下!
也要快點追蹤我的 FB粉絲專頁 — 飛比尋常的程式設計世界 ,不會太頻繁出現在你的塗鴉牆騷擾你,好文章生產需要一點時間,有錯誤或想討論的都歡迎留言給我唷!那就下次見拉!

--

--

Phoebe Huang
Phoebe Huang

Written by Phoebe Huang

A software engineer from Taiwan, use free time to learn more about computer science.

No responses yet