ViewModel+LiveData+DataBinding实现MVVM

本文深入解析非Androidx项目中DataBinding的基础使用,包括Gradle配置、代码示例及自定义BindingAdapter,并结合ViewModel与LiveData实现MVVM模式,演示数据绑定与实时更新的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非Androidx项目DataBinding 基础讲解

非Androidx项目DataBinding 基础讲解_jianning-wu的博客-优快云博客

非Androidx项目DataBinding ListView使用

非Androidx项目DataBinding ListView使用_不使用androidx怎么用 databinding 兼容性问题_jianning-wu的博客-优快云博客

非Androidx项目DataBinding RecyclerView使用

非Androidx项目DataBinding RecyclerView使用_jianning-wu的博客-优快云博客

本章节讲述Androidx项目中使用DataBinding。

一.DataBinding讲解

1.Gradle依赖

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"

    defaultConfig {
        applicationId "com.example.rxjava20"
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    buildFeatures {
        dataBinding true
    }
}

buildFeatures {
    dataBinding true
}

非Androidx项目

dataBinding {
    enabled = true
}

2.代码讲解

<1> 基本使用

布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="login"
            type="com.example.rxjava20.databinding.login.LoginBean" />
    </data>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:text="@{login.name}"
            android:textColor="#FFFFFF">

        </TextView>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:text="@{login.pwd}"
            android:textColor="#FFFFFF">

        </TextView>

    </LinearLayout>

</layout>

实体

package com.example.rxjava20.databinding.login;

import java.io.Serializable;

public class LoginBean implements Serializable {

    public String name;
    public String pwd;

    public String getName() {
        return name;
    }

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

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

Activity调用

package com.example.rxjava20.databinding.login;

import android.os.Bundle;

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

import com.example.rxjava20.R;
import com.example.rxjava20.databinding.ActivityLoginBinding;

public class LoginActivity extends AppCompatActivity {

    private ActivityLoginBinding mActivityLoginBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        mActivityLoginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);

        initDataBindingView();
    }

    /**
     * 初始化
     */

    private void initDataBindingView() {
        //实体类
        LoginBean loginBean = new LoginBean();
        loginBean.setName("张三");
        loginBean.setPwd("123456");
        //绑定数据
        mActivityLoginBinding.setLogin(loginBean);
    }

}

说明

(1) 布局中的login为空时 不会空指针。测试

 mActivityLoginBinding.setLogin(null);

TextView不显示文字但没有闪退。 因为

DataBinding会自动处理:在表达式 @{login.name}表达式@{login.pwd} 中,如果 login为 Null,则为 login.name和login.pwd 分配默认值 null表现上就是上面的TextView什么都不显示。但是没有空指针崩溃

(2) 无须findViewById。只需在使用DataBinding的Activity中

 mActivityLoginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);

<2> 多个点击事件

非Androidx项目已经讲过DataBinding的点击事件。只不过是单个的点击事件。这里讲解一个布局中将所有的点击事件合在一个方法中。

布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="login"
            type="com.example.rxjava20.databinding.login.LoginBean" />

        <variable
            name="loginActivityClickMethod"
            type="com.example.rxjava20.databinding.login.LoginActivity.ActivityClick" />
    </data>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{loginActivityClickMethod::nameClick}"
            android:text="@{login.name}"
            android:textColor="#FFFFFF">

        </TextView>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{loginActivityClickMethod::pwdClick}"
            android:text="@{login.pwd}"
            android:textColor="#FFFFFF">

        </TextView>

    </LinearLayout>

</layout>

Activity调用

package com.example.rxjava20.databinding.login;

import android.os.Bundle;
import android.util.Log;
import android.view.View;

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

import com.example.rxjava20.R;
import com.example.rxjava20.databinding.ActivityLoginBinding;

public class LoginActivity extends AppCompatActivity {

    private ActivityLoginBinding mActivityLoginBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        mActivityLoginBinding = DataBindingUtil.setContentView(this, R.layout.activity_login);

        initDataBindingView();
    }

    /**
     * 初始化
     */

    private void initDataBindingView() {
        //实体类
        LoginBean loginBean = new LoginBean();
        loginBean.setName("张三");
        loginBean.setPwd("123456");
        //绑定数据
        mActivityLoginBinding.setLogin(loginBean);
        //设置点击事件
        mActivityLoginBinding.setLoginActivityClickMethod(new ActivityClick());
    }

    /**
     * 该Activity中所有的点击事件
     */

    public class ActivityClick {
        //姓名点击
        public void nameClick(View view) {
            Log.d("LoginActivity", "姓名点击----:" + mActivityLoginBinding.getLogin().getName());
        }

        //密码点击
        public void pwdClick(View view) {
            Log.d("LoginActivity", "密码点击----:" + mActivityLoginBinding.getLogin().getPwd());
        }
    }

}

