一、DataBinding简介

在传统的Android利用开发中,布局文件通常只负责利用界面的布局工作,如果须要实现页面交互就须要调用setContentView()将Activity、fragment和XML布局文件关联起来。而后通过控件的id找到控件,接着在页面中通过代码对控件进行逻辑解决。在这种传统的开发方式中,页面承当了大部分的工作量,大量的逻辑解决须要在Activity、Fragment中进行解决,因而页面显得臃肿不堪,保护起来也很艰难,为了加重页面的工作量,Google提出了DataBiiding(视图绑定)。

DataBinding的呈现让布局文件承当了本来属于Activity、Fragment页面的局部逻辑,使得Activity页面和XML布局之间的耦合度进一步升高。DataBinding次要有以下特点:

  • 代码更加简介,可读性高,可能在XML文件中实现UI管制。
  • 不再须要findViewById操作,
  • 可能与Model实体类间接绑定,易于保护和扩大。

事实上,DataBinding和MVVM架构是分不开的,DataBinding正是Google为了可能更好的实现MVVM架构而实现的。

二、DataBinding根本应用

2.1 开启viewBinding

视图绑定性能可按模块启用,要在某个模块中启用视图绑定,请将 viewBinding 元素增加到build.gradle 文件中,如下所示。

android {        ...        viewBinding {            enabled = true        }    }

DataBinding 是一个 support 包,增加完后下面的脚本后会发现工程的的External Libraries中多了四个aar包。别离是adapters、commen、runtime和viewbinding。

应用DataBinding时,如果心愿在生成绑定类时疏忽某个布局文件,能够将 tools:viewBindingIgnore="true" 属性增加到相应布局文件的根视图中,如下所示。

<LinearLayout   ...    tools:viewBindingIgnore="true" >   ...</LinearLayout>

2.2 批改布局文件

应用DataBinding的第一步,就是先革新XML文件。其实革新布局文件也特地简略,只须要在原来文件内容的根底上,最外层改为<layout>标签即可,如下所示。

<?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">    <androidx.constraintlayout.widget.ConstraintLayout        android:layout_width="match_parent"        android:layout_height="match_parent">        ...         <TextView            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"             android:textSize="24dp"            android:text="HelloWord" />       ...     </androidx.constraintlayout.widget.ConstraintLayout></layout>

当然,咱们也能够选中根布局,按住【Alt + 回车键】而后抉择 【Convert to data binding layout】也能够生成 DataBinding 须要的布局规定。在布局最外层加layout标签后,从新编译我的项目,DataBinding库就会生成对应的Binding类,该类用来实现XML布局文件与Model类的绑定,代码如下。

public class ActivityMainBindingImpl extends ActivityMainBinding  {    @Nullable    private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes;    @Nullable    private static final android.util.SparseIntArray sViewsWithIds;    static {        sIncludes = null;        sViewsWithIds = null;    }    // views    @NonNull    private final androidx.constraintlayout.widget.ConstraintLayout mboundView0;    // variables    // values    // listeners    // Inverse Binding Event Handlers    public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {        this(bindingComponent, root, mapBindings(bindingComponent, root, 1, sIncludes, sViewsWithIds));    }    private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {        super(bindingComponent, root, 0            );        this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];        this.mboundView0.setTag(null);        setRootTag(root);        // listeners        invalidateAll();    }    @Override    public void invalidateAll() {        synchronized(this) {                mDirtyFlags = 0x1L;        }        requestRebind();    }    @Override    public boolean hasPendingBindings() {        synchronized(this) {            if (mDirtyFlags != 0) {                return true;            }        }        return false;    }    @Override    public boolean setVariable(int variableId, @Nullable Object variable)  {        boolean variableSet = true;            return variableSet;    }    @Override    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {        switch (localFieldId) {        }        return false;    }    @Override    protected void executeBindings() {        long dirtyFlags = 0;        synchronized(this) {            dirtyFlags = mDirtyFlags;            mDirtyFlags = 0;        }        // batch finished    }    // Listener Stub Implementations    // callback impls    // dirty flag    private  long mDirtyFlags = 0xffffffffffffffffL;    /* flag mapping        flag 0 (0x1L): null    flag mapping end*/    //end}

生成的ActivityMainBindingImpl代码位于app/build目录下。生成Binding类的名字很非凡,它与XML布局文件的名字有对应关系,具体的分割就是,以XML布局文件为准,去掉下划线,所有单次以大驼峰的模式按程序拼接,最初再加上Binding。比方,我的XML布局文件名是activity_main.xml,生成的Binding类名就是ActivityMainBinding。

2.3 绑定布局

没有应用DataBinding的时候,为了将XML布局文件与Activity进行绑定,须要调用Activity的setContentView()办法,或者是在Fragment中调用LayoutInflate的inflate()办法来进行布局的绑定。如果应用了DataBinding之后,就须要应用DataBindingUtil类来进行视图的绑定,如下所示。

public class MainActivity extends AppCompatActivity {    private ActivityMainBinding binding;     @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        binding= DataBindingUtil.setContentView(this,R.layout.activity_main);         }}

