DataBinding简介
dataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。
DataBinding的作用
dataBinding翻译后叫数据绑定,它的主要作用是与UI界面绑定,通过dataBinding获取页面数据与修改页面数据。同时dataBinding还具有观察性,数据模型或者UI界面数据改变之后,它可以同步数据。
为什么使用DataBinding
由于Android开发语言的限制,在数据加载问题上设计了MVC、MVP架构不是代码臃肿就是方法满天飞,导致代码维护难度很大,开发者也很难准确的使用这些架构,还有就是这些架构从根本上来讲,还是没有解决数据与页面的绑定关系。dataBinding的就是为了解决这样的问题而出现,只要创建好对应实体类或者方法,即可轻松将数据绑定到UI上,UI随着数据的改变自动更新,反之亦然,大大的减少了代码量。
DataBinding的使用主要有以下几点
配置gradle
这也是最基础的一步,在module中的build.geadle中配置
android {
...
buildFeatures {
dataBinding true
}
}
配置Layout
基础配置
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
使用layout标签和data标签,其中layout标签需要包裹data标签与UI布局,如上述XML代码
如果您希望在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore="true" 属性添加到相应布局文件的根视图中:
<LinearLayout
...
tools:viewBindingIgnore="true" >
...
</LinearLayout>
activity关联layout布局
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding dataBinding;
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
dataBinding.textView.setText("");
dataBinding.textView.getText();
}
}
DataBinding是通过DataBindingUtil下的setContentView(Activity activity,int layoutId)方法与layout布局关联,返回一个泛型(<T extends ViewDataBinding> T)内部规则Activity+Activity类名(去掉Activity)+Binding,例如MainActivity返回的是ActivityMainBinding。返回的泛型也可以通过layout中的data标签中的class属性(注意这里自定义class名称一定要与页面关联,个人不建议自定义)。
DataBinding与组件关联是通过组件id进行关联的例如dataBinding.textView,内部规则采用驼峰式命名风格转换layout中的组件id。
DataBinding与Fragment关联是通过inflate(LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)方法绑定的,与activity关联大同小异,有兴趣可以自己研究一下。
上述部分是DataBinding的基础使用部分,有这些我们就可以实现UI界面与Activity或者Fragment的绑定,下面主要说的是数据双向绑定。
数据绑定
data节点下标签使用
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import
alias="string"
type="java.lang.String" />
<variable
name="contents"
type="string" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
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="@{contents}"
android:textColor="@color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
layout布局中data节点是存放数据。data 中的 variable 标签为变量,类似于我们定义了一个变量,name 为变量名,type 为变量全限定类型名,包括包名。import标签是用来导入包名\类名,还可以通过alias属性设置别名。布局中通过 @{} 来引用这个变量的值,{} 中可以是任意 Java 表达式,但不推荐使用过多的代码。
绑定普通数据
@Override
protected void initView() {
dataBinding.setContents("演示DataBinding数据绑定");
handler.removeMessages(0);
handler.sendEmptyMessageDelayed(0,2000);
}
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
sign++;
dataBinding.setContents("演示DataBinding数据绑定: " + sign);
handler.removeMessages(0);
handler.sendEmptyMessageDelayed(0,2000);
Log.d(TAG,dataBinding.getContents());
}
};
DataBinding 可以绑定普通数据对象(非 Observable/LiveData),例如上述例子中绑定了一个 String 类型的数据。绑定普通数据我们只需要按照上述的代码设置即可。
绑定可观察数据
绑定可观察数据意味着当数据变化时 UI 会跟着一起变化,绑定可观察数据有三种方式:fields、 collections和 objects.
fields变量绑定(单个变量)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<import
alias="field"
type="androidx.databinding.ObservableField" />
<variable
name="input"
type="field<String>" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{input}"
android:textColor="@color/black" />
</LinearLayout>
</layout>
public class MainActivity extends BaseActivity<ActivityMainBinding> {
private static final String TAG = MainActivity.class.getName();
ObservableField<String> inputs = new ObservableField<>("演示ObservableField");
@Override
protected void initView() {
dataBinding.setInput(inputs);
}
}
fields变量绑定主要是引用带有泛型参数的ObservableField来创建,其中泛型是数据类型。
对于基本类型和 Parcelable 我们可以直接使用对应的包装类:
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
collections数据绑定(集合数据)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="mapKey"
type="String" />
<variable
name="map"
type="androidx.databinding.ObservableMap<String,String>" />
<variable
name="listSubscript"
type="java.lang.Integer" />
<variable
name="list"
type="androidx.databinding.ObservableList<String>" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{map.get(mapKey)}"
android:textColor="@color/black" />
<TextView
android:text="@{list[listSubscript]}"
android:textColor="@color/black"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
public class MainActivity extends BaseActivity<ActivityMainBinding> {
private static final String TAG = MainActivity.class.getName();
ObservableMap<String,String> maps = new ObservableArrayMap<>();
ObservableList<String> lists = new ObservableArrayList<>();
int listSign = 0;
boolean isName = true;
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
if (isName){
isName = false;
dataBinding.setMapKey("name");
}else{
isName = true;
dataBinding.setMapKey("age");
}
handler.removeMessages(1);
handler.sendEmptyMessageDelayed(1, 2000);
break;
case 2:
if (listSign<5){
dataBinding.setListSubscript(listSign);
listSign++;
}else{
listSign = 0;
dataBinding.setListSubscript(listSign);
}
handler.removeMessages(2);
handler.sendEmptyMessageDelayed(2, 2000);
break;
}
}
};
@Override
protected void data() {
maps.clear();
maps.put("name","Map集合演示:1");
maps.put("age","Map集合演示:2");
dataBinding.setMap(maps);
lists.clear();
for (int i = 0; i < 5; i++) {
lists.add("list集合数据演示:" + i);
}
dataBinding.setList(lists);
handler.removeMessages(1);
handler.sendEmptyMessageDelayed(1, 2000);
handler.removeMessages(2);
handler.sendEmptyMessageDelayed(2, 2000);
}
}
对于集合而言,List是通过下标去取值的,而Map是通过key取value,我通过定义两个变量listSubscript和mapKey,通过代码修改这两个变量来改变取值。
对于集合提供的包装类
ObservableArrayList
ObservableArrayMap
objects数据绑定(对象)
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.welbell.recipes.bean.UserBean" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.age)}"
android:textColor="@color/black" />
</LinearLayout>
</layout>
public class UserBean extends BaseObservable {
private String name;
private int age;
public UserBean(String name, int age) {
this.name = name;
this.age = age;
notifyPropertyChanged(BR.user);
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age);
}
}
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 3:
age++;
UserBean userBean = new UserBean("tao",age);
dataBinding.setUser(userBean);
handler.removeMessages(3);
handler.sendEmptyMessageDelayed(3, 2000);
break;
}
}
};
@Override
protected void data() {
handler.removeMessages(3);
handler.sendEmptyMessageDelayed(3, 2000);
}
对应对象而言必须BaseObservable或者Observable,BaseObservable相对于Observable比较完善,使用更简单,get方法需要加注解@Bindable,set方法使用notifyPropertyChanged通知对于变量更新。
Observable 接口具有添加和移除监听器的机制,但何时发送通知必须由您决定。为便于开发,数据绑定库提供了用于实现监听器注册机制的 BaseObservable 类。实现 BaseObservable 的数据类负责在属性更改时发出通知。具体操作过程是向 getter 分配 Bindable 注释,然后在 setter 中调用 notifyPropertyChanged() 方法
双向绑定
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="input"
type="androidx.databinding.ObservableField<String>" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{input}"
android:textColor="@color/black" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入名称"
android:text="@={input}" />
</LinearLayout>
</layout>
双向绑定直接在@{}中间加上@={}
事件绑定
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="clickHandler"
type="com.welbell.recipes.ClickHandler" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:onClick="@{clickHandler::onSubmit}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/submit" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{clickHandler::onConfirm}"
android:text="@string/confirm" />
</LinearLayout>
</layout>
@Override
protected void initView() {
dataBinding.setClickHandler(clickHandler);
}
ClickHandler clickHandler = new ClickHandler(){
@Override
public void onSubmit(View v) {
Log.d(TAG, "onSubmit:");
}
@Override
public void onConfirm(View v) {
Log.d(TAG, "onConfirm:");
}
};
public interface ClickHandler {
void onSubmit(View v);
void onConfirm(View v);
}
事件绑定个人认为有点本末倒置,没有那么方便好用,不过写到这里了,还是说一下,事件绑定与数据绑定variable标签内容与对象绑定差不多,都需要导入一个类(内部类或者回调接口),在写方法的时候需要注意一下带个参数view或者在组件中使用(View)->clickHandler::onConfirm,个人习惯卸载方法参数中,其中“::”也可以换成"."方法名,还有就是不管是方法还是类都需要使用public修饰一下。
BindingAdapter(自定义参数绑定)
目前已经支持的双向绑定的参数列表如下:
除了上述的参数外,我们也可以使用 BindingAdapter 创建自定义参数。
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<import
alias="string"
type="java.lang.String" />
<variable
name="networkPictures"
type="string" />
<variable
name="localPictures"
type="java.lang.Integer" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ImageView
image="@{networkPictures}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/network_local"
defaultLocalImage="@{localPictures}"
netWorkImage="@{networkPictures}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
@Override
protected void data() {
dataBinding.setNetworkPictures("https://x0.ifengimg.com/ucms/2022_10/8A28619D49C8DE5999F84E9101E304F4F9DA73E1_size127_w1008_h435.jpg");
dataBinding.setLocalPictures(R.mipmap.ic_launcher);
}
@BindingAdapter("image")
public static void setImage(ImageView v, String url) {
if (!TextUtils.isEmpty(url)) {
Picasso.get().load(url).into(v);
} else {
v.setBackgroundColor(Color.GRAY);
}
}
@BindingAdapter(value = {"netWorkImage","defaultLocalImage"},requireAll = false)
public static void setImage(ImageView v, String url,int rid){
if (!TextUtils.isEmpty(url)) {
Picasso.get().load(url).placeholder(R.mipmap.ic_launcher).into(v);
} else {
v.setImageResource(rid);
}
}
layout中的image、defaultLocalImage与netWorkImage都是通过BindingAdapter自定义的,需要注意的是定义的方法必须是静态方法哦,关于URL的传入就和正常的数据绑定一样即可。
LiveData
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 activity、fragment 或 service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。
您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 activity 和 fragment 特别有用,因为它们可以放心地观察 LiveData 对象,而不必担心泄露(当 activity 和 fragment 的生命周期被销毁时,系统会立即退订它们)。
如需详细了解如何使用 LiveData,请参阅使用 LiveData 对象。
LiveData对象在使用时,需要注意一下LiveData不能直接被改变,需要改变LiveData时请使用MutableLiveData。
//点击事件
ClickHandler mClickHandler = new ClickHandler(){
@Override
public void onSubmit(View v) {
Log.d(TAG, "onSubmit: ");
liveData.setValue("更新演示数据");
}
@Override
public void onConfirm(View v) {
Log.d(TAG, "onConfirm: ");
}
};
MutableLiveData<String> liveData = new MutableLiveData<>("演示");
public void LiveDateMonitor(){
Observer<String> liveDataObserver = s -> {
dataBinding.setContents(s);
};
liveData.observe(this,liveDataObserver);
}
总结
针对上述描述,以满足databinding的初步使用,demo关于LiveData部分尚未完善,暂不上传(有需要的可以留言),由于工作原因,暂时先更新这么多,若是有人针对liveData有更好的例子,大家可以推荐一下,我这边也会找时间更新这部分。
有兴趣的也可以去官网瞅瞅:DataBinding ViewBinding