结果

点击姓名

D/LoginActivity: 姓名点击----:张三

点击密码

D/LoginActivity: 密码点击----:123456

说明

(1) 布局中

 <variable
     name="loginActivityClickMethod"
     type="com.example.rxjava20.databinding.login.LoginActivity.ActivityClick" />

name:值 随意 但是这个值有两个地方用。

type:值 Activity中点击事件内部类全路径。

android:onClick="@{loginActivityClickMethod::nameClick}"


android:onClick="@{loginActivityClickMethod::pwdClick}"

表达式中:loginActivityClickMethod:即上述name属性对应的值。用处2之1。

表达式中:

nameClick:Activity中点击事件内部类 姓名点击方法。

pwdClick:Activity中点击事件内部类 密码点击方法。

(2) Activity中

//设置点击事件
mActivityLoginBinding.setLoginActivityClickMethod(new ActivityClick());

设置点击事件: setXXX:XXX取值就是上述布局中的name属性对应的值loginActivityClickMethod用处2之2。

参数:new ActivityClick()就是点击事件内部类。

内部类中的点击时机方法

/**
 * 该Activity中所有的点击事件
 */

public class ActivityClick {
    //姓名点击
    public void nameClick(View view) {
        Log.d("LoginActivity", "姓名点击----:" + mActivityLoginBinding.getLogin().getName());
    }

    //密码点击
    public void pwdClick(View view) {
        Log.d("LoginActivity", "密码点击----:" + mActivityLoginBinding.getLogin().getPwd());
    }
}

nameClick(View view)和pwdClick(View view)分表对应表达式中 ::后面的nameClick和pwdClick。

<3> 自定义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">

    <data>

        <variable
            name="login"
            type="com.example.rxjava20.databinding.login.LoginBean" />

        <variable
            name="loginActivityClickMethod"
            type="com.example.rxjava20.databinding.login.LoginActivity.ActivityClick" />
    </data>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{loginActivityClickMethod::nameClick}"
            android:text="@{login.name}"
            android:textColor="#FFFFFF">

        </TextView>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{loginActivityClickMethod::pwdClick}"
            android:text="@{login.pwd}"
            android:textColor="#FFFFFF">

        </TextView>

        <!--imageUrl:设置图片网络路径  placeHolder设置占位符 -->
        <ImageView
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_marginTop="20dp"
            app:imageUrl="@{login.avatar}"
            app:placeHolder="@{@drawable/ic_launcher}">

        </ImageView>

    </LinearLayout>

</layout>

工具类

package com.example.rxjava20.databinding.login;

import android.graphics.drawable.Drawable;
import android.util.Log;
import android.widget.ImageView;

import androidx.databinding.BindingAdapter;

import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;

public class ImageViewUtils {

    /**
     * 类名:随意
     * 方法名:随意 但必须是static的
     * 使用@BindingAdapter {"app:imageUrl", "app:placeHolder"}中 app:imageUrl对应布局中的app:imageUrl app:placeHolder对应布局中的app:placeHolder
     * 参数1:ImageView对象  imageView:随意
     * 参数2:图片路径 imageUri:随意
     * 参数3:占位符 placeHolder:随意
     * 参数2和参数3的顺序和@BindingAdapter({"app:imageUrl", "app:placeHolder"})一致。
     */

    @BindingAdapter({"app:imageUrl", "app:placeHolder"})
    public static void loadImageView(ImageView imageView, String imageUri, Drawable placeHolder) {
        RequestOptions options = new RequestOptions().placeholder(placeHolder).error(placeHolder);
        Glide.with(imageView.getContext())
                .load(imageUri)
                .apply(options)
                .into(imageView);
        Log.d("LoginActivity", "图片路径----:" + imageUri);
    }

}

二.ViewModel+LiveData+DataBinding实现MVVM

还记得讲过DataBinding 数据更新时 更新xml中的数据 需要在实体类中各种操作,比如

public class People extends BaseObservable {

    private int fieldId = 10;

    private String name;
    private String age;

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(fieldId);
    }

    @Bindable
    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
        notifyPropertyChanged(fieldId);
    }
}

如果有多个实体类,这样操作回比较麻烦。那么,结合LiveData后就不需要每个实体类都这样操作了。下面通过代码讲解

Model层

UserListModel类

package com.example.rxjava20.databinding.list;

public class UserListModel {

    /**
     * 模拟获取网络数据
     */

    public void getUserList(UserViewModel.ServiceCallback callback) {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        UserBean userBean = new UserBean();
        userBean.setName("修改后的姓名");
        userBean.setPwd("修改后的密码");

        callback.getResult(userBean);
    }

}

View层

Activity代码

package com.example.rxjava20.databinding.list;

