View Binding 是什麼 ?

View Binding 是一個機制,可以讓開發者更簡單以及更安全的參考佈局檔的 UI 元件。
ref : https://developer.android.com/topic/libraries/view-binding

當 Module 啟動 View Binding 之後,該 Module 內每個佈局檔都會產生一個相對應的 binding class,binding class 會包含其對應的佈局檔中具有 id 的屬性,使用者便可透過 binding class 直接操作佈局檔的 UI 元件,不必再寫 findViewById。

View Binding 用來做什麼 ?

在大部分的情況下,View Binding 用來取代 findViewById。

設定和相依性

注意
1.目前(2019-09-12)只支援 Android Studio 3.6 Canary 11+
2.如果已經使用 data binding,不必再啟動 View Binding

在 Module 的 build.gradle 啟動 View Binding

android {
    ...
    viewBinding {
        enabled = true
    }
}

如果不想為某個佈局檔自動產生 binding class,在該佈局檔中加入以下設定

<LinearLayout
        ...
        tools:viewBindingIgnore="true" >
    ...
</LinearLayout>

用法

在 Module 中啟動 View Binding 之後,只要執行 make project,便會為該 Module 中每個佈局檔建立 binding class。

binding class 檔案位於
app -> build -> generated -> data_binding_base_class_source_out -> debug -> out -> package name -> databinding
(這個其實不重要,因為不必手動修改它)

binding class 名稱會根據對應的佈局檔延伸,規則是佈局檔+Binding
假設有一個為 activity_view_binding_example.xml 佈局檔,內容如下。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ViewBindingExampleActivity">
    <TextView
        android:id="@+id/view_binding_example_hello_world"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/view_binding_example_click_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click me"
        app:layout_constraintLeft_toLeftOf="@+id/view_binding_example_hello_world"
        app:layout_constraintTop_toBottomOf="@+id/view_binding_example_hello_world" />
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="bottom button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

可以看到其中一個 TextView 和 Button 都有 id,而另一個 Button 沒有 id。
binding class 會自動持有具有 id 屬性的 UI 元件變數,忽略掉沒有 id 屬性的UI 元件。

對應的 binding class 名稱為 ActivityViewBindingExampleBinding。它會持有名為 viewBindingExampleClickMe 變數以及 viewBindingExampleHelloWorld 變數。這兩個變數對應佈局檔的

<TextView
 android:id="@+id/view_binding_example_hello_world"
...
/>
<Button
 android:id="@+id/view_binding_example_click_me"
...
/>

只要將 ViewBinding 初始化之後就可以透過這兩個變數來操作佈局檔的 UI 元件,以下為在 Activity 中使用的範例。

class ViewBindingExampleActivity : AppCompatActivity() {
    private lateinit var mViewBinding: ActivityViewBindingExampleBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mViewBinding = ActivityViewBindingExampleBinding.inflate(layoutInflater)
        setContentView(mViewBinding.root)
        initUI()
    }
    private fun initUI() {
        mViewBinding.viewBindingExampleClickMe.setOnClickListener { _ ->
            Toast.makeText(this, "change to NEW hello world!!!", Toast.LENGTH_SHORT).show()
            mViewBinding.viewBindingExampleHelloWorld.text = "new hello world"
        }
    }
}

mViewBinding 就是 binding class 的實體變數。

注意初始化 mViewBinding 的呼叫順序部分。初始化完 mViewBinding 之後呼叫setContentView 方法是傳入 mViewBinding 的 root 方法回傳值。
root 方法其實就是回傳對應佈局檔的根元素,本範例就是 ConstraintLayout。

        mViewBinding = ActivityViewBindingExampleBinding.inflate(layoutInflater)
        setContentView(mViewBinding.root)

而在 initUI 方法中就是透過 mViewBinding 操作 UI 元件,不須經過初始化也不用 findViewById。

    private fun initUI() {
        mViewBinding.viewBindingExampleClickMe.setOnClickListener { _ ->
            Toast.makeText(this, "change to NEW hello world!!!", Toast.LENGTH_SHORT).show()
            mViewBinding.viewBindingExampleHelloWorld.text = "new hello world"
        }
    }

優勢

View Binding 透過提供 binding class 的實體來操作 UI 元件,這種方式比 findViewById 多了一些好處(就是不用寫 findViewById!!)。

首先為型態安全,因為不必在意呼叫 findViewById 時轉型的型態是否正確。
第二為 Null 安全,因為不必在意呼叫 findViewById 時指定 UI 元件的 ID。

和 Data Binding 的不同

  • Data Binding 只處理使用<layout>標籤來綁定數據。
  • View Binding 無法處理在佈局檔定義的變數以及表達式。

總結

就目前而言 View Binding 是很簡易的新特性,用途有限,主要用於取代 findViewById,其 binding class 提供的方法也不多。