应用DataBindingUtil类的setContentView()办法对Activity进行绑定,其返回值就是工具生成的Binding类。如果是Fragment,则对应的绑定布局的代码如下。

    private ActivityMainBinding binding;    @Override    public View onCreateView (LayoutInflater inflater,                              ViewGroup container,                              Bundle savedInstanceState) {        binding = ActivityMainBinding.inflate(inflater, container, false);        View view = binding.getRoot();        return view;    }    @Override    public void onDestroyView() {        super.onDestroyView();        binding = null;    }

2.4 增加data标签

通过后面的步骤后,咱们曾经应用DataBinding将XML文件与UI组件绑定起来,如果要在XML文件中承受Model数据,就须要用到data标签与variable标签。

在XML文件的layout标签下,创立data标签,在data标签中再创立variable标签,variable标签次要用到的就是name属性和type属性,相似于Java语言申明变量时,须要为该变量指定类型和名称。新建一个名为UserModel的实体类,代码如下。

public class UserModel {    private String firstName;    private String lastName;    public UserModel(String firstName, String lastName) {        this.firstName = firstName;        this.lastName = lastName;    }    public String getFirstName() {        return this.firstName;    }    public String getLastName() {        return this.lastName;    }}

而后在布局的 data 标签里申明要应用到的变量名、类的全门路等信息,如下所示。

<?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">    <data>      <variable          name="user"          type="com.xzh.jetpack.UserModel" />    </data>    ... //省略其余代码</layout>

如果 User 有多处用到,也能够间接将之 import 进来,这样就不必每次都指明整个包名门路了,而 java.lang.* 包中的类会被主动导入,所以能够间接应用。

<data>   <import type="com.xzh.jetpack.UserModel" />   <variable       name="user"       type="UserModel" />  </data>

如果存在 import 的类名雷同的状况,能够应用 alias 指定别名,如下所示。

    <data>        <import type="com.xzh.jetpack.UserModel" />        <import            alias="TempUser"            type="com.xzh.jetpack.uer.UserModel" />        <variable            name="userInfo"            type="User" />        <variable            name="tempUserInfo"            type="TempUser" />    </data>

2.5 应用variable

在XML文件中申明好variable属性后,接下来就能够在XML应用它了。应用variable属性时须要应用到布局表达式: @{ }。能够在布局表达式@{ }中获取传入variable对象的值,如下所示。

<?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">    <data>        <import type="com.xzh.jetpack.UserModel" />        <variable            name="user"            type="UserModel" />    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center"        android:orientation="vertical">        <TextView            android:id="@+id/tv1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.firstName}"            android:hint="Tv1"            android:textSize="24dp" />        <TextView            android:id="@+id/tv2"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.lastName}"            android:textSize="24dp" />    </LinearLayout></layout>

而后,咱们在UI界面中应用Binding类为每一个variable标签应用set办法传递数据,如下所示。

 binding= DataBindingUtil.setContentView(this,R.layout.activity_main); UserModel user =new UserModel("zhang", "beijing"); binding.setUser(user);

通过下面的解决后,咱们曾经给UserModel对象设置给了Binding类,所以这里间接运行代码就能够看到成果了。

2.6 响应事件

后面咱们介绍了DataBinding的一些根本用法,咱们能够在布局文件中对控件某些属性进行赋值,使得Model类数据间接绑定在布局中,而且Model属性发生变化时,布局文件中的内容能够即时刷新。除了这些简略的应用场景外,咱们还能够应用DataBinding响应用户事件。

