Android Kotlin 基礎課程 02.4:資料繫結基本概念

本程式碼研究室是 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 檔案中啟用資料繫結,因為系統預設不會啟用此功能。這是因為資料繫結會增加編譯時間,並可能影響應用程式的啟動時間。

  1. 如果您沒有舊版程式碼研究室中的關於 MeMe 應用程式,請從 GitHub 取得 關於 MeDatabinding-Starter 程式碼。在 Android Studio 中開啟。
  2. 開啟 build.gradle (Module: app) 檔案。
  3. android 區段內部的大括號前,新增 dataBinding 區段並將 enabled 設為 true
dataBinding {
    enabled = true
}
  1. 系統提示時,請同步專案。如果您沒有看到系統提示,請選取 [File > Sync Project with Gradle Files]
  2. 您可以執行應用程式,但不會看到任何變更。

步驟 2:變更版面配置檔案以搭配資料繫結使用

若要使用資料繫結,您必須使用 <layout> 標記來包裝 XML 版面配置。如此一來,根類別不再是檢視群組,而是包含檢視群組和檢視的版面配置。然後,繫結物件就能瞭解版面配置及其中的視圖。

  1. 開啟 activity_main.xml 檔案。
  2. 切換至「文字」分頁。
  3. <layout></layout> 新增為 <LinearLayout> 周圍的最外層標記。
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. 選擇 [程式碼 &符號;重新格式化程式碼] 可修正程式碼縮排。

    版面配置的命名空間宣告必須置於最外層的標記中。
  1. 剪下 <LinearLayout> 中的命名空間宣告,然後貼到 <layout> 標記中。您的 <layout> 開頭標記應如下所示,而 <LinearLayout> 標記則只能包含檢視屬性。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. 建構並執行應用程式,以確認您已正確執行此操作。

步驟 3:在主要活動中建立繫結物件

為繫結活動新增參照物件的參照,以便您使用該物件存取檢視:

  1. 開啟 MainActivity.kt 檔案。
  2. onCreate() 前,在頂層建立繫結物件的變數。這個變數可稱為 binding

    binding 的類型 (ActivityMainBinding 類別) 是由編譯器專為這個主要活動建立的。這個名稱來自版面配置檔案的名稱,也就是 activity_main + Binding
private lateinit var binding: ActivityMainBinding
  1. 如果出現 Android Studio 的提示,請匯入 ActivityMainBinding。如果您沒有看到系統提示,請按一下 ActivityMainBinding,然後按下 Alt+Enter (在 Mac 上為 Option+Enter) 匯入這個遺失的課程。(如需其他鍵盤快速鍵,請參閱鍵盤快速鍵)。

    import 的陳述式應該與下方顯示的類似。
import com.example.android.aboutme.databinding.ActivityMainBinding

接著,將目前的 setContentView() 函式替換為執行下列步驟的指示:

  • 建立繫結物件。
  • 使用 DataBindingUtil 類別的 setContentView() 函式,將 activity_main 版面配置與 MainActivity 建立關聯。這個 setContentView() 函式也會處理資料檢視的部分資料繫結設定。
  1. onCreate() 中,將 setContentView() 呼叫替換為下列幾行程式碼。
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. 匯入 DataBindingUtil
import androidx.databinding.DataBindingUtil

步驟 4:使用繫結物件取代所有呼叫 tofindViewById()

您現在可以將 findViewById() 的所有呼叫替換為參照物件中的檢視。建立繫結物件時,編譯器會根據版面配置中的檢視 ID 產生繫結物件中的檢視名稱,並將其轉換成駱駝大小寫。因此,例如 done_button 是繫結物件中的 doneButtonnickname_edit 會變成 nicknameEdit,而 nickname_text 會變成 nicknameText

  1. onCreate() 中,將使用 findViewById() 的程式碼找到參照該繫結物件中按鈕的程式碼的 done_button

    請將這段程式碼:findViewById<Button>(R.id.done_button)
    換成:binding.doneButton

    您在 onCreate()中設定點擊事件監聽器的程式碼應該如下所示。
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. 針對 addNickname() 函式中所有對 findViewById() 的呼叫執行相同步驟。
    findViewById<View>(R.id.id_view) 的所有例項替換為 binding.idView。方法如下:
  • 刪除 editTextnicknameTextView 變數的定義和對 findViewById() 的呼叫。這樣就會發生錯誤。
  • binding 物件取得 nicknameTextnicknameEditdoneButton 檢視,而不是刪除 (刪除) 變數,以修正錯誤。
  • 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
  1. nicknameText 需要 String,而 nicknameEdit.textEditable。使用資料繫結時,必須將 Editable 明確轉換為 String
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. 您可以刪除灰色的匯入項目。
  2. 使用 apply{} 將函式設定 Kotlin。
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. 建構並執行應用程式...應用程式外觀和運作方式與先前完全相同。

