Blog of CodeJELLOW’S ENGINEERING NOTES
← 返回文章

Jetpack之DataBinding

Jetpack之DataBinding DataBinding的基础使用 对include的组件进行数据绑定 使用BaseAdapter对不可直接绑定的组件绑定数据 使用BaseObservable进行双向绑定 使用ObservableField进行双向绑定 …

DataBinding的基础使用

将.xml文件转为DataBinding的文件,会出现一个<data>的标签,在其中可以定义变量<variables>或导入<import>:

TEXT
    <data>

        <variable
            name="idol"
            type="com.shopkeeper.databinding.Idol" />

        <import type="com.shopkeeper.databinding.StarUtils" />

        <variable
            name="eventHandler"
            type="com.shopkeeper.databinding.EventHandleListener" />
    </data>

在绑定数据时,使用@{}语法:

TEXT
        <TextView
            android:text="@{idol.name}"/>
        <TextView
            android:text="@{StarUtils.getDarling(idol.star)}"/>

在绑定事件的监听时,以onClick为例,可以使用@{}传入一个方法:

TEXT
        <Button
            android:onClick="@{eventHandler.btnOnClick}"/>

该方法会传入一个View的实例

TEXT
public class EventHandleListener {
    private Context context;

    public EventHandleListener(Context context) {
        this.context = context;
    }

    public void btnOnClick(View view){
        Toast.makeText(context,"like 02",Toast.LENGTH_SHORT).show();
    }
}

也可以绑定一个监听器(可以传入任意参数),以一个viewModel的add方法为例:

TEXT
        <Button
            android:onClick="@{()->viewModel.add()}"/>

对include的组件进行数据绑定

需要使用app命名空间传入该变量名。例如sub.xml定义的一个variable是idol:

TEXT
    <data>
        <variable
            name="idol"
            type="com.shopkeeper.databinding2.Idol" />
        <import type="com.shopkeeper.databinding2.StarUtils"/>
    </data>

那么在include其的main.xml中,使用app:idol传入其需要的变量:

TEXT
        <include
            layout="@layout/sub"
            app:idol="@{idol}"
            >

使用BaseAdapter对不可直接绑定的组件绑定数据

以ImageView为例,显示图片时需要使用到glide,因此可以定义一个类,用@BindingAdapter来注解用于处理图片的方法:

TEXT
public class IVBindingAdapter {
    @BindingAdapter("image")
    public static void setImage(ImageView imageView,String url){
        if (!TextUtils.isEmpty(url)){
            Glide.with(imageView)
                    .load(url)
                    .into(imageView);
        }
    }
}

@BindingAdapter括号内的字符串即是在ImageView组件中需要使用的属性(用法类似include传参),例如此时括号内定义的value是”image”,那么在ImageView中需要传入image属性:

TEXT
        <ImageView
            android:id="@+id/imageView"
            app:image="@{networkImage}" />

由于value可以传入一个数组

图片暂不可用。

因此可以传入多个参数,并通过requireAll来配置是否需要全部传入:

TEXT
public class IVBindingAdapter {
    @BindingAdapter(value = {"image","default_image"},requireAll = false)
    public static void setImage(ImageView imageView,String url,int res){
        if (!TextUtils.isEmpty(url)){
            Glide.with(imageView)
                    .load(url)
                    .placeholder(R.drawable.ic_launcher_background)
                    .into(imageView);
        }else {
            Glide.with(imageView)
                    .load(res)
                    .placeholder(R.drawable.ic_launcher_background)
                    .into(imageView);        }
    }
}

在xml中也需要传入这两个参数:

TEXT
        <ImageView
            android:id="@+id/imageView"
            app:image="@{networkImage}"
            app:default_image="@{localImage}" />

使用BaseObservable进行双向绑定

自定义一个继承于BaseObservable的ViewModel(并不是Jetpack的ViewModel类),可用于双向绑定,具体做法如下:

想要绑定一个数据,就需要在其get方法中用@Bindable注解:

TEXT
    private User user;

    @Bindable
    public String getUserName(){
        return user.userName;
    }

在对其进行修改的时候,需要调用notifyPropertyChanged以声明其已经改变:

TEXT
    public void setUserName(String name){
        if (!TextUtils.isEmpty(name)&&!name.equals(user.userName)){
            user.userName=name;
            Log.d("shopkeeperTag", "setUserName: "+name);
//            BR是用Bindable注解标记的量,且必须是getXXX
            notifyPropertyChanged(BR.userName);
        }
    }

在xml中进行数据的双向绑定时,需要使用@={}语法:

TEXT
        <EditText
            android:text="@={viewModel.userName}" />

使用ObservableField进行双向绑定

ObservableField的使用类似于LiveData:

TEXT
private ObservableField<User> userObservableField;

此时,只需要定义需要绑定的变量的setter和getter方法之后,并在xml中使用@={}语法,就能完成双向绑定:

TEXT
    public String getUserName(){
        return userObservableField.get().userName;
    }

    public void setUserName(String userName){
        Log.d("shopkeeperTag", "setUserName: "+userName);
        userObservableField.get().setUserName(userName);
    }

此方法的一个弊端是,必须使用setter方法来对数据进行修改,否则更改不会应用到视图上。

对RecyclerView进行数据绑定

本质上就是在RecyclerView.Adapter中对每一个Item进行数据绑定,onCreateViewHolder中返回的是通过item的DataBinding的实例创建出来的组件对象;onBindViewHolder中对item的DataBinding实例设置了数据。

TEXT
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {

    List<Idol> idols;

    public RecyclerViewAdapter(List<Idol> idols) {
        this.idols = idols;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemBinding binding=DataBindingUtil.inflate(LayoutInflater.from(parent.getContext())
            ,R.layout.item,parent,false);
        return new MyViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        Idol idol=idols.get(position);
        holder.binding.setIdol(idol);
    }

    @Override
    public int getItemCount() {
        return idols.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder{

        private ItemBinding binding;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        public MyViewHolder(@NonNull ItemBinding binding) {
            super(binding.getRoot());
            this.binding=binding;
        }
    }
}

结合ViewModel和LiveData使用

用法与ObservableField类似,只不过不需要专门设置getter和setter,直接在xml中进行绑定livedata对象即可。

通过 RSS 订阅更新 →