JetPack–DataBinding

MVP和MVVM中,ViewModel与Presenter很类似,只不过ViewModel和View层多了双向绑定,当ViewModel中数据更改,View层能够知道,反之,View层数据改变,ViewModel也能够改变。ViewModel中LiveData可以被观测,进而在Activity中观测到值发生变化来更新ui,结合DataBinding,我们可以直接在xml中设置ViewModel,进一步简化逻辑,使我们的MVVM构架更加低耦合

一、初识DataBinding

DataBinding是一个可以将xml转换为对象的工具,我们知道对象的属性是可以赋值的,所以我们可以直接只用变量来赋值xml上的属性,使得它更据灵活性
1.在xml中使用实体类

在gradle中添加DataBinding支持

defaultConfig{
...
        dataBinding {
            enabled = true
        }
}

定义一个实体类,我们将把它放入布局文件

package com.aruba.databinding;

/**
 * Created by aruba on 2021/9/11.
 */
public class Idol {
    public String name;
    public String star;

    public Idol(String name, String star) {
        this.name = name;
        this.star = star;
    }
}

在将xml转换后,在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"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="104dp"
            android:text="点赞"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="appear" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@drawable/sijiali"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.248"
            tools:srcCompat="@tools:sample/avatars" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{idol.name}"
            android:textSize="30sp"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView"
            app:layout_constraintVertical_bias="0.232"
            tools:text="name" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{idol.star}"
            android:textSize="24sp"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2"
            app:layout_constraintVertical_bias="0.244"
            tools:text="Star" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

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

    </data>
</layout>

Activity中使用DataBinding将布局转换成对象

package com.aruba.databinding;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;

import android.os.Bundle;

import com.aruba.databinding.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        activityMainBinding.setIdol(new Idol("斯嘉丽·约翰逊", "五星"));
    }
}

效果:

 
2.xml中调用函数

如果我们的实体类中star属性为int型,有一个静态全局函数根据传入的star返回一个字符串,那么怎么调用它

package com.aruba.databinding;

/**
 * Created by aruba on 2021/9/11.
 */
public class Idol {
    public String name;
    public int star;

    public Idol(String name, int star) {
        this.name = name;
        this.star = star;
    }
}

package com.aruba.databinding;

/**
 * Created by aruba on 2021/9/11.
 */
public class StarUtils {
    public static String getStar(int star) {
        String starStr = null;
        switch (star) {
            case 1:
                starStr = "一星";
                break;
            case 2:
                starStr = "二星";
                break;
            case 3:
                starStr = "三星";
                break;
            case 4:
                starStr = "四星";
                break;
            case 5:
                starStr = "五星";
                break;
        }

        return starStr;
    }
}

在xml中使用import标签导入工具类,在相应的地方调用工具类方法

<?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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="104dp"
            android:text="点赞"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="appear" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@drawable/sijiali"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.248"
            tools:srcCompat="@tools:sample/avatars" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{idol.name}"
            android:textSize="30sp"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView"
            app:layout_constraintVertical_bias="0.232"
            tools:text="name" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{StarUtils.getStar(idol.star)}"
            android:textSize="24sp"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2"
            app:layout_constraintVertical_bias="0.244"
            tools:text="Star" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

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

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

    </data>
</layout>

我们还要想要给点赞button调用一个对象的方法,定义对象如下:

package com.aruba.databinding;

import android.content.Context;
import android.view.View;
import android.widget.Toast;

/**
 * Created by aruba on 2021/9/11.
 */
public class OnClickHandler {
    private Context context;

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

    public void onClick(View view) {
        Toast.makeText(context, "点赞", Toast.LENGTH_SHORT).show();
    }
}

xml中加入,并调用函数


    <data>

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

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

        <variable
            name="onClickHandler"
            type="com.aruba.databinding.OnClickHandler" />
    </data>


        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="104dp"
            android:onClick="@{onClickHandler.onClick}"
            android:text="点赞"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="appear" />