咱们对布局文件做一下批改,在外面增加一个控件,而后在Activity中增加如下代码。

 binding.btn1.setOnClickListener(new View.OnClickListener() {            @Override     public void onClick(View v) {       }  });

除此之外,咱们还能够应用另外一种形式。布局表达式不仅能够传入对象的属性,也能够调用对象的办法。首先创立一个工具类,在类中定义响应事件的办法,如下所示。

public class ButtonClickListener {    public void onClick(View view) {        Log.d("ButtonClickListener","onClick...");    }}

而后在布局文件中增加点击事件的代码,如下所示。

<?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">    <data>        <variable            name="btnHandler"            type="com.xzh.jetpack.databinding.ButtonClickListener" />    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center"        android:orientation="vertical">       ...//省略其余代码        <Button            android:id="@+id/btn1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="按钮"            android:textSize="24dp"            android:onClick="@{btnHandler.onClick}"/>    </LinearLayout></layout>

在下面的代码中,首先在data标签中为ButtonClickListener类申明对象,在Button的onClick属性中传入布局表达式即可。

2.7 include标签

在Android利用开发中,为了可能让布局文件失去复用,在编写布局的时候咱们常常会应用include标签,雷同构造与内容的布局文件就能够在多处应用。然而如果一个布局文件中应用了DataBinding,同时也应用了include标签,那么如何应用nclude标签引入的布局文件中中的数据呢。

此时,咱们须要在同一级页面的include标签中,通过命名控件xmlns:app来引入布局变量User,将数据对象传递给二级页面,如下所示。

<?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">    <data>        <variable            name="user"            type="com.xzh.jetpack.UserModel" />    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center"        android:orientation="vertical">        <include            layout="@layout/layout_data_binding"            app:persondata="@{user}" />                ... //省略其余代码    </LinearLayout></layout>

布局表达式中间接传入页面变量user,include标签属性值能够任意取名,然而要留神的是,在二级页面的variable标签中的name属性,必须与一级页面中的include标签属性名统一,如layout_data_binding的代码所示。

<?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">    <data>        <variable            name="userData"            type="com.xzh.jetpack.UserModel" />    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{userData.firstName}"            android:gravity="center" />    </LinearLayout></layout>

三、BindingAdapter

3.1 BindingAdapter简介

应用DataBinding库时,DataBinding会针对控件属性生成对应的XXXBindingAdapter类,如TextViewBindingAdapter类,其对TextView的每个能够应用DataBinding的属性都生成了对应的办法,而且每个办法都应用了@BindingAdapter注解,注解中的参数就是对应View的属性。

