DataBinding使用介绍

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&lt;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&lt;String,String>" />

        <variable
            name="listSubscript"
            type="java.lang.Integer" />

        <variable
            name="list"
            type="androidx.databinding.ObservableList&lt;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&lt;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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丿末兮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值