Activty中修改传入的参数类型和点击事件对象后,效果如下:

 
3.传递对象至include标签

有时我们会使用include标签封装一些布局,那么这些布局怎么拿到主布局中定义的对象呢

首先定义include布局,转换成DataBing布局

<?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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingVertical="10dp">

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="@{idol.name}"
            android:textSize="30sp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="name" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="36dp"
            android:text="@{StarUtils.getStar(idol.star)}"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2"
            tools:text="Star" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

        <import type="com.aruba.databinding.StarUtils" />
        
        <variable
            name="idol"
            type="com.aruba.databinding.Idol" />

    </data>
</layout>

Activity的xml中引入include布局

<?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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="104dp"
            android:onClick="@{onClickHandler.onClick}"
            android:text="点赞"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            tools:text="appear" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:src="@drawable/sijiali"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.248"
            tools:srcCompat="@tools:sample/avatars" />

        <include
            layout="@layout/include_layout_info"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:idol="@{idol}"
            app:layout_constraintBottom_toTopOf="@+id/button"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView"
            app:layout_constraintVertical_bias="0.32" />


    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

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

        <variable
            name="onClickHandler"
            type="com.aruba.databinding.OnClickHandler" />
    </data>
</layout>

只需要改xml就可以了,非常方便
效果:

 
4.自定义BindingAdapter

虽然我们可以在xml调用函数,但只能针对一些简单逻辑,如果需要加载网络图片,并实现更复杂的逻辑代码,那么使用BindingAdapter是实现它们的好工具,它支持我们自定义一个属性,并实现相应的方法
定义一个类,使用BindingAdapter注解:

package com.aruba.databinding;

import android.widget.ImageView;

import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;

/**
 * Created by aruba on 2021/9/11.
 */
public class ImageViewBindingAdapter {
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, String url) {

    }
}

这样我们就可以在xml中使用image了,我们使用image传入一个变量,界面渲染后,DataBinding会自动调用上面定义的setImage方法

        <variable
            name="imageUrl"
            type="String" />


        <ImageView
            android:id="@+id/imageView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            app:image="@{imageUrl}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.248"
            tools:srcCompat="@tools:sample/avatars" />

在Activity中传入这个imageUrl变量

activityMainBinding.setImageUrl("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp3.ssl.cdn.btime.com%2Ft01ef53f09683630079.jpg%3Fsize%3D640x1137&refer=http%3A%2F%2Fp3.ssl.cdn.btime.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633920976&t=59dab0f58d59d80e00bf3ef182728a80");

我们再对setImage方法进行加工处理

package com.aruba.databinding;

import android.text.TextUtils;
import android.widget.ImageView;

import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;

import com.squareup.picasso.Picasso;

/**
 * Created by aruba on 2021/9/11.
 */
public class ImageViewBindingAdapter {
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, String url) {
        if (!TextUtils.isEmpty(url)) {
            Picasso.get()
                    .load(url)
                    .placeholder(R.drawable.sijiali)
                    .into(imageView);
        } else {
            imageView.setImageResource(R.drawable.sijiali);
        }
    }
}

 

BindingAdapter也支持重载

package com.aruba.databinding;

import android.text.TextUtils;
import android.widget.ImageView;

import androidx.databinding.Bindable;
import androidx.databinding.BindingAdapter;

import com.squareup.picasso.Picasso;

/**
 * Created by aruba on 2021/9/11.
 */
public class ImageViewBindingAdapter {
    @BindingAdapter("image")
    public static void setImage(ImageView imageView, String url) {
        if (!TextUtils.isEmpty(url)) {
            Picasso.get()
                    .load(url)
                    .placeholder(R.drawable.sijiali)
                    .into(imageView);
        } else {
            imageView.setImageResource(R.drawable.sijiali);
        }
    }

    @BindingAdapter("image")
    public static void setImage(ImageView imageView, int resId) {
        imageView.setImageResource(resId);
    }

