學學Viewbinding

想植髮先禿頭發表於2020-08-03

Viewbinding

1.環境需求

環境上,需要Android Studio 3.6 Canary 11+

同樣的Gradle也需要升級(這年頭都4.0了,應該沒有還在用低版本的了吧...)

2.配置viewbinding

gradle 版本在 3.6 - 3.9 以上的:

app資料夾 下的 build.grale 裡面新增

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

如果你的 gradle 是 4.0+ ,那麼需要改一下寫法

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

否則可能會報一個warnning,雖然不改並大概率不影響使用,但作為一個優秀程式設計師湊合了事可不是什麼好習慣

DSL element 'android.viewBinding.enabled' is obsolete and has been replaced with 'android.buildFeatures.viewBinding'.

3.用法

原理

Viewbinding 的原理就是根據 idbuild 資料夾下生成對應的 java類,然後在 java類 裡面自動幫你 findViewById(對,沒錯,原理還是findViewById,並沒什麼新鮮的),佈局檔案裡面的子控制元件,就對應 java類 裡面的 field(成員變數)
然後你就可以呼叫 java 類來操作控制元件

對應生成的java類檔案,名字就是 刪除下劃線 再加 Binding

例如:activity_main.xml ==> ActivityMainBinding.java

某些佈局不用viewbinding

如果你不希望某個佈局(layout)被 Viewbinding 所“控制”(生成java類檔案),那就新增 tools:viewBindingIgnore="true"

<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>

Tip:可能這裡你有一個騷操作——我佈局採用 Viewbinding 但,某個控制元件不用,我自己 findViewById ,想法不錯,但是Google不同意,在子佈局或控制元件里加 tools:viewBindingIgnore="true" 是無效的

實戰

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding mMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mMainBinding.getRoot());
    }
}

這個程式碼用法非常簡單,就是宣告一個Viewbinding幫我們生成的ActivityMainBinding,然後用inflate載入他,getRoot就是獲得他的根佈局,跟R.layout.activity_main是一個東西。

inflate我多說兩句

inflate 跟Layoutinflate的inflate不是一個方法,但用法類似。
作用就是載入佈局(初始化ViewBinding)

原始碼是

  @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);
  }

一共兩個過載,還是很好理解的,用過LayoutInflate的肯定一看就明白

  1. 如果parent為null,attachToParent將失去作用,設定任何值都沒有意義。

  2. 如果parent不為null,attachToParent設為true,則會給載入的佈局檔案的指定一個父佈局,即parent。

  3. 如果parent不為null,attachToParent設為false,則會將佈局檔案最外層的所有layout屬性進行設定,當該view被新增到父view當中時,這些layout屬性會自動生效。

  4. 在不設定attachToParent引數的情況下,如果parent不為null,attachToParent引數預設為true。

載入控制元件(套娃模式)

我們先在 activity_main.xml 新增一個textView

<?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/text"**
**        android:layout_width="wrap_content"**
**        android:layout_height="wrap_content"**
**        android:background="#FF8686"**
**        android:text="111"**
**        android:textSize="40sp" />**

</LinearLayout>

再在 MainActivity 裡面編寫呼叫程式碼

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding mMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mMainBinding.getRoot());

        **mMainBinding.text.setText("dududududu");**
    }
}

呼叫很簡單,直接 mMainBinding.text.settext就可以了,這跟我們之前說的將控制元件 生成為java類 的成員變數也十分符合。

可我為什麼稱它為套娃模式呢???我們再嘗試新增一個 巢狀一個layout試試

新建一個佈局叫:layout_include.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/include_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="50sp" />

</FrameLayout>

再修改 activity_main.xml 的內容,將layout_include載入進去

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    ...>

    <TextView
     ... />

    **<include **
        **android:id="@+id/include"**
        **layout="@layout/layout_include" />**
</LinearLayout>

那麼我們怎麼呼叫 layout_include.xml 裡面的 textview 呢???

我們修改MainActivity裡面的程式碼

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding mMainBinding;
    private LayoutIncludeBinding mIncludeBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mMainBinding.getRoot());

        mMainBinding.text.setText("dududududu");

        mMainBinding.include.includeText.setText("qqqqqqqqqqq");

    }
}

這就是套娃模式(當然名字是我起的)

mMainBinding.include.includeText.setText("qqqqqqqqqqq");

在佈局巢狀多的時候,層層呼叫。
這也很好理解, mMainBinding.include 這返回的是layout_include
mMainBinding.include.includeText.setText("qqqqqqqqqqq");
這行就是 layout_include 呼叫自己的includeText。

別以為到這你就全會了,萬一有一個佈局用了 merge 怎麼辦???

用到merge

merge是一個解決父佈局多餘巢狀的一個標籤,他不會最終載入到佈局裡,更多的是作為一個標誌存在

**比如我們修改 layout_include.xml **

<?xml version="1.0" encoding="utf-8"?>
<merge
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/include_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="50sp" />

</merge>

這時候你再執行肯定就報錯了,那是因為,merge並不會載入到佈局裡,而我們又給 include 新增了id,所以Viewbinding在生成 java類 的時候,隨著id找過來,發現是merge,無從下手,就奔潰了。

怎麼解決這個問題呢??

將include的id去掉不就行了, ^O^

你這會兒肯定又不樂意了,去掉怎麼呼叫 include_text 呢???

細心的小可愛肯定發現了上面的inflate的原始碼,第一個過載呼叫第二個過載,第二個返回 bind(root) 。

bind 就是我們需要用到的方法了。

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding mMainBinding;
    private LayoutIncludeBinding mIncludeBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(mMainBinding.getRoot());
        ...

        mIncludeBinding = LayoutIncludeBinding.bind(mMainBinding.getRoot());
        mIncludeBinding.includeText.setText("lalala");
    }
}

我們先宣告瞭一個 LayoutIncludeBinding ,然後呼叫 LayoutIncludeBinding.bind(mMainBinding.getRoot()) 將其初始化,也就是將他繫結到 mMainBinding.getRoot() 上,也就是他的父佈局。
這時候我們再呼叫 mIncludeBinding.includeText.setText("lalala"); 就沒問題了

到此,關於ViewBinding基本都已經學完了,其他使用上的一些技巧就交給大家去探索了!

相關文章