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還有很多強大的用處,後面學習了再貼出,先到這裏。有什麼意見或者建議歡迎在留言區討論