    @BindingAdapter(value = {"image", "default"}, requireAll = false)
    public static void setImage(ImageView imageView, String url, int resId) {
        if (!TextUtils.isEmpty(url)) {
            Picasso.get()
                    .load(url)
                    .placeholder(resId)
                    .into(imageView);
        } else {
            imageView.setImageResource(resId);
        }
    }
}

二、双向绑定

DataBinding支持双向绑定,前面我们实现了单向绑定,当变量值发生变化,那么控件上也会更新,双向绑定和单向绑定对比,新增了如果控件中属性的值发生变化,那么变量的值也会发生变化

 
1.BaseObservable

首先定义一个实体类

package com.aruba.databinding2;

/**
 * Created by aruba on 2021/9/11.
 */
public class User {
    public String userName;

    public User(String userName) {
        this.userName = userName;
    }
}

再定义一个ViewModel继承至BaseObservable,并在想要双向绑定的方法上加上@Bindable注解,那么相应的get和set方法就会被自动调用,当值发生变化时,调用set方法,我们还可以通过notifyPropertyChanged方法来通知绑定get方法的控件重新执行get方法

package com.aruba.databinding2;

import android.util.Log;
import android.widget.Toast;

import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;

/**
 * Created by aruba on 2021/9/11.
 */
public class UserViewModel extends BaseObservable {
    private static final String TAG = UserViewModel.class.getSimpleName();
    private User user;

    public UserViewModel() {
        user = new User("张三");
    }

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

    public void setUserName(String userName) {
        if (userName != null && !userName.equals(getUserName())) {
            user.userName = userName;
            notifyPropertyChanged(BR.userName);
            Log.i(TAG, userName);
        }
    }
}

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"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.appcompat.widget.AppCompatEditText
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:minHeight="48dp"
            android:text="@={userViewModel.userName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

        <variable
            name="userViewModel"
            type="com.aruba.databinding2.UserViewModel" />
    </data>
</layout>

最后在Activity中绑定ViewModel

package com.aruba.databinding2;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;

import android.os.Bundle;

import com.aruba.databinding2.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    private UserViewModel userViewModel;
    private ActivityMainBinding activityMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        userViewModel = new UserViewModel();
        activityMainBinding.setUserViewModel(userViewModel);
    }
}

效果:

 

日志打印:
2021-09-11 12:41:07.601 5811-5811/com.aruba.databinding2 I/UserViewModel: 张
2021-09-11 12:41:08.854 5811-5811/com.aruba.databinding2 I/UserViewModel: h
2021-09-11 12:41:09.060 5811-5811/com.aruba.databinding2 I/UserViewModel: hh
2021-09-11 12:41:09.834 5811-5811/com.aruba.databinding2 I/UserViewModel: hhu
2021-09-11 12:41:10.550 5811-5811/com.aruba.databinding2 I/UserViewModel: hhut

2.ObservableField

除了继承BaseObservable外,还可以使用ObservableField对象,重新定义一个ViewModel,ObservableField不需要加注解了,使用方便很多

package com.aruba.databinding2;

import android.util.Log;

import androidx.databinding.ObservableField;

/**
 * Created by aruba on 2021/9/11.
 */
public class UserViewModel2 {
    private static final String TAG = UserViewModel2.class.getSimpleName();
    private ObservableField userObservableField;

    public UserViewModel2() {
        User user = new User("赵四");
        userObservableField = new ObservableField<>(user);
    }

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

    public void setUserName(String userName) {
        Log.i(TAG,userName);
        userObservableField.get().userName = userName;
    }
}

布局中运用变量

<?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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/appCompatEditText"
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:minHeight="48dp"
            android:text="@={userViewModel.userName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.appcompat.widget.AppCompatEditText
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:minHeight="48dp"
            android:text="@={userViewModel2.userName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintHorizontal_bias="0.495"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/appCompatEditText"
            app:layout_constraintVertical_bias="0.133" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

        <variable
            name="userViewModel"
            type="com.aruba.databinding2.UserViewModel" />
        <variable
            name="userViewModel2"
            type="com.aruba.databinding2.UserViewModel2" />
        
    </data>