您可以利用資料繫結,讓資料類別可直接在資料檢視中使用。這項技術可簡化程式碼,對於處理更複雜的情況而言極為有用。

在此範例中,與其使用字串資源設定名稱和暱稱,您需為名稱和暱稱建立資料類別。您可以使用資料繫結,在資料檢視畫面中顯示資料類別。

步驟 1:建立 MyName 資料類別

  1. 在 Android Studio 中的 java 目錄中,開啟 MyName.kt 檔案。如果沒有這個檔案,請建立新的 Kotlin 檔案並命名為 MyName.kt
  2. 定義名稱和暱稱的資料類別。使用空白字串做為預設值。
data class MyName(var name: String = "", var nickname: String = "")

步驟 2:在版面配置中加入資料

activity_main.xml 檔案中,目前名稱字串是由 TextView 中的字串資源設定。您需要將參照中的名稱替換為資料類別中的資料參照。

  1. 在「Text」分頁中開啟 activity_main.xml
  2. 在版面配置頂端的 <layout><LinearLayout> 標記之間插入 <data></data> 標記。您可以在這裡將資料檢視與資料連結。
<data>
  
</data>

在資料標記中,您可以宣告具名變數,以存放類別的參照。

  1. <data> 標記中加入 <variable> 標記。
  2. 新增 name 參數,將變數命名為 "myName"。新增 type 參數,並將類型設為 MyName 資料類別的完整名稱 (套件名稱 + 變數名稱)。
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

現在,您可以參照 myName 變數,而不是使用名稱的字串名稱。

  1. android:text="@string/name" 替換成下方的程式碼。

@={} 是用於取得大括號中參照的資料。

myName 會參照您先前定義的 myName 變數,指向 myName 資料類別,並從類別擷取 name 屬性。

android:text="@={myName.name}"

步驟 3:建立資料

現在,您的版面配置檔案資料已經參照了。接下來,您需要建立實際資料。

  1. 開啟 MainActivity.kt 檔案。
  2. onCreate() 上方,建立一個私人變數 (依慣例為 myName)。將變數指派給 MyName 資料類別的執行個體,並傳入名稱。
private val myName: MyName = MyName("Aleks Haecky")
  1. onCreate() 中,將版面配置檔案中的 myName 變數值設為您剛剛宣告的 myName 變數值。您無法直接存取 XML 中的變數。您必須透過繫結物件存取該物件。
binding.myName = myName
  1. 這可能會顯示錯誤,因為您必須在進行變更後重新整理繫結物件。建構應用程式,錯誤應該就會消失。

步驟 4:在 TextView 中使用類別的資料類別

最後一步是使用「TextView」中暱稱所屬的類別。

  1. 開啟 activity_main.xml
  2. nickname_text 文字檢視中,新增 text 屬性。參照資料類別中的 nickname,如下所示。
android:text="@={myName.nickname}"
  1. ActivityMain 中,將
    nicknameText.text = nicknameEdit.text.toString()
    換成程式碼,以便在 myName 變數中設定暱稱。
myName?.nickname = nicknameEdit.text.toString()

暱稱設定完成後,您的程式碼就會以新資料重新整理使用者介面。如要這麼做,您必須撤銷所有繫結運算式,以正確的資料重新建立運算式。

  1. 在暱稱新增後加入 invalidateAll(),讓系統使用更新的繫結物件中的值重新整理使用者介面。
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. 建構並執行應用程式,其運作方式與之前相同。

Android Studio 專案:關於 MeDatabinding

使用資料繫結取代對 findViewById() 的呼叫的步驟:

  1. build.gradle 檔案的 Android 部分中啟用資料繫結:
    dataBinding { enabled = true }
  2. 使用 <layout> 做為 XML 版面配置的根視圖。
  3. 定義繫結變數:
    private lateinit var binding: ActivityMainBinding
  4. MainActivity 中建立繫結物件,取代 setContentView
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. 將呼叫的 findViewById() 替換為繫結物件中的檢視參照。例如:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (在這個例子中,資料檢視的名稱是利用 XML 中的 View's id 產生駱駝案例)。

將檢視表繫結至資料的步驟:

  1. 為資料建立資料類別。
  2. <layout> 標記中加入 <data> 區塊。
  3. 以名稱以及資料類別的 <variable> 定義。
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. MainActivity 中,建立包含資料類別執行個體的變數。例如:
    private val myName: MyName = MyName("Aleks Haecky")
  1. 在繫結物件中,將變數設為剛建立的變數:
    binding.myName = myName
  1. 在 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}"

開始下一門課程:3.1:建立片段

如要瞭解本課程中其他程式碼研究室的連結,請參閱 Android Kotlin 基礎程式碼程式碼到達網頁