Android框架組件 ViewBinding詳解及使用

初識ViewBinding

顧名思義ViewBinding的意思就是如何將view代碼綁定在一塊兒。因此其主要解決如何安全優雅地從代碼中引用到XML layout文件中的view控件的問題。直到目前爲止,Android構建用戶界面的主流方式仍然是使用XML格式的layout文件。java

視圖訪問的方式有經常使用的findViewById,ButterKnife等多種方式,這些方式的各方面對好比下android

谷歌在Android Studio 3.6 Canary 11版本中正式推出視圖綁定View Binding安全

首先須要使用AS 3.6 Canary 11之上的版本,這裏我使用AS 3.6.1升級gradle plugin版本到3.6.1markdown

ViewBinding是一項功能,使您能夠更輕鬆地編寫與視圖交互的代碼。在模塊中啓用視圖綁定後,它將爲該模塊中存在的每一個XML佈局文件生成一個綁定類。綁定類的實例包含對在相應佈局中具備ID的全部視圖的直接引用。在大多數狀況下,視圖綁定替換findViewById()app

環境要求

  • Android Studio版本3.6及以上
  • Gradle 插件版本3.6.0及以上

使用步驟

  • 視圖綁定功能可按模塊啓用。要在某個模塊中啓用視圖綁定,請將 viewBinding 元素添加到其 build.gradle 文件中,以下例所示:
android {
    ...
    
    viewBinding{
        enabled = true
    }
}
複製代碼
  • 若是您但願在生成綁定類時忽略某個佈局文件,請將 tools:viewBindingIgnore="true" 屬性添加到相應佈局文件的根視圖中:
<LinearLayout
            ...
            tools:viewBindingIgnore="true" >
        ...
    </LinearLayout>
    
複製代碼

爲某個模塊啓用視圖綁定功能後,系統會爲該模塊中包含的每一個 XML 佈局文件各生成一個綁定類。每一個綁定類均包含對根視圖以及具備 ID 的全部視圖的引用。系統會經過如下方式生成綁定類的名稱:將 XML 文件的名稱轉換爲駝峯式大小寫,並在末尾添加「Binding」一詞。ide

  • 假設咱們有一個xml佈局文件,命名爲activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="150dp"/>

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        />
            
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>
複製代碼

生成的綁定類將名爲 ActivityMainBinding。此類具備兩個字段:一個是名爲 tvTextTextView,另外一個是名爲 btnButton。該佈局中的 ImageView 沒有 ID,所以綁定類中不存在對它的引用。佈局

每一個綁定類還包含一個 getRoot() 方法,用於爲相應佈局文件的根視圖提供直接引用。在此示例中,ActivityMainBinding 類中的 getRoot() 方法會返回 LinearLayout 根視圖。gradle

  • 要獲取生成的綁定類的實例,您能夠調用其靜態 inflate() 方法。一般狀況下,還能夠調用 setContentView(),從而將該綁定類的根視圖做爲參數進行傳遞,以使它成爲屏幕上的活動視圖。在此示例中,您能夠在 Activity 中調用 ActivityMainBinding.inflate()
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding mBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mBinding.getRoot());

        mBinding.tvText.setText("是否是這樣使用呢?");
        mBinding.btn.setText("我是一個按鈕");

    }
}
複製代碼
  • 效果

與使用 findViewById 相比,視圖綁定具備一些很顯著的優勢:

  • Null 安全:因爲視圖綁定會建立對視圖的直接引用,所以不存在因視圖 ID 無效而引起 Null 指針異常的風險。此外,若是視圖僅出如今佈局的某些配置中,則綁定類中包含其引用的字段會使用 @Nullable 標記。
  • 類型安全:每一個綁定類中的字段均具備與它們在 XML 文件中引用的視圖相匹配的類型。這意味着不存在發生類轉換異常的風險。

原理

  • 咱們先來看一下咱們使用ViewBinding時生成的ActivityMainBinding類

  • ActivityMainBinding類代碼以下:

原理就是Google在那個用來編譯的gradle插件中增長了新功能,當某個module開啓ViewBinding功能後,編譯的時候就去掃描此模塊下的layout文件,生成對應的binding類。那些你所熟悉的findViewById操做都是在這個自動生成的類裏面呢,以下所示ui

// Generated by view binder compiler. Do not edit!
package com.example.viewbinding.databinding;

public final class ActivityMainBinding implements ViewBinding {
  @NonNull
  private final LinearLayout rootView;

  @NonNull
  public final Button btn;

  @NonNull
  public final TextView tvText;

  private ActivityMainBinding(@NonNull LinearLayout rootView, @NonNull Button btn, @NonNull TextView tvText) {
    this.rootView = rootView;
    this.btn = btn;
    this.tvText = tvText;
  }

  @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.
    String missingId;
    missingId: {
      Button btn = rootView.findViewById(R.id.btn);
      if (btn == null) {
        missingId = "btn";
        break missingId;
      }
      TextView tvText = rootView.findViewById(R.id.tv_text);
      if (tvText == null) {
        missingId = "tvText";
        break missingId;
      }
      return new ActivityMainBinding((LinearLayout) rootView, btn, tvText);
    }
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }
}

複製代碼

其中核心代碼是bind(@NonNull View rootView)方法,除此以外還有兩個inflate()重載方法,通常狀況下咱們使用這兩個方法得到binding類的實例,這些方法都是public static的,經過bind(@NonNull View rootView)這個方法應該能夠實現延遲綁定,可是其使用場景應該不多。this

相關文章
相關標籤/搜索