import android.os.Bundle;
import android.util.Log;
import android.view.View;

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

import com.example.rxjava20.R;
import com.example.rxjava20.databinding.ActivityUserlistBinding;

public class UserListActivity extends AppCompatActivity {

    private ActivityUserlistBinding mActivityUserlistBinding;
    private UserViewModel mUserViewModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivityUserlistBinding = DataBindingUtil.setContentView(this, R.layout.activity_userlist);

        //点击事件
        mActivityUserlistBinding.setUserListActivityClickMethod(new ActivityClick());

        //DataBinding绑定生命周期
        mActivityUserlistBinding.setLifecycleOwner(this);

        //获取ViewModel
        ViewModelProvider provider = new ViewModelProvider(this, new ViewModelFactory());
        mUserViewModel = provider.get(UserViewModel.class);
        mActivityUserlistBinding.setUserViewModel(mUserViewModel);
    }

    /**
     * 该Activity中所有的点击事件
     */

    public class ActivityClick {
        //姓名点击
        public void nameClick(View view) {
            mUserViewModel.getUserList();
            Log.d("UserListActivity", "姓名点击 更新用户信息");
        }

        //密码点击
        public void pwdClick(View view) {
            Log.d("UserListActivity", "密码点击 更新用户信息");
        }
    }
}

布局xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="userViewModel"
            type="com.example.rxjava20.databinding.list.UserViewModel" />

        <variable
            name="userListActivityClickMethod"
            type="com.example.rxjava20.databinding.list.UserListActivity.ActivityClick" />
    </data>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{userListActivityClickMethod::nameClick}"
            android:text="@{userViewModel.userBeanMutableLiveData.name}"
            android:textColor="#FFFFFF">

        </TextView>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:layout_margin="10dp"
            android:background="@color/colorPrimary"
            android:gravity="center"
            android:onClick="@{userListActivityClickMethod::pwdClick}"
            android:text="@{userViewModel.userBeanMutableLiveData.pwd}"
            android:textColor="#FFFFFF">

        </TextView>


    </LinearLayout>

</layout>

VM层

ViewModel实现类

package com.example.rxjava20.databinding.list;

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

public class UserViewModel extends ViewModel {

    private MutableLiveData<UserBean> mUserBeanMutableLiveData;

    public UserViewModel() {
        mUserBeanMutableLiveData = new MutableLiveData<>();
    }

    /**
     * 获取网络数据
     */

    public void getUserList() {
        new UserListModel().getUserList(new ServiceCallback() {
            @Override
            public void getResult(UserBean userBean) {
                mUserBeanMutableLiveData.setValue(userBean);
            }
        });
    }

    /**
     * 获取LiveData
     */

    public MutableLiveData<UserBean> getUserBeanMutableLiveData() {
        return mUserBeanMutableLiveData;
    }

    /**
     * 回调
     */

    public interface ServiceCallback {
        void getResult(UserBean userBean);
    }

}

说明

<1> 相比单独使用DataBinding时。结合ViewModelLiveData使用时 布局中不再使用 实体类 而是 ViewModel

 <variable
    name="userViewModel"
    type="com.example.rxjava20.databinding.list.UserViewModel" />

name:和单独使用时一致,就是一个变量。

type:自定义ViewModel的全路径。

<2> 相比单独使用DataBinding时。结合ViewModelLiveData使用时 布局中不再使用 实体类.属性 而是ViewModel.LiveData拿到实体类然后.属性

android:text="@{userViewModel.userBeanMutableLiveData.name}"

 
android:text="@{userViewModel.userBeanMutableLiveData.pwd}"

<3> View层需要 DataBinding绑定生命周期

//DataBinding绑定生命周期
mActivityUserlistBinding.setLifecycleOwner(this);

<4> View层需要获取ViewModel实现类 然后 设置到布局中。

//获取ViewModel实现类
ViewModelProvider provider = new ViewModelProvider(this, new ViewModelFactory());
mUserViewModel = provider.get(UserViewModel.class);

//将获取到的ViewModel实现类设置到布局中
mActivityUserlistBinding.setUserViewModel(mUserViewModel);

<5> 适当的时机更新数据 其实更新的是LiveData的数据。

/**
 * 该Activity中所有的点击事件
 */

public class ActivityClick {
    //姓名点击
    public void nameClick(View view) {
        mUserViewModel.getUserList();
        Log.d("UserListActivity", "姓名点击 更新用户信息");
    }

    //密码点击
    public void pwdClick(View view) {
        Log.d("UserListActivity", "密码点击 更新用户信息");
    }
}

<6> 结合DataBinding后不需要 再获取ViewModel实现类的LiveData 观察LiveData的变化。

ViewModel+LiveData实现MVVM具体详解 

ViewModel+LiveData实现MVVM_jianning-wu的博客-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值