数据绑定库是一种支持库,借助该库,可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。
在Module的build.gradle android模块中添加如下配置
android {
dataBinding {
enabled = true
}
}
同步时出现Cause: unable to find valid certification path to requested target错误的解决办法:在项目的build.gradle的buildscript的repositories下加入:
maven { url "http://jcenter.bintray.com"}
再在allprojects的repositories下加入
mavenCentral()
jcenter{ url "http://jcenter.bintray.com/" }
maven { url "https://jitpack.io" }
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> //根节点变为layout,并且它不能指定长和宽等属性,只能声明命名空间,否则在构建时会报错
<data> //data节点作用是连接 View 和 Modle 的桥梁
<variable
name="viewmodel"
type="com.myapp.data.ViewModel" /> //bean实体类
</data>
<LinearLayout //UI布局的根布局
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="@{viewmodel.name}" />
<!--注意:这里age是int类型,必须转化为String,否则会运行时异常-->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(viewmodel.age)}" />
</LinearLayout>
</layout>
可以在表达式语言中使用字符串连接运算符(即在@{…}中使用),逻辑和算术运算符等常用的运算符和关键字
上面的绑定为单向绑定(当ViewModel中的数据发生改变时,View相应的自动改变),而双向数据绑定多的一向是在用户行为对View发生影响时,ViewModel接收到View的改变并对其作出反应。
示例:
<CheckBox
android:id="@+id/rememberMeCheckBox"
android:checked="@={viewmodel.rememberMe}"
/>
**注意:**双向数据绑定(@={})对于单向(@{})的区别是多了一个 =
为了应对数据的更改,可以将布局变量设置为 Observable(通常为 BaseObservable) 的实现,并使用 @Bindable 注释get方法,如以下代码段所示:
public class LoginViewModel extends BaseObservable {
// private Model data = ...
@Bindable
public Boolean getRememberMe() { //VM->View VM中数据的改变引起View的改变
return data.rememberMe;
}
public void setRememberMe(Boolean value) { //View->VM View的改变引起VM中数据的改变
// Avoids infinite loops.
if (data.rememberMe != value) {
data.rememberMe = value;
// React to the change.
saveData();
// Notify observers of a new value.
notifyPropertyChanged(BR.remember_me);
}
}
}
系统会为每个布局文件生成一个绑定类。默认情况下,类名称基于布局文件的名称,它会转换为 Pascal 大小写形式并在末尾添加 Binding 后缀。以上布局文件名为 activity_main.xml
,因此生成的对应类为 ActivityMainBinding
。此类包含从布局属性(例如,user
变量)到布局视图的所有绑定,并且知道如何为绑定表达式指定值。建议的绑定创建方法是在扩充布局时创建,如以下示例所示:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
User user = new User("Test", "User");
binding.setUser(user);
}
通过数据绑定,可以编写从视图分派的表达式处理事件(例如,onClick()
方法)。事件特性名称由监听器方法的名称确定,但有一些例外情况。例如,View.OnClickListener
有一个 onClick()
方法,所以该事件的特性为 android:onClick
。
可以使用以下机制处理事件:
方法引用:在表达式中,您可以引用符合监听器方法名称的方法(即和它名称相同的方法)。当表达式求值结果为方法引用时,数据绑定会将方法引用和所有者对象封装到监听器中,并在目标视图上设置该监听器。如果表达式的求值结果为 ,则数据绑定不会创建监听器,而是设置
监听器。
例如在上面布局和绑定表达式的代码中的TextView中加入 android:onClick="@{() -> viewmodel.onLike()}" 属性
监听器绑定:这些是在事件发生时进行求值的 lambda 表达式。数据绑定始终会创建一个要在视图上设置的监听器。事件被分派后,监听器会对 lambda 表达式进行求值。使用类似与方法引用,也可在 lambda 表达式中使用多个参数。
ViewModel是一个抽象类,旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续存在。在使用前需要声明相应的依赖。架构组件为界面控制器提供了ViewModel辅助程序类,该类负责为界面准备数据。 在配置更改期间会自动保留ViewModel对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。
在Model - build.gradle添加依赖
implementation "android.arch.lifecycle:viewmodel:1.1.1"
public class MyViewModel extends ViewModel { //继承ViewModel抽象类
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<User>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// Do an asynchronous operation to fetch users. 异步操作获取users
}
}
创建ViewModel对象后,就可以在活动中使用了
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
//当系统首次调用活动的onCreate方法时创建一个ViewModel对象
//重新创建了该 Activity,它接收的 MyViewModel 实例与第一个 Activity 创建的实例相同
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class); //通过这种方法获取ViewModel对象
model.getUsers().observe(this, users -> {
//更新UI
});
}
}
LiveData是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
LiveData 是一种可用于任何数据的封装容器,其中包括可实现 Collections
的对象,如 List
。LiveData对象通常存储在ViewModel对象中,并可通过 getter 方法进行访问,如以下示例中所示:
public class NameViewModel extends ViewModel {//LiveData对象存储在ViewModel类中
// Create a LiveData with a String
private MutableLiveData<String> currentName; //因为LiveData是一个抽象类,所以我们使用它的子类MutableLiveData
public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}
// Rest of the ViewModel...
}
注意:确保将用于更新界面的LiveData对象存储在ViewModel对象中,而不是将其存储在 Activity 或 Fragment 中,原因如下:
应在活动的onCreate方法中观察LiveData,以确保 Activity 或 Fragment 变为活跃状态后具有可以立即显示的数据
public class NameActivity extends AppCompatActivity {
private NameViewModel model;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other code to setup the activity...
//获取ViewModel对象
model = ViewModelProviders.of(this).get(NameViewModel.class);
//下面创建更新UI的观察者对象
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};
//观察LiveData,传入作为LifecycleOwner的活动和observer.
model.getCurrentName().observe(this, nameObserver);
}
}
LiveData 没有公开可用的方法来更新存储的数据。MutableLiveData类将公开 setValue(T) 和 postValue(T) 方法,如果需要修改存储在LiveData对象中的值,则必须使用这些方法。通常情况下会在 ViewModel中使用 MutableLiveData,然后 ViewModel 只会向观察者公开不可变的 LiveData对象。
注意:必须调用 setValue(T) 方法以从主线程更新 LiveData 对象。如果在 worker 线程中执行代码,则可以改用 postValue(T) 方法来更新 LiveData 对象,即两个方法都可以用。
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
model.getCurrentName().setValue(anotherName);
}
});
在本例中调用 setValue(T) 导致观察者使用值 John Doe 调用其 onChanged() 方法。在所有情况下,调用 setValue() 或 postValue() 都会触发观察者并更新界面。
Android JetPack