DataBinding 单向绑定
前言
在学习DataBinding后,我们使用它绑定控件后,每次改变数值,都要向DataBinding传值进行更新后才能刷新UI,这时我们就要使用单向绑定实现自动刷新UI。
-
使用单向绑定刷新UI的方式有三种
- BaseObservable
- ObservableField
- ObservableCollection
BaseObservable
BaseObservable提供了两个刷新UI的方法,分别是
- notifyPropertyChanged() 和 notifyChange() 。
notifyPropertyChanged(); 只会刷新属于它的UI,对应BR的数据
notifyChange(); 会刷新所有UI。
- 自定义类继承BaseObservable
- 在Getter上添加注解@Bindable,以便在BR中的调用
- 在Setter里面调用notifyPropertyChanged
public class Good extends BaseObservable {
public String name;
private String detail;
private float price;
public Good(String name,String detail,float price) {
this.detail = detail;
this.name = name;
this.price = price;
}
public float getPrice() {
return price;
}
@Bindable
public String getDetail() {
return detail;
}
@Bindable
public String getName() {
return name;
}
public void setDetail(String detail) {
this.detail = detail;
notifyChange();
}
public void setName(String name) {
this.name = name;
//此处可以简写为BR.name但是name会标红,不会出现编译错误
notifyPropertyChanged(com.example.mvvmthree.BR.name);
}
public void setPrice(float price) {
this.price = price;
}
}
在MainActivity中定义改变类,说明改变规则,为了更好的理解notifyPropertyChanged() 和 notifyChange() 的区别,在改变规则中我都加了对price的改变
对layout布局中在onClick中规定改变规则,格式为@{ }
layout具体实现如下:android:textAllCaps = false 只是为了使显示的文字小写
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.example.mvvmthree.Good" />
<import type="com.example.mvvmthree.MainActivity.GoodHandler" />
<variable
name="good"
type="Good" />
<variable
name="goodsHandler"
type="GoodHandler" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{good.name}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{good.detail}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{String.valueOf(good.price)}"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{()->goodsHandler.changeGoodName()}"
android:text="改变属性 name 和 price"
android:textAllCaps="false" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="@{()->goodsHandler.changeGoodDetail()}"
android:text="改变属性 details 和 price"
android:textAllCaps="false" />
</LinearLayout>
</layout>
和上一节DataBinding(一)所学相似,初始化对象赋值即可
当我们点击改变属性name 和price按钮时,只由name发生了改变,而点击另一个按钮时,两个属性都发生了改变,这就是notifyPropertyChanged() 和 notifyChange() 的区别的体现。
我们可以通过addOnPropertyChangedCallback方法查看哪些数据发生了改变
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
good = new Good("火龙果","好吃不贵",24);
mainBinding.setGood(good);
mainBinding.setGoodsHandler(new GoodHandler());
good.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
@Override
public void onPropertyChanged(Observable sender, int propertyId) {
if (propertyId == com.example.mvvmthree.BR.name) {
Log.e("MAIN", "BR.name");
} else if (propertyId == com.example.mvvmthree.BR.detail) {
Log.e("MAIN", "BR.details");
} else if (propertyId == com.example.mvvmthree.BR._all) {
Log.e("MAIN", "BR._all");
} else {
Log.e("MAIN", "未知");
}
}
});
}
ObservableField
ObservableField实际上是对BaseObservable的进一步封装,可以不需要再使用注解,与BaseObservable的用法相差不大,只是再自定义类的创建以及使用时有所不同,再自定义类中的变量类型改为如下形式:
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableField< 泛型>
注意:在ObservableField提供了get、set方法,可以拿到值和更新值来达到更新UI效果,一般在类中的变量多用public final修饰,无法书写Set方法,而且即便是写了Set方法也无法对值进行修改,只有通过ObservableField提供的set方法才能修改值。
public class ObservableGood {
private ObservableField<String> name;
private ObservableField<String> details;
private ObservableFloat prices;
public ObservableGood(ObservableField<String> name,ObservableField<String> details,ObservableFloat prices) {
this.details = details;
this.name = name;
this.prices = prices;
}
public ObservableField<String> getDetails() {
return details;
}
public ObservableFloat getPrices() {
return prices;
}
public ObservableField<String> getName() {
return name;
}
}
同样定义变化类
在此时定义的变化类内部的方法会使对应数据和布局发生改变。
layout的具体实现:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="com.example.databiningtext.MainActivity.Listener" />
<import type="com.example.databiningtext.ObservableGood"
alias="Good"/>
<variable
name="good"
type="Good" />
<variable
name="listener"
type="Listener" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20dp"
android:text="@{good.name}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20dp"
android:text="@{good.details}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20dp"
android:text="@{String.valueOf(good.prices)}"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="改变价格和细节"
android:layout_gravity="center"
android:onClick="@{()->listener.changePrice()}"
android:textAllCaps="false"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="改变名字和细节"
android:layout_gravity="center"
android:onClick="@{()->listener.changeName()}"
android:textAllCaps="false"/>
</LinearLayout>
</layout>
最后同样进行初始化和赋值即可
public class MainActivity extends AppCompatActivity {
private ObservableGood good;
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
good = new ObservableGood(new ObservableField<String>("jerry"),new ObservableField<String>("beta"),new ObservableFloat(90));
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.setGood(good);
binding.setListener(new Listener());
}
···
}
ObservableCollection
当我们的使用对象改为Map或者List形式时,DataBinding提供了 ObservableMap 和 ObservableList可供使用,我们可以直接导入包,直接使用。
(1)ObservableList的使用
首先应引入,并声明数据类型
在写布局时,应注意此时Text View中Text属性使用单引号。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="androidx.databinding.ObservableList"/>
<variable
name="list"
type="ObservableList<String>" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text= '@{list[0]}'/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{list[1]}'/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{list[2]}'/>
</LinearLayout>
</layout>
初始化List,并显示布局
更新UI的方法与上面介绍的方法相同:声明新的变化类,并引用
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private ObservableList<String> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
list = new ObservableArrayList<>();
list.add("study");
list.add("work");
list.add("up");
binding.setList(list);
binding.setUpdata(new Updata());
}
public class Updata{
public void change() {
for (int i = 0;i < list.size();i ++) {
list.set(i,"以改变" + i);
}
}
}
}
在layout中引用:注意ObservableList类型的书写,ObservableList < 元素类型 >
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="androidx.databinding.ObservableList"/>
<import type="com.example.databindingtext5.MainActivity.Updata"/>
<!-- 注意ObservableList类型的书写,ObservableList < 元素类型 >-->
<variable
name="list"
type="ObservableList<String>" />
<variable
name="updata"
type="Updata" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{list[0]}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{list[1]}"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@{list[2]}"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="@{()->updata.change()}"
android:text="改变"/>
</LinearLayout>
</layout>
效果图:
查看代码
( 2 )ObservableMap的使用
- 布局文件添加ObservableMap的包:androidx.databinding.ObservableMap,与上面的ObservableList的类型书写形式相似:
ObservableMap < key类型,对应数据类型>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import type="androidx.databinding.ObservableMap"/>
<variable
name="people"
type="ObservableMap<String,Object>" />
</data>
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{String.valueOf(people["ss"])}'
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{people["yy"]}'/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{people["xx"]}'/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{String.valueOf(people["aa"])}'/>
</LinearLayout>
</layout>
同样的初始化并赋值
自动更新UI与上面所学形式相同,此处不在举例。