本程式碼研究室是 Android Kotlin 基礎課程的一部分。使用程式碼研究室逐步完成程式碼課程後,您將能充分發揮本課程的潛能。所有課程程式碼研究室清單均列於 Android Kotlin 基礎程式碼研究室到達網頁。
簡介
在先前的課程研究室中,您已使用 findViewById()
函式取得資料檢視參照。如果您的應用程式含有複雜的檢視階層,findViewById()
的費用高昂,而且應用程式執行速度變慢,原因在於 Android 會跨越檢視區塊階層,直到找出所需的檢視為止。幸運的是,這是更好的方式。
為了在資料檢視中設定資料,您使用了字串資源,並設定該活動中的資料。如果資料檢視能解讀相關資料,更有效率。不過別擔心,您確實可以這麼做。
在這個程式碼研究室中,您將瞭解如何使用資料繫結排除 findViewById()
的需求。並瞭解如何使用資料繫結,直接在資料檢視中存取資料。
須知事項
您應該很熟悉:
- 何謂活動,以及如何在
onCreate()
中透過版面配置設定活動。 - 建立文字檢視,並設定顯示文字檢視的文字。
- 使用
findViewById()
取得檢視表的參照。 - 可建立及編輯資料檢視的基本 XML 版面配置。
課程內容
- 如何使用資料繫結程式庫,避免呼叫
findViewById()
的低效呼叫。 - 如何直接從 XML 存取應用程式資料。
執行步驟
- 修改應用程式以使用資料繫結 (而非
findViewById()
),並且直接從版面配置的 XML 檔案存取資料。
在這個程式碼研究室中,您先從 aboutMe 應用程式開始,並將應用程式變更為使用資料繫結。完成後,應用程式的外觀完全相同!
瞭解 MeMe 應用程式的用途:
- 使用者開啟應用程式時,應用程式會顯示名稱、輸入暱稱的欄位、[完成] 按鈕、星號圖片和可捲動的文字。
- 使用者可以輸入暱稱,然後輕觸 [完成] 按鈕。可編輯的欄位和按鈕會替換為顯示已輸入暱稱的文字檢視。
您可以使用先前在程式碼研究室中建立的程式碼,也可以從 GitHub 下載 AboutMeDatabinding-Starter 程式碼。
您在先前的程式碼研究室中編寫的程式碼會使用 findViewById()
函式來取得檢視參照。
建立或重新建立資料檢視後,每當您使用 findViewById()
搜尋資料檢視時,Android 系統都會在執行階段穿過檢視階層,藉此找出該資料檢視。您的應用程式只有少數的瀏覽次數,因此不需要擔心。不過,在製作應用程式的精選應用程式中,有數十種視圖,而且即使採用最優質的設計,仍會呈現巢狀檢視。
假設某個線性版面配置包含捲動畫面,其中包含文字檢視。對於大型或深度檢視階層,尋找資料檢視可能需要很長的時間,才能大幅降低應用程式的執行速度。快取變數可能很有幫助,但仍需在個別命名空間中為每個資料檢視初始化變數。觀看次數和活動量也相當多元
其中一種解決方法就是建立包含每個檢視參照的物件。這個物件稱為 Binding
物件,可供整個應用程式使用。這項技術稱為資料繫結。為應用程式建立繫結物件後,您可以透過繫結繫結檢視檢視資料和其他資料,而不需要查看檢視區塊階層或搜尋資料。
資料繫結具有以下優點:
- 相較於使用
findByView()
的程式碼,程式碼比較短、易於閱讀,且更容易維護。 - 資料和資料檢視會分開顯示。在本課程中,資料繫結的優勢越來越重要。
- Android 系統只會跨越檢視區塊階層一次來取得各個檢視,並且會在應用程式啟動時 (而不是在使用者與應用程式互動時於執行階段執行) 進行。
- 您可以使用類型安全機制來存取資料檢視。(「類型安全」表示編譯器會在編譯期間驗證類型;如果您嘗試將錯誤的類型指派給變數,就會擲回錯誤)。
在這項工作中,您會設定資料繫結,並使用資料繫結,將對 findViewById()
的呼叫替換為繫結物件的呼叫。
步驟 1:啟用資料繫結
如要使用資料繫結,您必須在 Gradle 檔案中啟用資料繫結,因為系統預設不會啟用此功能。這是因為資料繫結會增加編譯時間,並可能影響應用程式的啟動時間。
- 如果您沒有舊版程式碼研究室中的關於 MeMe 應用程式,請從 GitHub 取得 關於 MeDatabinding-Starter 程式碼。在 Android Studio 中開啟。
- 開啟
build.gradle (Module: app)
檔案。 - 在
android
區段內部的大括號前,新增dataBinding
區段並將enabled
設為true
。
dataBinding {
enabled = true
}
- 系統提示時,請同步專案。如果您沒有看到系統提示,請選取 [File > Sync Project with Gradle Files]。
- 您可以執行應用程式,但不會看到任何變更。
步驟 2:變更版面配置檔案以搭配資料繫結使用
若要使用資料繫結,您必須使用 <layout>
標記來包裝 XML 版面配置。如此一來,根類別不再是檢視群組,而是包含檢視群組和檢視的版面配置。然後,繫結物件就能瞭解版面配置及其中的視圖。
- 開啟
activity_main.xml
檔案。 - 切換至「文字」分頁。
- 將
<layout></layout>
新增為<LinearLayout>
周圍的最外層標記。
<layout>
<LinearLayout ... >
...
</LinearLayout>
</layout>
- 選擇 [程式碼 &符號;重新格式化程式碼] 可修正程式碼縮排。
版面配置的命名空間宣告必須置於最外層的標記中。
- 剪下
<LinearLayout>
中的命名空間宣告,然後貼到<layout>
標記中。您的<layout>
開頭標記應如下所示,而<LinearLayout>
標記則只能包含檢視屬性。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
- 建構並執行應用程式,以確認您已正確執行此操作。
步驟 3:在主要活動中建立繫結物件
為繫結活動新增參照物件的參照,以便您使用該物件存取檢視:
- 開啟
MainActivity.kt
檔案。 - 在
onCreate()
前,在頂層建立繫結物件的變數。這個變數可稱為binding
。binding
的類型 (ActivityMainBinding
類別) 是由編譯器專為這個主要活動建立的。這個名稱來自版面配置檔案的名稱,也就是activity_main + Binding
。
private lateinit var binding: ActivityMainBinding
- 如果出現 Android Studio 的提示,請匯入
ActivityMainBinding
。如果您沒有看到系統提示,請按一下ActivityMainBinding
,然後按下Alt+Enter
(在 Mac 上為Option+Enter
) 匯入這個遺失的課程。(如需其他鍵盤快速鍵,請參閱鍵盤快速鍵)。import
的陳述式應該與下方顯示的類似。
import com.example.android.aboutme.databinding.ActivityMainBinding
接著,將目前的 setContentView()
函式替換為執行下列步驟的指示:
- 建立繫結物件。
- 使用
DataBindingUtil
類別的setContentView()
函式,將activity_main
版面配置與MainActivity
建立關聯。這個setContentView()
函式也會處理資料檢視的部分資料繫結設定。
- 在
onCreate()
中,將setContentView()
呼叫替換為下列幾行程式碼。
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- 匯入
DataBindingUtil
。
import androidx.databinding.DataBindingUtil
步驟 4:使用繫結物件取代所有呼叫 tofindViewById()
您現在可以將 findViewById()
的所有呼叫替換為參照物件中的檢視。建立繫結物件時,編譯器會根據版面配置中的檢視 ID 產生繫結物件中的檢視名稱,並將其轉換成駱駝大小寫。因此,例如 done_button
是繫結物件中的 doneButton
,nickname_edit
會變成 nicknameEdit
,而 nickname_text
會變成 nicknameText
。
- 在
onCreate()
中,將使用findViewById()
的程式碼找到參照該繫結物件中按鈕的程式碼的done_button
。
請將這段程式碼:findViewById<Button>(R.id.
done_button
)
換成:binding.doneButton
您在onCreate()
中設定點擊事件監聽器的程式碼應該如下所示。
binding.doneButton.setOnClickListener {
addNickname(it)
}
- 針對
addNickname()
函式中所有對findViewById()
的呼叫執行相同步驟。
將findViewById<
View
>(R.id.
id_view
)
的所有例項替換為binding.
idView
。方法如下:
- 刪除
editText
和nicknameTextView
變數的定義和對findViewById()
的呼叫。這樣就會發生錯誤。 - 從
binding
物件取得nicknameText
、nicknameEdit
和doneButton
檢視,而不是刪除 (刪除) 變數,以修正錯誤。 - 以
binding.doneButton.visibility
取代view.visibility
。 使用binding.doneButton
而非傳遞的view
,讓程式碼更一致。
結果如下:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
- 功能沒有任何異動。如有需要,您現在可以刪除
view
參數並更新view
的所有用途,以便使用這個函式中的binding.doneButton
。
nicknameText
需要String
,而nicknameEdit.text
是Editable
。使用資料繫結時,必須將Editable
明確轉換為String
。
binding.nicknameText.text = binding.nicknameEdit.text.toString()
- 您可以刪除灰色的匯入項目。
- 使用
apply{}
將函式設定 Kotlin。
binding.apply {
nicknameText.text = nicknameEdit.text.toString()
nicknameEdit.visibility = View.GONE
doneButton.visibility = View.GONE
nicknameText.visibility = View.VISIBLE
}
- 建構並執行應用程式...應用程式外觀和運作方式與先前完全相同。
您可以利用資料繫結,讓資料類別可直接在資料檢視中使用。這項技術可簡化程式碼,對於處理更複雜的情況而言極為有用。
在此範例中,與其使用字串資源設定名稱和暱稱,您需為名稱和暱稱建立資料類別。您可以使用資料繫結,在資料檢視畫面中顯示資料類別。
步驟 1:建立 MyName 資料類別
- 在 Android Studio 中的
java
目錄中,開啟MyName.kt
檔案。如果沒有這個檔案,請建立新的 Kotlin 檔案並命名為MyName.kt
。 - 定義名稱和暱稱的資料類別。使用空白字串做為預設值。
data class MyName(var name: String = "", var nickname: String = "")
步驟 2:在版面配置中加入資料
在 activity_main.xml
檔案中,目前名稱字串是由 TextView
中的字串資源設定。您需要將參照中的名稱替換為資料類別中的資料參照。
- 在「Text」分頁中開啟
activity_main.xml
。 - 在版面配置頂端的
<layout>
和<LinearLayout>
標記之間插入<data></data>
標記。您可以在這裡將資料檢視與資料連結。
<data>
</data>
在資料標記中,您可以宣告具名變數,以存放類別的參照。
- 在
<data>
標記中加入<variable>
標記。 - 新增
name
參數,將變數命名為"myName"
。新增type
參數,並將類型設為MyName
資料類別的完整名稱 (套件名稱 + 變數名稱)。
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
現在,您可以參照 myName
變數,而不是使用名稱的字串名稱。
- 將
android:text="@string/name"
替換成下方的程式碼。
@={}
是用於取得大括號中參照的資料。
myName
會參照您先前定義的 myName
變數,指向 myName
資料類別,並從類別擷取 name
屬性。
android:text="@={myName.name}"
步驟 3:建立資料
現在,您的版面配置檔案資料已經參照了。接下來,您需要建立實際資料。
- 開啟
MainActivity.kt
檔案。 - 在
onCreate()
上方,建立一個私人變數 (依慣例為myName
)。將變數指派給MyName
資料類別的執行個體,並傳入名稱。
private val myName: MyName = MyName("Aleks Haecky")
- 在
onCreate()
中,將版面配置檔案中的myName
變數值設為您剛剛宣告的myName
變數值。您無法直接存取 XML 中的變數。您必須透過繫結物件存取該物件。
binding.myName = myName
- 這可能會顯示錯誤,因為您必須在進行變更後重新整理繫結物件。建構應用程式,錯誤應該就會消失。
步驟 4:在 TextView 中使用類別的資料類別
最後一步是使用「TextView
」中暱稱所屬的類別。
- 開啟
activity_main.xml
。 - 在
nickname_text
文字檢視中,新增text
屬性。參照資料類別中的nickname
,如下所示。
android:text="@={myName.nickname}"
- 在
ActivityMain
中,將nicknameText.text = nicknameEdit.text.toString()
換成程式碼,以便在myName
變數中設定暱稱。
myName?.nickname = nicknameEdit.text.toString()
暱稱設定完成後,您的程式碼就會以新資料重新整理使用者介面。如要這麼做,您必須撤銷所有繫結運算式,以正確的資料重新建立運算式。
- 在暱稱新增後加入
invalidateAll()
,讓系統使用更新的繫結物件中的值重新整理使用者介面。
binding.apply {
myName?.nickname = nicknameEdit.text.toString()
invalidateAll()
...
}
- 建構並執行應用程式,其運作方式與之前相同。
Android Studio 專案:關於 MeDatabinding
使用資料繫結取代對 findViewById()
的呼叫的步驟:
- 在
build.gradle
檔案的 Android 部分中啟用資料繫結:dataBinding { enabled = true }
- 使用
<layout>
做為 XML 版面配置的根視圖。 - 定義繫結變數:
private lateinit var binding: ActivityMainBinding
- 在
MainActivity
中建立繫結物件,取代setContentView
:binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- 將呼叫的
findViewById()
替換為繫結物件中的檢視參照。例如:findViewById<Button>(R.id.done_button) ⇒ binding.doneBu
tton
(在這個例子中,資料檢視的名稱是利用 XML 中的 View'sid
產生駱駝案例)。
將檢視表繫結至資料的步驟:
- 為資料建立資料類別。
- 在
<layout>
標記中加入<data>
區塊。 - 以名稱以及資料類別的
<variable>
定義。
<data>
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
</data>
- 在
MainActivity
中,建立包含資料類別執行個體的變數。例如:private val myName: MyName = MyName("Aleks Haecky")
- 在繫結物件中,將變數設為剛建立的變數:
binding.myName = myName
- 在 XML 中,將檢視畫面的內容設為您在
<data>
區塊中定義的變數。使用點標記法存取資料類別中的資料。android:text="@={myName.name}"
Udacity 課程:
Android 開發人員說明文件:
- 資料繫結程式庫
- 產生的繫結類別
- 開始使用資料繫結
- 版面配置與繫結運算式
這個部分會列出在代碼研究室中,受老師主導的課程作業的可能學生作業。由老師自行決定要執行下列動作:
- 視需要指派家庭作業。
- 告知學生如何提交家庭作業。
- 批改家庭作業。
老師可視需要使用這些建議,並視情況指派其他合適的家庭作業。
如果您是自行操作本程式碼研究室,歡迎透過這些家庭作業來測試自己的知識。
回答這些問題
第 1 題
為什麼您要減少對 findViewById()
的明確及隱性呼叫?
- 每次呼叫
findViewById()
時,都會越過視圖階層。 findViewById()
會在主要或 UI 執行緒上執行。- 這些呼叫會讓使用者介面的速度變慢。
- 你的應用程式不太可能會當機。
第 2 題
您會如何描述資料繫結?
舉例來說,您可以對資料繫結說一句話:
- 資料繫結的一大概念,就是建立一個物件,以在編譯時建立並連接/連接/連接兩個不同的連續資訊,這樣您就不需要在執行階段尋找資料。
- 呈現這些繫結繫結的物件稱為繫結物件。
- 繫結物件是由編譯器建立。
第 3 題
以下何者「不是」資料繫結的好處?
- 程式碼比較短、易於閱讀及維護。
- 資料和資料檢視會分開顯示。
- Android 系統只會將檢視階層疊加一次,以取得各項檢視。
- 呼叫
findViewById()
會產生編譯器錯誤。 - 類型:用來存取資料檢視。
第 4 題
<layout>
代碼的功能是什麼?
- 然後將這些元素納入版面配置的根區塊中。
- 系統會為版面配置中的所有檢視建立繫結。
- 這會指定使用資料繫結的 XML 版面配置的頂層檢視。
- 您可以在
<layout>
中使用<data>
標記,將變數繫結至資料類別。
第 5 題
下列哪一項是 XML 版面配置中參照繫結資料的正確方式?
android:text="@={myDataClass.property}"
android:text="@={myDataClass}"
android:text="@={myDataClass.property.toString()}"
android:text="@={myDataClass.bound_data.property}"
開始下一門課程:
如要瞭解本課程中其他程式碼研究室的連結,請參閱 Android Kotlin 基礎程式碼程式碼到達網頁。