</layout>

Activity中传入绑定后,效果:

 

日志打印:
2021-09-11 12:50:39.530 6277-6277/com.aruba.databinding2 I/UserViewModel2: 赵
2021-09-11 12:50:40.634 6277-6277/com.aruba.databinding2 I/UserViewModel2: b
2021-09-11 12:50:41.292 6277-6277/com.aruba.databinding2 I/UserViewModel2: bf
2021-09-11 12:50:41.457 6277-6277/com.aruba.databinding2 I/UserViewModel2: bff
2021-09-11 12:50:42.560 6277-6277/com.aruba.databinding2 I/UserViewModel2: bffy

三、RecyclerView的绑定

定义实体类

package com.aruba.databinding3;

/**
 * Created by aruba on 2021/9/11.
 */
public class Idol {
    public String chName;
    public String enName;
    public String image;

    public Idol(String chName, String enName, String image) {
        this.chName = chName;
        this.enName = enName;
        this.image = image;
    }
}

定义item的布局文件

<?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">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingVertical="10dip">

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="100dip"
            android:layout_height="100dip"
            app:image="@{idol.image}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/guideline2"
            app:layout_constraintHorizontal_bias="0.432"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.054"
            tools:srcCompat="@tools:sample/avatars" />

        <TextView
            android:id="@+id/textViewChName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{idol.chName}"
            android:textSize="24sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.138"
            app:layout_constraintStart_toStartOf="@+id/guideline2"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.063"
            tools:text="斯嘉丽.约翰逊" />

        <TextView
            android:id="@+id/textViewEnName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="28dp"
            android:text="@{idol.enName}"
            android:textSize="18sp"
            app:layout_constraintStart_toStartOf="@+id/textViewChName"
            app:layout_constraintTop_toBottomOf="@+id/textViewChName"
            tools:text="Scarlett Johansson" />

        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/guideline2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_percent="0.4" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

        <variable
            name="idol"
            type="com.aruba.databinding3.Idol" />
    </data>
</layout>

定义adapter,转换databinding对象和Activity中稍有不同

package com.aruba.databinding3;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;

import com.aruba.databinding3.databinding.ItemBinding;

import java.util.List;

/**
 * Created by aruba on 2021/9/11.
 */
public class RecyclerViewAdapter extends RecyclerView.Adapter {
    private List datas;

    public RecyclerViewAdapter(List datas) {
        this.datas = datas;
    }

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

        return new MyViewHolder(inflate);
    }

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


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

    static class MyViewHolder extends RecyclerView.ViewHolder {
        private ItemBinding itemBinding;

        public MyViewHolder(ItemBinding itemBinding) {
            super(itemBinding.getRoot());
            this.itemBinding = itemBinding;
        }

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

Activity中配置RecyclerView

package com.aruba.databinding3;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.LinearLayoutManager;

import android.os.Bundle;

import com.aruba.databinding3.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    private RecyclerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        adapter = new RecyclerViewAdapter(IdolUtils.get());
        activityMainBinding.recyclerview.setAdapter(adapter);
        activityMainBinding.recyclerview.setLayoutManager(new LinearLayoutManager(this));
    }
}

效果:

 

四、DataBinding+ViewModel+LiveData组合使用

我们现在将最开始的明星界面完全使用ViewModel代替xml中的变量,并新增了分数,我们点击点赞时,分数+1

首先实体类如下:

package com.aruba.databingviewmodellivedata;

import androidx.lifecycle.MutableLiveData;

/**
 * Created by aruba on 2021/9/11.
 */
public class Idol {
    private MutableLiveData name;
    private MutableLiveData star;
    private MutableLiveData imageUrl;
    private MutableLiveData score;

    public Idol(String name, int star, String imageUrl,int score) {
        this.name = new MutableLiveData<>();
        this.name.setValue(name);
        this.star = new MutableLiveData<>();
        this.star.setValue(star);
        this.imageUrl = new MutableLiveData<>();
        this.imageUrl.setValue(imageUrl);
        this.score = new MutableLiveData<>();
        this.score.setValue(score);
    }

