非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时。结合ViewModel和LiveData使用时 布局中不再使用 实体类 而是 ViewModel。
<variable
name="userViewModel"
type="com.example.rxjava20.databinding.list.UserViewModel" />
name:和单独使用时一致,就是一个变量。
type:自定义ViewModel的全路径。
<2> 相比单独使用DataBinding时。结合ViewModel和LiveData使用时 布局中不再使用 实体类.属性 而是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具体详解