從 Android Studio 3.6 開始,視圖綁定可以經過生成綁定對象來替代 findViewById,從而能夠幫您簡化代碼、移除 bug,而且從 findViewById 的模版代碼中解脫出來。html
本文梗概java
(activity_awesome.xml
→ ActivityAwesomeBinding.java
)android
更多詳細內容能夠從視頻:使用視圖綁定替代 findViewById 中獲取。git
開啓視圖綁定無須引入額外依賴,從 Android Studio 3.6 開始,視圖綁定將會內建於 Android Gradle 插件中。須要打開視圖綁定的話,只須要在 build.gradle 文件中配置 viewBinding 選項:github
// 須要 Android Gradle Plugin 3.6.0 android { viewBinding { enabled = true } }
在 Android Studio 4.0 中,viewBinding 變成屬性被整合到了 buildFeatures 選項中,因此配置要改爲:編程
// Android Studio 4.0 android { buildFeatures { viewBinding = true } }
配置完成後,視圖綁定就會爲全部佈局文件自動生成對應的綁定類。無須修改原有佈局的 XML 文件,視圖綁定將根據您現有的佈局自動完成全部工做。安全
視圖綁定將會根據現有的 XML 文件,爲 Module 內全部的佈局文件生成綁定對象。
您能夠在任何須要填充佈局的地方使用綁定對象,好比 Fragment
、Activity
、甚至是 RecyclerView Adapter
(或者說是 ViewHolder
中)。編程語言
假如您有一個佈局文件名叫 activity_awesome.xml,其中包含了一個按鈕和兩個文本視圖。視圖綁定會爲這個佈局生成一個名叫 ActivityAwesomeBinding 的類,佈局文件中全部擁有 id 的視圖,都會在這個類中有一個對應的屬性:ide
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = ActivityAwesomeBinding.inflate(layoutInflater) binding.title.text = "Hello" binding.subtext.text = "Concise, safe code" binding.button.setOnClickListener { /* ... */ } setContentView(binding.root) }
△ 在 Activity 中使用視圖綁定佈局
使用視圖綁定時,無須再調用 findViewById
方法,只要直接調用綁定對象中的對應屬性便可。
佈局的根視圖(不管有沒有 id)都會自動生成一個名爲 root
的屬性。在 Activity
的 onCreate
方法中,要將 root
傳入 setContentView
方法,從而讓 Activity
可使用綁定對象中的佈局。
一個常見的錯誤用法是: 在開啓了視圖綁定的同時,依然在 setContentView(...) 中傳入佈局的 id 而不是綁定對象。這將形成同一佈局被填充兩次,同時監聽器也會被添加到錯誤的佈局對象中。解決方案: 在 Activity 中使用視圖綁定時,必定要將綁定對象的 root 屬性傳入 setContentView() 方法中。
findViewById
是許多用戶可見 bug 的來源: 咱們很容易傳入一個佈局中根本不存在的 id,從而致使空指針異常而崩潰;因爲此方法類型不安全,也很容易令人寫出像 findViewById<TextView>(R.id.image)
這樣的,致使類型轉換錯誤的代碼。爲了解決這些問題,視圖綁定把 findViewById 替換成了更加簡潔和安全的實現。
視圖綁定有下面兩個特性:
TextView
,視圖綁定就會暴露一個 TextView
類型的屬性給您。@Nullable
註解的屬性。因此即便在多種配置下定義的佈局文件,視圖綁定依然可以保證空安全。因爲生成的綁定類是普通的 Java 類,而且其中添加了 Kotlin 友好的註解,因此 Java 和 Kotlin 均可以使用視圖綁定。
如前文所說,視圖綁定會生成一個包含替代 findViewById 功能的 Java 類。它會爲 Module 下的每個佈局的 XML 文件生成一個對應的綁定對象,並根據源文件爲其命名,好比 activity_awesome.xml 對應的綁定對象爲 ActivityAwesomeBinding.java。
生成代碼的邏輯被優化爲,當您在 Android Studio 中編輯 XML 佈局文件時,只會更新所修改佈局對應的綁定對象。同時這些工做會在內存中運行,從而使這個過程能夠迅速完成。這意味着您的修改會當即反映在綁定對象中,而無須等待或者從新構建工程。
Android Studio 被優化爲能夠在您編輯過 XML 佈局文件後當即更新綁定對象。
讓咱們經過一個 示例 XML 佈局 所生成的代碼,來了解一下視圖綁定究竟生成了什麼。
public final class ActivityAwesomeBinding implements ViewBinding { @NonNull private final ConstraintLayout rootView; @NonNull public final Button button; @NonNull public final TextView subtext; @NonNull public final TextView title;
△ 視圖綁定生成的屬性。能夠看到它們都是類型安全以及空安全的
視圖綁定會根據每一個擁有 id 的視圖生成類型正確的屬性。他也會爲根佈局生成 rootView 屬性並經過 getRoot 暴露給您。視圖綁定沒有添加任何額外的邏輯,他只是把視圖屬性暴露給您,從而幫您在不使用 findViewById 的狀況下也能調用它們。這樣一來便保證了生成文件簡潔性(固然也避免了拖慢構建速度)。
若是您正在使用 Kotlin,視圖綁定的生成類也已經對互操做進行了優化。經過 @Nullable
和 @NonNull
註解的使用,Kolin 能夠正確的將屬性暴露爲空安全類型。若是想要了解更多關於兩種語言的互操做問題,請查閱文檔: 在 Kotlin 中調用 Java。
private ActivityAwesomeBinding(@NonNull ConstraintLayout rootView, @NonNull Button button, @NonNull TextView subtext, @NonNull TextView title) { … } @NonNull public static ActivityAwesomeBinding inflate(@NonNull LayoutInflater inflater) { /* 編輯過: 移除了重載方法 inflate(inflater, parent, attachToParent) 的調用*/ View root = inflater.inflate(R.layout.activity_awesome, null, false); return bind(root); }
視圖綁定會生成 inflate 方法做爲生成一個綁定對象實例的主要方式。在 ActivityAwesomeBinding.java 中,視圖綁定生成了一個只有一個參數的 inflate 方法,該方法經過將 parent 設定爲空值來指定當前視圖不會綁定到父視圖中;視圖綁定也暴露了一個有三個參數的 inflate 方法,來讓您在須要的時候傳入 parent 和 attachToParent 參數。
真正神奇的地方是 bind 方法的調用。這裏會填充視圖並綁定全部的屬性,同時作一些錯誤檢測並生成清晰的錯誤提示。
@NonNull public static ActivityAwesomeBinding bind(@NonNull View rootView) { /* 編輯: 簡化代碼 – 真實狀況下生成的代碼是一個優化過的版本 */ Button button = rootView.findViewById(R.id.button); TextView subtext = rootView.findViewById(R.id.subtext); TextView title = rootView.findViewById(R.id.title); if (button != null && subtext != null && title != null) { return new ActivityAwesomeBinding((ConstraintLayout) rootView, button, subtext, title); } throw new NullPointerException("Missing required view […]"); }
△ 自動生成的 bind 方法的簡化版本
bind 是綁定對象中最複雜的一個方法,它經過調用 findViewById 來綁定每一個視圖。既然編譯器能夠經過 XML 佈局文件知道每一個屬性的類型和爲空的可能性,那他就能夠安全的調用 findViewById。
請注意,視圖綁定生成的真正的 bind 方法要來的更長,而且其中使用了一個標記 break 語句來優化字節碼,您能夠查看 Jake Wharton 撰寫的這篇文章 來了解更多優化有關的內容。在每一個綁定對象中,都會暴露三個靜態方法來建立綁定對象實例,下面是每一個方法使用場景的簡要說明:
前面已經講過,視圖綁定會爲 Module 下的每個佈局文件生成一個綁定對象,這個說法在佈局文件被另外一個佈局文件使用 include 引入時依然適用。
<!-- activity_awesome.xml --> <androidx.constraintlayout.widget.ConstraintLayout> <include android:id="@+id/includes" layout="@layout/included_buttons" </androidx.constraintlayout.widget.ConstraintLayout> <!-- included_buttons.xml --> <androidx.constraintlayout.widget.ConstraintLayout> <Button android:id="@+id/include_me" /> </androidx.constraintlayout.widget.ConstraintLayout>
△ 視圖綁定中使用 include 標籤的示例
注意: include 標籤下有一個 id。
在使用引入佈局的時候,視圖綁定會建立一個被引入佈局綁定對象的引用。注意 include>標籤有一個 id: android:id="@+id/includes"。這裏的邏輯跟使用普通視圖同樣, include 標籤也須要有一個 id 才能在綁定對象中生成對應的屬性。
include 標籤必須有一個 id,才能生成對應的屬性。
public final class ActivityAwesomeBinding implements ViewBinding { ... @NonNull public final IncludedButtonsBinding includes;
視圖綁定會在 ActivityAwesomeBinding 中生成一個 IncludedButtonsBinding 的引用。
視圖綁定只是 findViewById 的取代方案,若是您但願在 XML 中自動綁定視圖,可使用 數據綁定 庫。數據綁定和視圖綁定能夠生成一樣的組件,它們能夠同時工做。
在二者都被開啓時,使用 <layout> 標籤的佈局會由數據綁定來生成綁定對象;而其他的佈局則由視圖綁定生成綁定對象。
您能夠在同一 Module 中同時使用數據綁定和視圖綁定。
咱們之因此開發視圖綁定做爲數據綁定的補充,是由於許多開發者反映說,但願有一個輕量的解決方案,能在數據綁定以外替代 findViewById——視圖綁定提供的正是這一功能。
關於視圖綁定,一個最多見的問題是: "我是否應該用視圖綁定替代 Kotlin 合成方法或 ButterKnife ? " 兩者都是目前十分紅功的組件庫,有許多應用使用它們解決 findViewById 的問題。
對於大多數應用來講,咱們推薦嘗試使用視圖綁定來替代這兩個庫,由於視圖綁定能夠提供更加安全和準確的視圖映射方式。
上圖爲對比視圖綁定、ButterKnife 和 Kotlin 合成方法的功能。
雖然 ButterKnife 會在運行時校驗可空與不可空,可是編譯器並不會檢查您匹配的視圖是否在存在於您的佈局之中。爲了安全性與更簡潔代碼,咱們推薦嘗試使用視圖綁定。
點擊這裏瞭解更多有關視圖綁定的信息。