ViewBinding(視圖綁定)
通過ViewBinding,可以更輕松地編寫可與視圖交互的代碼。在模塊中啟用ViewBinding后,系統(tǒng)會(huì)為該模塊中的每個(gè)XML布局文件生成一個(gè)綁定類。綁定類的實(shí)例包含對(duì)在相應(yīng)布局中具有ID的所有視圖的直接引用。多數(shù)情況下ViewBinding會(huì)替換掉findViewById。開啟方式有如下兩種方式:
//方式一
viewBinding{
enabled=true
}
//方式二
buildFeatures {
viewBinding true
}
如果希望在生成綁定類的時(shí)候忽略某個(gè)布局文件,可以在布局文件的根目錄上添加tools:viewBindingIgnore="true"屬性。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:viewBindingIgnore="true">
</LinearLayout>
模塊開啟視圖綁定功能后,系統(tǒng)會(huì)為該模塊中包含的每個(gè)XML布局文件生成一個(gè)綁定類。每個(gè)綁定類均包含對(duì)根View以及布局中具有ID的所有視圖的引用。系統(tǒng)生成綁定類的命名方式是:通過將XML文件的名稱去下劃線后轉(zhuǎn)換成駝峰式大小寫并在末尾添加"Binding"的方式。例如 activity_main.xml 會(huì)生成一個(gè)ActivityMainBinding的視圖綁定類。如下為activity_main.xml文件的內(nèi)容
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/name_view"
android:layout_width="match_parent"
android:layout_height="46dp"
android:gravity="center"
android:text="@string/app_name" />
<TextView
android:layout_width="match_parent"
android:layout_height="46dp"
android:gravity="center"
android:text="ceshi" />
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="46dp"
android:text="確定" />
</LinearLayout>
此XML布局文件對(duì)應(yīng)的ActivityMainBinding類,包含了對(duì)根視圖的引用及具有ID 的View的引用,通過綁定類 具體實(shí)例的getRoot()方法來獲取根視圖的引用,視圖中具有ID的view則會(huì)在綁定類中生成相應(yīng)ID名稱對(duì)應(yīng)的字段,如此視圖中的"name_view"和"button"這兩個(gè)view會(huì)在綁定類中生成nameView 和button兩個(gè)字段(字段生成規(guī)則為去下劃線后改駝峰式大小寫命名)。具體生成的綁定類如下:
// Generated by view binder compiler. Do not edit!
package com.gexing.test.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.viewbinding.ViewBinding;
import com.gexing.test.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final LinearLayout rootView;
@NonNull
public final Button button;
@NonNull
public final TextView nameView;
private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull Button button,
@NonNull TextView nameView) {
this.rootView = rootView;
this.button = button;
this.nameView = nameView;
}
@Override
@NonNull
public LinearLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.button;
Button button = rootView.findViewById(id);
if (button == null) {
break missingId;
}
id = R.id.name_view;
TextView nameView = rootView.findViewById(id);
if (nameView == null) {
break missingId;
}
return new ActivityMainBinding((LinearLayout) rootView, button, nameView);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}
ViewBinding如何在Activity和Fragment中使用:
上述綁定類中 我們發(fā)現(xiàn)有多個(gè)靜態(tài)類方法:
1.ActivityMainBinding inflate(@NonNull LayoutInflater inflater)
2.public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent)
3.public static ActivityMainBinding bind(@NonNull View rootView)
這三個(gè)靜態(tài)方法的返回值都式綁定類類型的實(shí)例,通過這個(gè)實(shí)例我們便可以獲取到相應(yīng)的rootView 由此便可以在activity和fragment中使用。
在Activity中使用:直接在onCreate方法中替換原有的setContentView(R.layout.activity_main)的方式
package com.gexing.test
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.gexing.test.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
lateinit var mainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_main)
mainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mainBinding.root)
}
}
在Fragment中使用:在onCreateView方法中處理
package com.gexing.test
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.gexing.test.databinding.ActivityMainBinding
class TestFragment : Fragment() {
var mainBinding: ActivityMainBinding? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
mainBinding = mainBinding ?: ActivityMainBinding.inflate(inflater, container, false)
return mainBinding?.root
}
}
override fun onDestroyView() {
super.onDestroyView()
mainBinding = null
}
ViewBinding與普通findViewById的區(qū)別:
1.Null安全,由于ViewBinding會(huì)創(chuàng)建對(duì)視圖的直接引用,因此不存在因視圖ID無效而引發(fā)的Null指針異常的風(fēng)險(xiǎn)。此外如果視圖僅出現(xiàn)在布局的某些配置中,則綁定類中包含其引用的字段會(huì)使用@Nullable標(biāo)記。
2.類型安全每個(gè)綁定類中的字段均具有與它們XML文件中引用的View相匹配的類型,這意味著不存在類轉(zhuǎn)換異常的風(fēng)險(xiǎn)。
這些差異意味著布局和代碼之間的不兼容將會(huì)導(dǎo)致構(gòu)建在編譯時(shí)失敗,而非在運(yùn)行時(shí)出現(xiàn)異常。