@RestrictTo(RestrictTo.Scope.LIBRARY)@SuppressWarnings({"WeakerAccess", "unused"})@BindingMethods({        @BindingMethod(type = TextView.class, attribute = "android:autoLink", method = "setAutoLinkMask"),        @BindingMethod(type = TextView.class, attribute = "android:drawablePadding", method = "setCompoundDrawablePadding"),        @BindingMethod(type = TextView.class, attribute = "android:editorExtras", method = "setInputExtras"),        @BindingMethod(type = TextView.class, attribute = "android:inputType", method = "setRawInputType"),        @BindingMethod(type = TextView.class, attribute = "android:scrollHorizontally", method = "setHorizontallyScrolling"),        @BindingMethod(type = TextView.class, attribute = "android:textAllCaps", method = "setAllCaps"),        @BindingMethod(type = TextView.class, attribute = "android:textColorHighlight", method = "setHighlightColor"),        @BindingMethod(type = TextView.class, attribute = "android:textColorHint", method = "setHintTextColor"),        @BindingMethod(type = TextView.class, attribute = "android:textColorLink", method = "setLinkTextColor"),        @BindingMethod(type = TextView.class, attribute = "android:onEditorAction", method = "setOnEditorActionListener"),})public class TextViewBindingAdapter {    private static final String TAG = "TextViewBindingAdapters";    @SuppressWarnings("unused")    public static final int INTEGER = 0x01;    public static final int SIGNED = 0x03;    public static final int DECIMAL = 0x05;    @BindingAdapter("android:text")    public static void setText(TextView view, CharSequence text) {        final CharSequence oldText = view.getText();        if (text == oldText || (text == null && oldText.length() == 0)) {            return;        }        if (text instanceof Spanned) {            if (text.equals(oldText)) {                return; // No change in the spans, so don't set anything.            }        } else if (!haveContentsChanged(text, oldText)) {            return; // No content changes, so don't set anything.        }        view.setText(text);    }    @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")    public static String getTextString(TextView view) {        return view.getText().toString();    }    @BindingAdapter({"android:autoText"})    public static void setAutoText(TextView view, boolean autoText) {        KeyListener listener = view.getKeyListener();        TextKeyListener.Capitalize capitalize = TextKeyListener.Capitalize.NONE;        int inputType = listener != null ? listener.getInputType() : 0;        if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {            capitalize = TextKeyListener.Capitalize.CHARACTERS;        } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0) {            capitalize = TextKeyListener.Capitalize.WORDS;        } else if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) {            capitalize = TextKeyListener.Capitalize.SENTENCES;        }        view.setKeyListener(TextKeyListener.getInstance(autoText, capitalize));    }}

BindingAdapter类中,所有的办法都是static办法,并且每个办法都应用了@BindingAdapter注解,注解中申明所操作的View属性,当应用了DataBinding的布局文件被渲染时,属性所对应的static办法就会主动调用。

3.2 自定义BindingAdapter

除了应用库主动生成的BindingAdapter类之外,开发者也能够自定义BindingAdapter类,供开发者来实现零碎没有提供的属性绑定,或者是批改原有的属性。

例如,有上面这样一个需要:咱们心愿能够依据图片地址动静的扭转显示图片。如果应用BindingAdapter 如何实现呢?

此处,咱们加载图片应用的是glide图片库,并且加载图片须要拜访网路,所以请确保申请了网路权限。

 <uses-permission android:name="android.permission.INTERNET"/>

接下来,咱们编写一个解决图片的自定义BindingAdapter类。而后定义一个静态方法,次要用于增加 BindingAdapter 注解,注解值是 ImageView 控件自定义的属性名,如下所示。

public class ImageBindingAdapter {    @BindingAdapter({"url"})    public static void loadImage(ImageView view, String url) {       if(!TextUtils.isEmpty(url)){           Glide.with(view)                   .load(url)                   .centerCrop()                   .placeholder(R.drawable.ic_launcher_background)//加载中显示的图片                   .error(R.drawable.ic_launcher_foreground)// 谬误后显示的图片                   .into(view);       }    }}

能够发现没,loadImage()静态方法的两个参数,第一个参数必须是所操作的View类型,第二个参数是图片的地址。当 ImageView 控件的 url 属性值发生变化时,dataBinding 就会将 ImageView 实例以及新的 url 值传递给 loadImage() 办法,从而实现动静扭转 ImageView的相干属性。

而后,咱们在 XML 文件中关联变量值,如下所示。

<?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">    <data> </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:gravity="center">        <ImageView            android:layout_width="300dp"            android:layout_height="200dp"            app:url="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"/>    </LinearLayout></layout>

须要阐明的是,请留神布局文件的最外层蕴含以下命名控件,这样能力调用@BindingAdapter标签定义的静态方法。

xmlns:app="http://schemas.android.com/apk/res-auto"

须要阐明的是,Activity和XML绑定的代码肯定要用DataBindingUtil,如下所示。

DataBindingUtil.setContentView(this,R.layout.activity_main);

通过下面的解决后,咱们就能够很不便的应用 imageUrl 属性来加载网络图片了,并且不必放心线程切换问题,DataBinding 库会主动实现线程切换。运行下面的代码,成果如下所示。

有时候,咱们须要自定义多个属性,那如何解决呢?和一个参数一样,咱们只须要应用BindingAdapter增加参数即可,如下所示。

public class ImageBindingAdapter {    @BindingAdapter(value = {"url", "placeholder", "error"})    public static void loadImage(ImageView view, String url, Drawable placeholder, Drawable error) {       if(!TextUtils.isEmpty(url)){           RequestOptions options = new RequestOptions();           options.placeholder(placeholder);           options.error(error);           Glide.with(view.getContext())                   .load(url)                   .apply(options)                   .into(view);       }    }}

而后在布局中传入属性值即可,如下所示。

<ImageView       android:layout_width="300dp"       android:layout_height="200dp"       android:layout_marginTop="10dp"       app:url="@{`https://goss.veer.com/creative/vcg/veer/800water/veer-136599950.jpg`}"       app:placeholder="@{@drawable/icon}"       app:error="@{@drawable/error}"/>

3.3 BindingConversion

在某些状况下,咱们须要对设置属性时类型进行转化,类型转化能够借助注解 @BindingConversion 来实现。例如,android:background 属性接管的是一个 Drawable ,然而咱们在应用的时候须要给databinding 表达式中设置一个色彩值,此时就须要 @BindingConversion

首先,创立一个色彩转化的类ColorConversion,用于将色彩值转化为Drawable,如下所示。

public class ColorConversion {        @BindingConversion    public static ColorDrawable colorToDrawable(int color){        return new ColorDrawable(color);    }}

而后,创立一个布局文件,增加如下代码。

<?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">    <data> </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:gravity="center_horizontal">                <!--类型转换-->        <ImageView            android:layout_width="100dp"            android:layout_height="100dp"            android:background="@{true ? @color/colorRed : @color/colorBlue}"/>    </LinearLayout></layout>

在布局中应用 @BindingConversion 注解时要应用雷同类型,否则会报错。

四、双向绑定

DataBinding的自身是对View层状态的一种观察者模式的实现,通过让View与ViewModel层可察看的对象(比方LiveData)进行绑定,当ViewModel层数据发生变化,View层也会主动进行UI的更新,此种场景称之为单向绑定。

但理论开发中,单向绑定并不能足够所有的需要。例如有上面的场景:如果布局中有一个EditText,当用户在输入框中输出内容时,咱们心愿对应的Model类可能实时更新,这就须要双向绑定,DataBinding同样反对这样的能力。

实现双向绑定须要用到ObservableField类,它可能将一般的数据对象包装成一个可察看的数据对象,数据能够是根本类型变量、汇合,也能够是自定义类型。为了实现双向绑定,咱们须要先定义一个继承自BaseObservable的ViewModel类,并提供get和set办法,如下所示。

public class UserViewModel extends BaseObservable {    private String name;    private String address;    private int age;    @Bindable    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;        notifyPropertyChanged(BR.name);    }    ... //省略其余代码}

而后,咱们在XML布局文件中应用DataBinding,如下所示。

<?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">    <data>        <variable            name="userModel"            type="com.xzh.jetpack.UserViewModel" />    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:gravity="center"        android:orientation="vertical">        <EditText            android:id="@+id/et1"            android:layout_width="match_parent"            android:layout_height="50dp"            android:text="@={userModel.name}" />        <Button            android:id="@+id/btn1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:paddingLeft="30dp"            android:paddingRight="30dp"            android:textSize="24dp"            android:text="保留" />        <TextView            android:id="@+id/tv1"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:textSize="24dp"            android:text="@={userModel.name}" />    </LinearLayout></layout>

须要留神的是,不同于单向绑定,之前的布局表达式是@{}表达式,双向绑定应用的布局表达式是@={},多了一个等号。

接下来,咱们在Activity中增加获取用户输出的逻辑,如下所示。

public class MainActivity extends AppCompatActivity {    private final String TAG = "MainActivity";    private ActivityMainBinding activityMainBinding;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        activityMainBinding= DataBindingUtil.setContentView(this,R.layout.activity_main);        activityMainBinding.btn1.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                String name=activityMainBinding.et1.getText().toString();                UserViewModel viewModel=new UserViewModel();                viewModel.setName(name);                activityMainBinding.setUserModel(viewModel);            }        });    }}

通过下面的解决后,双向绑定就根本实现了。能够发现,双向绑定与LiveData十分类似,都是将一般的数据对象封装成了可察看对象,实践上二者是能够相互代替的,但LiveData具备生命周期感知能力,并且须要调用observe()办法进行监听,而双向绑定中更举荐应用ObservableField,不须要应用observe()办法,保护起来绝对简略。

五、在RecyclerView中应用DataBinding

5.1 根本应用

列表布局在Android利用开发中是十分常见的场景,实现列表布局须要应用RecyclerView控件,DataBinding反对在RecyclerViieew中实现数据绑定。

应用RcyclerView,就须要用到Adapter,在Adapter中实例化Item布局,而后将List中的数据绑定到布局中,而DataBinding就能够帮忙开发者实例化布局并绑定数据。

首先,咱们编写Adapter的item布局,在item布局中应用DataBinding将User数据进行绑定,item_user.xml的代码如下所示。

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable            name="user"            type="com.xzh.jetpack.UserModel" />    </data>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_margin="10dp"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.name}" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.address}" />        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@{user.age}" />    </LinearLayout></layout>

接下来,编写Adapter类业务解决,UserAdapter的代码如下所示。

public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder> {    private List<UserModel> mDataList;    public UserAdapter(List<UserModel> mDataList) {        this.mDataList = mDataList;    }    public class ViewHolder extends RecyclerView.ViewHolder {        ItemUserBinding binding;        public ViewHolder(@NonNull ViewDataBinding binding) {            super(binding.getRoot());            this.binding=(ItemUserBinding)binding;        }        public ItemUserBinding getBinding() {            return binding;        }    }    @NonNull    @Override    public UserAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {        ItemUserBinding binding= DataBindingUtil.inflate((LayoutInflater.from(parent.getContext())), R.layout.item_user,parent,false);        return new ViewHolder(binding);    }    @Override    public void onBindViewHolder(@NonNull UserAdapter.ViewHolder holder, int position) {        UserModel model=mDataList.get(position);        holder.getBinding().setUser(model);    }    @Override    public int getItemCount() {        if (mDataList.size()>0){            return  mDataList.size();        }        return 0;    }}

能够发现,之前咱们都是须要在ViewHolder中进行findViewById对子控件进行实例化,因为咱们应用了DataBinding,所以不再须要这些操作,只须要传入生成的Binding类即可,而后在super中调用getRoot()办法返回根View。

在RecyclerView中应用DataBinding就是如此简略,当List中的item数据发生变化时,列表中的内容也会随之更新。

而后,依照RecyclerView的根本应用办法,咱们在MainActivity增加一些测试数据,并将它和UserAdapter进行绑定,代码如下。

public class MainActivity extends AppCompatActivity {    private final String TAG = "MainActivity";    private ActivityMainBinding activityMainBinding;    private List<UserModel> userModels;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);        initData();        initRecyclerView();    }    private void initData() {        userModels = new ArrayList<UserModel>();        for (int i = 0; i < 10; i++) {            UserModel userModel = new UserModel("zhangsan"+1, "beijing"+i, "age"+i);            userModels.add(userModel);        }    }    private void initRecyclerView() {        LinearLayoutManager layoutManager = new LinearLayoutManager(this);        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);        activityMainBinding.recycle.setLayoutManager(layoutManager);        activityMainBinding.recycle.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));        UserAdapter adapter = new UserAdapter(userModels);        activityMainBinding.recycle.setAdapter(adapter);    }}

