资料来源
Android MVVM 系列之 Databinding(一)
曾经有一个梦想
比如一个EditText和一个String变量 name
我希望让EditText自己显示name,当EditText的内容变化时,同步更新到name变量中
现在databinding能实现这个基本功能_双向绑定,这个是Android Studio 2.1 Preview 3之后支持的功能
确保gradle版本不低于2.1.0-alpha3
在gradle的android配置中添加打开databinding功能
android{
...
dataBinding {
enabled = true
}
...
}
改写xml文件,用layout节点包裹布局,在layout和布局中添加data节点
这个功能可以添加databinding support插件,支持把现有的布局文件转换成layout包裹的布局
<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"
>
<data>
<variable
name="user"
type="com.example.cleanarchitecturetest.User"/><!--包名路径中不要有大写字母,容易报告找不到类名(因为实际上是包名)-->
</data>
<android.support.constraint.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/tv_store_a"
android:layout_width="80dp"
android:layout_height="40dp"
android:text='@={user.name}'
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
<EditText
android:id="@+id/tv_store_b"
android:layout_width="80dp"
android:layout_height="40dp"
android:text='@={user.age}'
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.9"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
<Button
android:id="@+id/b_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
</layout>
User就是一个很简单的数据类,只有自己的成员变量和对应的set/get方法
public class User extends BaseObservable {
private String name;
private String age;
public User(String name, String age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
然后,在活动中,添加绑定,改动onCreate方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding viewBinding=DataBindingUtil.setContentView(this,R.layout.activity_main);
user=new User("Luohao","18");
viewBinding.setUser(user);
intiView();
}
private void intiView() {
tvStoreA=findViewById(R.id.tv_store_a);
tvStoreB=findViewById(R.id.tv_store_b);
bStart=findViewById(R.id.b_show);
bShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name=user.getName();
String age=String.valueOf(user.getAge());
Toast.makeText(Main2TestActivity.this,"name="+name+"\nage="+age,Toast.LENGTH_SHORT)
.show();
}
});
}
这个时候editText中的内容与User.name就互相绑定了
活动初始化时,显示的是User.name和age的值;EditText变化后,点击show按钮,toast会显示User.变化后的属性值,说明双向绑定成功了
注意点:
1,包名路径不能有大写字母,编译器会误认为类名,报错类名找不到
2,编译报错
Cannot find the getter for attribute 'android:text' with value type
这个是因为user的age属性无法正确识别导致,需要重写setText()方法
在user类中添加如下代码(来自overflow)
@BindingAdapter("android:text")
public static void setText(TextView view, int value) {
view.setText(Integer.toString(value));
}
@InverseBindingAdapter(attribute = "android:text")
public static int getText(TextView view) {
// 这里是对String变空时,无法正确赋值给age,导致页面崩溃做的优化
// 除此之外,也需要对editText限定输入值范围
String value=view.getText().toString();
if (value.equals("")){
value="0";
}
return Integer.parseInt(value);
这样可以做到数据的双向绑定,但是如何应用到mvvm架构里呢
mvvm架构里,UI层与ViewModel层绑定交换数据,而不再是简单的数据类
了解ViewModel参考这里 ViewModels : A Simple Example
这里涉及到在xml文件中引用LiveData的成员变量来设置Text属性
由于暂时只支持get/set方法获取属性,所以建议成员变量设置为Sting/integer等基础类型,而不是类变量
下面时代码
UserModel类,继承ViewModel
public class UserModel extends ViewModel {
private MutableLiveData<String> name;
private MutableLiveData<Integer> age;
public UserModel() {
name=new MutableLiveData<>();
name.setValue("wwq");
age=new MutableLiveData<>();
age.setValue(28);
}
public MutableLiveData<String> getName() {
return name;
}
public void setName(MutableLiveData<String> name) {
this.name = name;
}
public MutableLiveData<Integer> getAge() {
return age;
}
public void setAge(MutableLiveData<Integer> age) {
this.age = age;
}
}
布局文件,引用userModel类
<?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">
<data>
<variable
name="userModel"
type="com.zy.jetpacktest.viewmodel.UserModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UI.Main2Activity">
<EditText
android:id="@+id/tv_store_a"
android:layout_width="80dp"
android:layout_height="40dp"
android:text='@={userModel.name}'
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
<EditText
android:id="@+id/tv_store_b"
android:layout_width="80dp"
android:layout_height="40dp"
android:text='@={userModel.age}'
android:digits="0123456789"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.9"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.3" />
<Button
android:id="@+id/b_add_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="A_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
<Button
android:id="@+id/b_add_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="B_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5" />
<Button
android:id="@+id/b_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Main2Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMain2Binding viewBinding= DataBindingUtil.setContentView(this,R.layout.activity_main2);
intiView();
mUserModel = ViewModelProviders.of(this).get(UserModel.class);
viewBinding.setUserModel(mUserModel);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.b_add_a:
tvStoreA.setText("测试");
break;
case R.id.b_add_b:
tvStoreB.setText("12345");
break;
case R.id.b_show:
String name= mUserModel.getName().getValue();
String age= mUserModel.getAge().getValue().toString();
Toast.makeText(Main2Activity.this,"name="+name+"\nage="+age,Toast.LENGTH_SHORT)
.show();
break;
}
}
private void intiView() {
tvStoreA=findViewById(R.id.tv_store_a);
tvStoreB=findViewById(R.id.tv_store_b);
bAddA=findViewById(R.id.b_add_a);
bAddB=findViewById(R.id.b_add_b);
bShow =findViewById(R.id.b_show);
bAddA.setOnClickListener(this);
bAddB.setOnClickListener(this);
bShow.setOnClickListener(this);
}