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 的原理就是根據 id
在 build
文件夾下生成對應的 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的肯定一看就明白
-
如果parent為null,attachToParent將失去作用,設置任何值都沒有意義。
-
如果parent不為null,attachToParent設為true,則會給加載的布局文件的指定一個父布局,即parent。
-
如果parent不為null,attachToParent設為false,則會將布局文件最外層的所有layout屬性進行設置,當該view被添加到父view當中時,這些layout屬性會自動生效。
-
在不設置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基本都已經學完了,其他使用上的一些技巧就交給大家去探索了!