5.2 自定义BindingAdapter

在下面的代码中,对RecyclerView设置LayoutManager和Adapter属于对View的一些简单操作,这些操作能够通过自定义BindingAdapter的形式进行简化。首先,定义一个新的属性,将数据List间接通过DataBinding在布局文件中绑定,并且将这些操作都封装到BindindAdapter中,Activity中不再须要设置LayoutManager和Adapter操作。

首先,定义BindingAdapter,如下所示。

public class UserBindingAdapter {    @BindingAdapter("users")    void setUsers(RecyclerView recyclerView, List<UserModel> users )  {        LinearLayoutManager layoutManager = new LinearLayoutManager(recyclerView.getContext());        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);        recyclerView.setLayoutManager(layoutManager);        UserAdapter adapter = new UserAdapter(users);        recyclerView.setAdapter(adapter);    }}

在下面的代码中,咱们申明了一个新的属性users,而后应用@BindingAdapter润饰静态方法,而后在办法外面对RecyclerView设置LayoutManager和Adapter。接下来,咱们只须要布局中应用DataBinding即可。

<?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">    <data>        <import type="com.xzh.jetpack.UserModel" />        <import type="java.util.List" />        <variable            name="users"            type="List&lt;UserModel&gt;" />    </data>    <androidx.recyclerview.widget.RecyclerView        android:id="@+id/recycle"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:users="@{users}"/></layout>

参考:
Android Jetpack架构组件(七)之WorkManager
Android Jetpack架构组件(六)之Room
Android Jetpack架构组件(五)之Navigation
Android Jetpack架构组件(四)之LiveData
Android Jetpack架构组件(三)之ViewModel
Android Jetpack架构组件(二)之Lifecycle
Android Jetpack架构组件(一)与AndroidX