    public MutableLiveData getName() {
        return name;
    }

    public MutableLiveData getStar() {
        return star;
    }

    public MutableLiveData getImageUrl() {
        return imageUrl;
    }

    public void setName(String name) {
        this.name.setValue(name);
    }

    public void setStar(Integer star) {
        this.star.setValue(star);
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl.setValue(imageUrl);
    }

    public MutableLiveData getScore() {
        return score;
    }

    public void setScore(Integer score) {
        this.score.setValue(score);
    }
}

定义ViewModel,注意要实现双向绑定,返回的是LiveData对象

package com.aruba.databingviewmodellivedata;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

/**
 * Created by aruba on 2021/9/11.
 */
public class IdolViewModel extends ViewModel {
    private Idol idol;

    public IdolViewModel() {

    }

    public void setIdolMutableLiveData(Idol idol) {
        if (idol == null) throw new NullPointerException("idol can not be null");

        this.idol = idol;
    }

    public MutableLiveData getIdolName() {
        return idol.getName();
    }

    public MutableLiveData getIdolStar() {
        return idol.getStar();
    }

    public MutableLiveData getIdolImageUrl() {
        return idol.getImageUrl();
    }

    public MutableLiveData getScore() {
        return idol.getScore();
    }

    public void addScore(int add) {
        idol.setScore(idol.getScore().getValue() + add);
    }
}

在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"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            android:onClick="@{()->idolViewModel.addScore(2)}"
            android:text="点赞"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.498"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/include"
            tools:text="appear" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            app:image="@{idolViewModel.getIdolImageUrl()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.497"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.08"
            tools:srcCompat="@tools:sample/avatars" />

        <include
            android:id="@+id/include"
            layout="@layout/include_layout_info"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            app:idolViewModel="@{idolViewModel}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/imageView" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>
        <variable
            name="idolViewModel"
            type="com.aruba.databingviewmodellivedata.IdolViewModel" />
    </data>
</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"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingVertical="10dp">

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="4dp"
            android:text="@{idolViewModel.getIdolName()}"
            android:textSize="30sp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:text="name" />

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:text="@{StarUtils.getStar(idolViewModel.getIdolStar())}"
            android:textSize="24sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2"
            tools:text="Star" />

        <TextView
            android:id="@+id/textView3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp"
            android:text="@{String.valueOf(idolViewModel.getScore())}"
            android:textSize="30sp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView"
            tools:text="score" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <data>

        <variable
            name="idolViewModel"
            type="com.aruba.databingviewmodellivedata.IdolViewModel" />
        
        <import type="com.aruba.databingviewmodellivedata.StarUtils"/>

    </data>
</layout>

Activity中配置

package com.aruba.databingviewmodellivedata;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.ViewModelProvider;

import com.aruba.databingviewmodellivedata.databinding.ActivityMainBinding;


public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding activityMainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        IdolViewModel idolViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(IdolViewModel.class);
        Idol idol = new Idol("斯嘉丽·约翰逊", 4, "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp3.ssl.cdn.btime.com%2Ft01ef53f09683630079.jpg%3Fsize%3D640x1137&refer=http%3A%2F%2Fp3.ssl.cdn.btime.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633920976&t=59dab0f58d59d80e00bf3ef182728a80",10);
        idolViewModel.setIdolMutableLiveData(idol);
        activityMainBinding.setIdolViewModel(idolViewModel);
        activityMainBinding.setLifecycleOwner(this);
    }
}

效果:

 
通过DataBinding+ViewModel+LiveData组合使用,我们再也不需要findbyid了,并且MVVM架构层次清晰,ViewModel中值的改变,会自动更新ui,耦合性降低,大大减少了后期维护成本
Demo地址:https://gitee.com/aruba/my-jetpack-application.git
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/19331,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?