3.DataBinding
什么是DataBinding
1 2 3 4
| DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的 耦合性,使代码逻辑更加清晰。 DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件 中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。
|
启用 DataBinding 的方法是在对应 Model 的 build.gradle 文件里加入以下代码,同步后就能引入对 DataBinding 的支持
1 2 3 4 5 6
| android { ... buildFeatures { dataBinding true } }
|
简单使用
启用 DataBinding 后,这里先来看下如何在布局文件中绑定指定的变量
打开布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击 “Convert to data binding layout”,就可以生成 DataBinding 需要的布局规则
![img](/../../../images/v2-6fb5e43bec8a98f03158d1bb519646ad_720w.webp)
这里先来声明一个 Modle
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class User { private String name; private String pwd; public User(String name, String pwd) { this.name = name; this.pwd = pwd; } public String getName() { return name; }
public void setName(String name) { this.name = name; } public String getPwd() { return pwd; }
public void setPwd(String pwd) { this.pwd = pwd; } }
|
和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 标签用于声明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就需要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="user" type= "com.tyl.mystudy2.User"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:textSize="50dp" android:id="@+id/tv1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{user.name}" /> <TextView android:textSize="50dp" android:id="@+id/tv2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{user.pwd}" /> </LinearLayout> </layout>
|
通过 @{viewModel.name}
使 TextView 引用到相关的变量,DataBinding 会将之映射到相应的 getter 方法 之后可以在 Activity 中通过 DataBindingUtil
设置布局文件,省略原先 Activity 的 setContentView()
方法,并为变量 userInfo 赋值
如果 User 类型要多处用到,也可以直接将之 import 进来,这样就不用每次都指明整个包名路径了,而 java.lang.*
包中的类会被自动导入,所以可以直接使用
1 2 3 4 5 6
| <data> <import type="com.tyl.mystudy2.User"/> <variable name="user" type= "User"/> </data>
|
由于 @{user.name}
在布局文件中并没有明确的值,所以在预览视图中什么都不会显示,不便于观察文本的大小和字体颜色等属性,此时可以为之设定默认值(文本内容或者是字体大小等属性都适用),默认值将只在预览视图中显示,且默认值不能包含引号
1
| android:text="@{user.name,default=defaultValue}"
|
在 Activity 中通过 DataBindingUtil
设置布局文件,省略原先 Activity 的 setContentView()
方法,并为变量 userInfo 赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class DataBindingActivity extends AppCompatActivity { private ActivityDatabindingBinding binding; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, R.layout.activity_databinding); User user=new User("jett","123"); binding.setUser(user); binding.setVariable(BR.name,"jett"); } }
|
此外,也可以通过 ActivityMain2Binding 直接获取到指定 ID 的控件
1
| binding.tv1.setText("tyl");
|
Databinding 同样是支持在 Fragment 和 RecyclerView 中使用 。例如,可以看 Databinding 在 Fragment 中的使用
1 2 3 4 5 6
| @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false); fragmentBlankBinding.setHint("Hello"); return fragmentBlankBinding.getRoot(); }
|
以上实现数据绑定的方式,每当绑定的变量发生变化的时候,都需要重新向 ViewDataBinding 传递新的变量值才能刷新 UI 。接下来看如何实现自动刷新 UI
驱动 UI 刷新
实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservable
、ObservableField
、ObservableCollection
BaseObservable
一个纯净的 ViewModel 类被更新后,并不会让 UI 自动更新。而数据绑定后,我们自然会希望数据变更后 UI 会即时刷新,Observable 就是为此而生的概念
BaseObservable 提供了 notifyChange() 和 notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BR 的 flag,该 BR 的生成通过注释 @Bindable 生成,可以通过 BR notify 特定属性关联的视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class User extends BaseObservable { @Bindable public String name; private String pwd; public User(String name, String pwd) { this.name = name; this.pwd = pwd; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } @Bindable public String getPwd() { return pwd; }
public void setPwd(String pwd) { this.pwd = pwd; notifyChange(); } }
|
在 setName() 方法中更新的只是本字段,而 setPwd() 方法中更新的是所有字段