DataBinding
官方文档的解释:数据绑定库是一个支持库,允许您使用声明性格式而不是以编程方式将布局中的UI组件绑定到应用程序中的数据源。其实已经出来很久了,但是身边朋友使用的人不是特别多,今天出一个使用教程:
优点:DataBinding的优点比普通的findViewById之后再设置数据简单了很多,并且数据更新也很简单只要更新绑定的binding,数据更新会自动生效
官方文档
下面介绍如何使用它:
使用前的配置
1:在module级别的build.gradle上添加对DataBinding的支持:
android {
....
dataBinding {
enabled = true
}
}
使用的时候需要在你要绑定的视图的更目录添加data支持,具体如下
<?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="user"
type="com.kirk.model.User" />
<variable
name="isLogin"
type="boolean" />
<import type="android.view.View" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_background"
android:orientation="vertical">
</LinearLayout>
</layout>
代码中的 <layout><data> 是必须要的,添加在布局文件的根节点上 variable 是你在视图绑定中需要用到的类,比如我在这个视图中需要根据数据不同来设置隐藏显示那么就需要添加<import type="android.view.View" />因为View.GONE是android.view.View下面的属性,所以需要引入它,跟在class中的导包(import android.view.View)是一样,除了可以引入普通的数据类型之外,还可以引入系统的类以及使用中自定义的类。
那么如何你的某个控件上设置值呢,别着急下面重头戏来了,如果你要在TextView上设置动态text值,在未使用Databinding之前的做法都是findViewById或者通过注解拿到这个控件,然后给他setText("属性值"),但是在使用了Databinding之后就不用这么麻烦了,你连控件Id都不用添加,代码如下:
<?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="user"
type="com.kirk.model.User" />
<variable
name="isLogin"
type="boolean" />
<import type="android.view.View" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/app_background"
android:orientation="vertical">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@{user.name}"
android:textColor="?attr/colorPrimary"
android:textSize="@dimen/txt_size_Primary" />
</LinearLayout>
</layout>
就是这么简单,你不信?我们稍后再看,其实到这里,已经完成了一大半了,当你在根节点添加
<layout>
<data></data>
<原始布局/>
<layout>后,编译器会为你自动创建一个ViewDataBinding的类,他继承了android.databinding.ViewDataBinding,下面我们来看一下这个类
package com.jisu.sports.databinding;
import com.jisu.sports.R;
import com.jisu.sports.BR;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
@SuppressWarnings("unchecked")
public class FragmentMineBinding extends android.databinding.ViewDataBinding {
@Nullable
private static final android.databinding.ViewDataBinding.IncludedLayouts sIncludes;
@Nullable
private static final android.util.SparseIntArray sViewsWithIds;
static {
sIncludes = null;
sViewsWithIds = new android.util.SparseIntArray();
sViewsWithIds.put(R.id.tv_user_name, 7);
}
// views
@NonNull
public final android.widget.TextView tvUserName;
// variables
@Nullable
private boolean mIsLogin;
@Nullable
private com.kirk.model.User mUser;
// values
// listeners
// Inverse Binding Event Handlers
public FragmentMineBinding(@NonNull android.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
super(bindingComponent, root, 0);
final Object[] bindings = mapBindings(bindingComponent, root, 14, sIncludes, sViewsWithIds);
ensureBindingComponentIsNotNull(com.jisu.sports.common.AppBindingAdapter.class);
this.tvUserName = (android.widget.TextView) bindings[4];
this.tvUserName.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
@Override
public void invalidateAll() {
synchronized(this) {
mDirtyFlags = 0x4L;
}
requestRebind();
}
@Override
public boolean hasPendingBindings() {
synchronized(this) {
if (mDirtyFlags != 0) {
return true;
}
}
return false;
}
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.isLogin == variableId) {
setIsLogin((boolean) variable);
}
else if (BR.user == variableId) {
setUser((com.kirk.model.User) variable);
}
else {
variableSet = false;
}
return variableSet;
}
public void setIsLogin(boolean IsLogin) {
this.mIsLogin = IsLogin;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.isLogin);
super.requestRebind();
}
public boolean getIsLogin() {
return mIsLogin;
}
public void setUser(@Nullable com.kirk.model.User User) {
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x2L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
@Nullable
public com.kirk.model.User getUser() {
return mUser;
}
@Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
}
return false;
}
@Override
protected void executeBindings() {
long dirtyFlags = 0;
synchronized(this) {
dirtyFlags = mDirtyFlags;
mDirtyFlags = 0;
}
int userBalance = 0;
boolean isLogin = mIsLogin;
java.lang.String userName = null;
java.lang.String javaLangStringUserBalance = null;
int isLoginViewVISIBLEViewGONE = 0;
int isLoginViewGONEViewVISIBLE = 0;
java.lang.String javaLangStringLvUserLevel = null;
com.jisu.sports.ui.mine.model.User user = mUser;
java.lang.String userAvatar = null;
int userLevel = 0;
if ((dirtyFlags & 0x5L) != 0) {
if((dirtyFlags & 0x5L) != 0) {
if(isLogin) {
dirtyFlags |= 0x10L;
dirtyFlags |= 0x40L;
}
else {
dirtyFlags |= 0x8L;
dirtyFlags |= 0x20L;
}
}
// read isLogin ? View.VISIBLE : View.GONE
isLoginViewVISIBLEViewGONE = ((isLogin) ? (android.view.View.VISIBLE) : (android.view.View.GONE));
// read isLogin ? View.GONE : View.VISIBLE
isLoginViewGONEViewVISIBLE = ((isLogin) ? (android.view.View.GONE) : (android.view.View.VISIBLE));
}
if ((dirtyFlags & 0x6L) != 0) {
if (user != null) {
// read user.name
userName = user.getName();
}
}
// batch finished
if ((dirtyFlags & 0x6L) != 0) {
// api target 1
this.mBindingComponent.getAppBindingAdapter().image_src(this.ivUserIcon, userAvatar, getDrawableFromResource(ivUserIcon, R.drawable.ico_comment_avatar), (android.graphics.drawable.Drawable)null);
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvLabelLevel, javaLangStringLvUserLevel);
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvLabelMoney, javaLangStringUserBalance);
android.databinding.adapters.TextViewBindingAdapter.setText(this.tvUserName, userName);
}
if ((dirtyFlags & 0x5L) != 0) {
// api target 1
this.rlHasLogged.setVisibility(isLoginViewVISIBLEViewGONE);
this.rlNotLogin.setVisibility(isLoginViewGONEViewVISIBLE);
}
}
// Listener Stub Implementations
// callback impls
// dirty flag
private long mDirtyFlags = 0xffffffffffffffffL;
@NonNull
public static FragmentMineBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup root, boolean attachToRoot) {
return inflate(inflater, root, attachToRoot, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static FragmentMineBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.view.ViewGroup root, boolean attachToRoot, @Nullable android.databinding.DataBindingComponent bindingComponent) {
return android.databinding.DataBindingUtil.<FragmentMineBinding>inflate(inflater, com.jisu.sports.R.layout.fragment_mine, root, attachToRoot, bindingComponent);
}
@NonNull
public static FragmentMineBinding inflate(@NonNull android.view.LayoutInflater inflater) {
return inflate(inflater, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static FragmentMineBinding inflate(@NonNull android.view.LayoutInflater inflater, @Nullable android.databinding.DataBindingComponent bindingComponent) {
return bind(inflater.inflate(com.jisu.sports.R.layout.fragment_mine, null, false), bindingComponent);
}
@NonNull
public static FragmentMineBinding bind(@NonNull android.view.View view) {
return bind(view, android.databinding.DataBindingUtil.getDefaultComponent());
}
@NonNull
public static FragmentMineBinding bind(@NonNull android.view.View view, @Nullable android.databinding.DataBindingComponent bindingComponent) {
if (!"layout/fragment_mine_0".equals(view.getTag())) {
throw new RuntimeException("view tag isn't correct on view:" + view.getTag());
}
return new FragmentMineBinding(bindingComponent, view);
}
/* flag mapping
flag 0 (0x1L): isLogin
flag 1 (0x2L): user
flag 2 (0x3L): null
flag 3 (0x4L): isLogin ? View.VISIBLE : View.GONE
flag 4 (0x5L): isLogin ? View.VISIBLE : View.GONE
flag 5 (0x6L): isLogin ? View.GONE : View.VISIBLE
flag 6 (0x7L): isLogin ? View.GONE : View.VISIBLE
flag mapping end*/
//end
}
代码中我们可以看到, 这个自动生成的类已经把我们要设置的值,和控件全部准备好了,帮扩赋值一系列工作也做好了,万事俱备,只欠东风,come baby
此时只要把数据设置给dinding就大功告成了
MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity); User user = new User("张三", "18");
binding.setUser(user);
没错,生成的DataBinding类是根据你的布局文件的命名来的,后面追加了Binding,如果你打出了布局文件的名字但是没有提示出来MainActivityBinding,这时候你需要build一下工程。完成以上步骤就算是完整的databinding使用了。
对,我引入的View,你是不是想问难道只能设置text的时候才能用databinding吗,那也太单一了,NONONO,控件的隐藏显示,图片,都可以使用,比如:
android:visibility="@{isLogin?View.VISIBLE:View.GONE,default=visible}"
再比如背景色:
android:background="@{user.age>18@color/red_deepen:@color/reds,default=@color/reds}"
它还可以进行一些普通的逻辑运算
要注意的坑:&& 和 <
有些情况下我们需要双重判断也就是&&正常情况下我们这样写觉得没有问题,但是&&始终会报错,强制编译运行就出问题像下面这种错误
那是因为&&,< 在databinding中做逻辑判断使用时 &&需要转译为:&& < 需要转译为:< 修改后:
android:text="@{coin>=30&&coin < 60?`未完成`:``}"
databinding还有很多强大的用处,后面学习了再贴出,先到这里。有什么意见或者建议欢迎在留言区讨论