一、什么是ViewBinding
ViewBinding是Android Studio 3.6推出的新特性,旨在替代findViewById(內部實現還是使用findViewById)。通過ViewBinding,可以更輕松地編寫可與視圖交互的代碼。在模塊中啟用ViewBinding之后,系統會為該模塊中的每個 XML 布局文件生成一個綁定類。綁定類的實例包含對在相應布局中具有 ID 的所有視圖的直接引用。
二、ViewBinding的優勢
與使用findViewById相比,ViewBinding有明顯的優勢:
1.類型安全:ViewBinding 生成的屬性類型和布局中的View類型是一致的,不需要進行類型轉換,相對于findViewById有類型安全性。
//findViewById需要類型轉換
TextView textView=(TextView) findViewById(R.id.text_view);
//ViewBinding不需要類型轉換
binding.textView.setText("Hello");
2.減少空指針異常:ViewBinding可以直接訪問綁定類中的視圖,因此不存在因 view ID 找不到而引發空指針異常的風險。
3.代碼更簡潔:使用ViewBinding只需要獲取一次實例,就可以實現對所有控件的調用,相對于findViewById不用多次獲取實例,代碼更簡潔。
三、ViewBinding的使用
1.使用前提
1.1添加依賴
在 app目錄下的的 build.gradle 文件中,添加如下代碼:
android {...buildFeatures {viewBinding true}
}
如果你的 build.gradle 是 build.gradle.kts 這種文件,則這樣添加代碼:
android {...buildFeatures {viewBinding = true}
}
添加后點擊Sync Now進行同步工程,完成配置?
1.2生成綁定類
完成第一步后點擊編譯后自動生成綁定類
位置如圖所示:
?綁定類的命名規則:
將xml文件名轉化為駝峰命名法,即去掉下劃線并將每個單詞首字母大寫,例如:
布局文件名:activity_main.xml
生成綁定類名:ActivityMainBinding
?默認情況下,AS會對工程中的所有xml文件生成綁定類。如果不想為某個布局文件生成,則可以將 tools:viewBindingIgnore=“true” 屬性添加到該布局文件的根視圖中,例如:
<LinearLayout...tools:viewBindingIgnore="true" >...
</LinearLayout>
2.使用ViewBinding?
ViewBinding可以用在各種需要布局與代碼交互的地方,如Activity、Fragment、ViewHolder等
2.1在Activity中使用ViewBinding
在布局文件中,我們設定了兩個控件TextView和Button,不需要有任何修改
<?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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><TextViewandroid:id="@+id/text_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="136dp"android:text="TextView"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="92dp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toBottomOf="@+id/text_view" /></androidx.constraintlayout.widget.ConstraintLayout>
- 如果使用findViewById,我們需要多次獲取實例:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView textView=(TextView) findViewById(R.id.text_view);Button button1=(Button) findViewById(R.id.button1);}}
- 如果使用ViewBinding:
public class MainActivity extends AppCompatActivity {protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding binding=ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());binding.textView.setText("Hello");binding.button1.setOnClickListener(v -> {Toast.makeText(MainActivity.this,"Button1",Toast.LENGTH_SHORT).show();});}}
可以發現只用獲取一次實例就可以操作所有控件
1.使用inflate方法:inflate是ViewBinding提供的靜態方法,用于將布局文件解析成對應的視圖對象。getLayoutInflater用于獲取LayoutInflater對象,該對象可以將XML文件轉換為視圖。
2.創建綁定對象:會創建一個ActivityMainBinding類綁定對象。這個對象包含了對activity_main.xml布局文件中所有視圖的引用,可以通過這個對象直接訪問和操作視圖。
2.2在Adapter中使用ViewBinding?
在使用RecyclerView中,我們在自定義適配器中也有許多運用到findViewById的地方,可以用ViewBinding替代。
- 如果使用findViewById
package com.example.viewbinding;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;import androidx.recyclerview.widget.RecyclerView;import java.util.List;public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<String> mFruitList;public FruitAdapter(List<String> fruits){mFruitList=fruits;}public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view,parent,false);ViewHolder holder=new ViewHolder(view);return holder;}public void onBindViewHolder(ViewHolder holder,int position){String fruitname=mFruitList.get(position);holder.textView.setText(fruitname);}public int getItemCount(){return mFruitList.size();}static class ViewHolder extends RecyclerView.ViewHolder {TextView textView;public ViewHolder(View view) {super(view);textView = (TextView) view.findViewById(R.id.fruit_name);}}
}
- 如果使用ViewBinding
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {private List<String> mFruitList;private LayoutInflater inflater;public FruitAdapter(Activity activity,List<String> fruits){mFruitList=fruits;inflater=LayoutInflater.from(activity);}public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){RecyclerViewBinding binding = RecyclerViewBinding.inflate(inflater, parent, false);ViewHolder holder=new ViewHolder(binding);return holder;}public void onBindViewHolder(ViewHolder holder,int position){String fruitname=mFruitList.get(position);holder.textView.setText(fruitname);}public int getItemCount(){return mFruitList.size();}static class ViewHolder extends RecyclerView.ViewHolder {TextView textView;private RecyclerViewBinding binding;public ViewHolder(RecyclerViewBinding binding) {super(binding.getRoot());textView = binding.fruitName;}} }
2.3在布局中嵌套include標簽
如果布局中存在嵌套,比如使用?include 標簽引用了另一個布局,這時就沒法直接用XXXbinding對象去引用嵌套布局里的id了。
解決方法:
- 為include標簽添加id;
- 使用 binding 訪問到 include 節點,再訪問到 include節點內部的其他控件。
再舉例說明一下,在activity_main.xml中我們用include標簽引用了一個布局?title_bar.xml,同時為它添加了id
<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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><includeandroid:id="@+id/include_title_bar"layout="@layout/title_bar" /><TextViewandroid:id="@+id/text_view"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="136dp"android:text="TextView"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><Buttonandroid:id="@+id/button1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="92dp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toBottomOf="@+id/text_view" /></androidx.constraintlayout.widget.ConstraintLayout>
?title_bar.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/text_hello"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="hello"/></LinearLayout>
在Activity中訪問:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);ActivityMainBinding binding=ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());binding.textView.setText("Hello");binding.includeTitleBar.textHello.setText("Hello");binding.button1.setOnClickListener(v -> {Toast.makeText(MainActivity.this,"Button1",Toast.LENGTH_SHORT).show();});}
}
這樣我們就實現了使用ViewBinding完成布局嵌套。