dataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,利用该库可以实现在页面组件中直接绑定应用程序的数据源。
dataBinding翻译后叫数据绑定,它的主要作用是与UI界面绑定,通过dataBinding获取页面数据与修改页面数据。同时dataBinding还具有观察性,数据模型或者UI界面数据改变之后,它可以同步数据。
由于Android开发语言的限制,在数据加载问题上设计了MVC、MVP架构不是代码臃肿就是方法满天飞,导致代码维护难度很大,开发者也很难准确的使用这些架构,还有就是这些架构从根本上来讲,还是没有解决数据与页面的绑定关系。dataBinding的就是为了解决这样的问题而出现,只要创建好对应实体类或者方法,即可轻松将数据绑定到UI上,UI随着数据的改变自动更新,反之亦然,大大的减少了代码量。
这也是最基础的一步,在module中的build.geadle中配置
android {
...
buildFeatures {
dataBinding true
}
}
基础配置
使用layout标签和data标签,其中layout标签需要包裹data标签与UI布局,如上述XML代码
如果您希望在生成绑定类时忽略某个布局文件,请将 tools:viewBindingIgnore="true" 属性添加到相应布局文件的根视图中:
...
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布局关联,返回一个泛型(
DataBinding与组件关联是通过组件id进行关联的例如dataBinding.textView,内部规则采用驼峰式命名风格转换layout中的组件id。
DataBinding与Fragment关联是通过inflate(LayoutInflater inflater, int layoutId, ViewGroup parent, boolean attachToParent)方法绑定的,与activity关联大同小异,有兴趣可以自己研究一下。
上述部分是DataBinding的基础使用部分,有这些我们就可以实现UI界面与Activity或者Fragment的绑定,下面主要说的是数据双向绑定。
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.
public class MainActivity extends BaseActivity {
private static final String TAG = MainActivity.class.getName();
ObservableField inputs = new ObservableField<>("演示ObservableField");
@Override
protected void initView() {
dataBinding.setInput(inputs);
}
}
fields变量绑定主要是引用带有泛型参数的ObservableField来创建,其中泛型是数据类型。
对于基本类型和 Parcelable 我们可以直接使用对应的包装类:
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
public class MainActivity extends BaseActivity {
private static final String TAG = MainActivity.class.getName();
ObservableMap maps = new ObservableArrayMap<>();
ObservableList 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
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() 方法
双向绑定直接在@{}中间加上@={}
@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 创建自定义参数。
@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 具有生命周期感知能力,意指它遵循其他应用组件(如 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 liveData = new MutableLiveData<>("演示");
public void LiveDateMonitor(){
Observer liveDataObserver = s -> {
dataBinding.setContents(s);
};
liveData.observe(this,liveDataObserver);
}
针对上述描述,以满足databinding的初步使用,demo关于LiveData部分尚未完善,暂不上传(有需要的可以留言),由于工作原因,暂时先更新这么多,若是有人针对liveData有更好的例子,大家可以推荐一下,我这边也会找时间更新这部分。
有兴趣的也可以去官网瞅瞅:DataBinding ViewBinding