1.简介
官方文档:
https://developer.android.com/topic/libraries/data-binding
官方示例:
https://github.com/android/databinding-samples
作用:
把layout 与 数据对象关联,将layout上的组件与对象的成员绑定.让layout上的组件的值来自数据对象。如:
<TextView android:text="@{data.value}" />
2.简单用法:绑定Activity与数据源
2.1 打开绑定选项
只能在主module的 build.gradle 中打开,即使主module代码没有使用数据绑定,而他依赖的module使用了数据绑定,也要在主module中打开。
1 buildFeatures{ 2 dataBinding true 3 // for view binding : 4 // viewBinding true 5 }
2.2 布局 activity_main.xml
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:android="http://schemas.android.com/apk/res/android"> 3 <data> 4 <variable name="data" type="com.example.databind.Data" /> <!--声明的对象data--> 5 <variable name="click" type="com.example.databind.Click" /> 6 </data> 7 <androidx.constraintlayout.widget.ConstraintLayout > 8 9 <TextView android:text="@{data.value, default = value}" /> <!--值来自数据对象data--> 10 <Button android:onClick="@{() -> click.onStartClick(data)}" /> 11 12 </androidx.constraintlayout.widget.ConstraintLayout> 13 </layout>
其中
- 跟元素<layout> 包含 元素 <data>和 跟布局<ConstraintLayout >
- <data>元素用来定义layout使用的数据源,可以使用稍微复杂语法,如<import ...>,见第4、5行。
- layout上的组件使用@{数据源名.成员}指定值,@{}内可以使用简单的的绑定表达式,如第10行。
2.3 数据对象 Data.java
1 package com.example.databind; 2 3 public class Data { 4 public String key; 5 public int value; 6 7 public Data(String key, int value){ 8 this.key = key; 9 this.value = value; 10 } 11 12 @Override 13 public String toString() { 14 return "key = " + key + " value = " + value; 15 } 16 }
2.4 建立绑定关系
1 package com.example.databind; 2 import android.os.Bundle; 3 import androidx.appcompat.app.AppCompatActivity; 4 import androidx.databinding.DataBindingUtil; 5 6 import com.example.databind.databinding.ActivityMainBinding; 7 8 public class MainActivity extends AppCompatActivity { 9 10 private ActivityMainBinding binding; 11 private Data data; 12 13 private void init(){ 14 data = new Data("time", 10); 15 binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main); 16 binding.setData(data); 17 binding.setClick(new Click(binding)); 18 } 19 20 @Override 21 protected void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 setContentView(R.layout.activity_main); 24 init(); 25 } 26 }
关于其中的 ActivityMainBinding :
- 第15行得到了把一个activity和一个布局关联,得到一个绑定对象 ActivityMainBinding,它是绑定框架生成的,见5。
- 写完layout后 Rebuild Project 一下 就可以生成它。
3. 绑定Fragment与数据源
常用的方法:
- DataBindingUtil.inflate(inflater, R.layout.daily_data, container, false)
- DataBindingUtil.bind(root) ,返回可能为空的绑定对象。
1 lateinit var binding : DailyDataBinding 2 3 override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle? 4 ): View? { 5 binding = DataBindingUtil.inflate(inflater, R.layout.daily_data, container, false) 6 7 binding.dailyShare.setOnClickListener(this::onShareClicked) 8 //... 9 return binding.root 10 }
或者
1 var binding : DailyBinding? = null 2 fun initBinding(root : View){ 3 binding = DataBindingUtil.bind(root) 4 binding?.page = page 5 binding?.start = startTime 6 //... 7 }
3.1 布局文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 <data > 5 <variable name="data" type="com.example.databind.Data" /> 6 <variable name="click" type="com.example.databind.Click" /> 7 </data> 8 9 <androidx.constraintlayout.widget.ConstraintLayout 10 android:layout_width="match_parent" 11 android:layout_height="match_parent" 12 android:clickable="true" 13 > 14 <TextView 15 android:id="@+id/title" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_marginTop="16dp" 19 android:text="Hello Data Binding !" 20 app:layout_constraintLeft_toLeftOf="parent" 21 app:layout_constraintRight_toRightOf="parent" 22 app:layout_constraintTop_toTopOf="parent" /> 23 24 <TextView 25 android:id="@+id/key" 26 android:layout_width="wrap_content" 27 android:layout_height="wrap_content" 28 android:layout_marginTop="16dp" 29 android:text="@{data.key, default = key}" 30 app:layout_constraintStart_toStartOf="parent" 31 app:layout_constraintEnd_toStartOf="@+id/value" 32 app:layout_constraintTop_toBottomOf="@+id/title" 33 app:layout_constraintHorizontal_chainStyle="packed" 34 /> 35 36 <TextView 37 android:id="@+id/value" 38 android:layout_width="wrap_content" 39 android:layout_height="wrap_content" 40 android:layout_marginStart="16dp" 41 android:layout_marginLeft="16dp" 42 android:text="@{String.valueOf(data.value),default = value}" 43 app:layout_constraintEnd_toEndOf="parent" 44 app:layout_constraintStart_toEndOf="@+id/key" 45 app:layout_constraintTop_toTopOf="@+id/key" /> 46 47 <Button 48 android:id="@+id/start" 49 android:layout_width="wrap_content" 50 android:layout_height="wrap_content" 51 android:layout_marginTop="16dp" 52 android:onClick="@{() -> click.onStartClick(data)}" 53 android:text="start" 54 android:textAllCaps="false" 55 app:layout_constraintEnd_toStartOf="@+id/stop" 56 app:layout_constraintHorizontal_chainStyle="packed" 57 app:layout_constraintStart_toStartOf="parent" 58 app:layout_constraintTop_toBottomOf="@+id/key" /> 59 60 <Button 61 android:id="@+id/stop" 62 android:layout_width="wrap_content" 63 android:layout_height="wrap_content" 64 android:layout_marginStart="16dp" 65 android:layout_marginLeft="16dp" 66 android:onClick="@{() -> click.onStopClicked(data)}" 67 android:text="stop" 68 android:textAllCaps="false" 69 app:layout_constraintBottom_toBottomOf="@+id/start" 70 app:layout_constraintEnd_toStartOf="@+id/reset" 71 app:layout_constraintStart_toEndOf="@+id/start" 72 app:layout_constraintTop_toTopOf="@+id/start" /> 73 74 <Button 75 android:id="@+id/reset" 76 android:layout_width="wrap_content" 77 android:layout_height="wrap_content" 78 android:layout_marginStart="16dp" 79 android:layout_marginLeft="16dp" 80 android:text="reset" 81 android:textAllCaps="false" 82 app:layout_constraintBottom_toBottomOf="@+id/start" 83 app:layout_constraintEnd_toEndOf="parent" 84 app:layout_constraintStart_toEndOf="@+id/stop" 85 app:layout_constraintTop_toTopOf="@+id/start" 86 app:layout_constraintVertical_bias="1.0" /> 87 88 <Button 89 android:id="@+id/list_fragment" 90 android:layout_width="wrap_content" 91 android:layout_height="wrap_content" 92 android:layout_marginTop="32dp" 93 android:text="list fragment" 94 android:textAllCaps="false" 95 app:layout_constraintEnd_toEndOf="parent" 96 app:layout_constraintStart_toStartOf="parent" 97 app:layout_constraintTop_toBottomOf="@+id/stop" /> 98 99 <EditText 100 android:id="@+id/editText" 101 android:layout_width="wrap_content" 102 android:layout_height="wrap_content" 103 android:layout_marginTop="32dp" 104 android:ems="10" 105 android:inputType="textPersonName" 106 android:text="Name" 107 app:layout_constraintEnd_toEndOf="@+id/list_fragment" 108 app:layout_constraintStart_toStartOf="@+id/list_fragment" 109 app:layout_constraintTop_toBottomOf="@+id/list_fragment" /> 110 111 </androidx.constraintlayout.widget.ConstraintLayout> 112 113 </layout>
3.2 数据类
Data.java
1 package com.example.databind; 2 3 public class Data { 4 public String key; 5 public int value; 6 7 public Data(String key, int value){ 8 this.key = key; 9 this.value = value; 10 } 11 12 @Override 13 public String toString() { 14 return "key = " + key + " value = " + value; 15 } 16 }
click.java
1 package com.example.databind; 2 import android.util.Log; 3 import androidx.databinding.ViewDataBinding; 4 import java.util.Timer; 5 import java.util.TimerTask; 6 7 public class Click { 8 ViewDataBinding binding; 9 private Timer timer; 10 11 public void onStartClick(final Data data){ 12 timer = new Timer(); 13 timer.schedule(new TimerTask() { 14 @Override 15 public void run() { 16 if (--data.value < 0){ 17 cancel(); 18 return; 19 } 20 Log.e("ActivityMainBinding","data : " + data); 21 data.key = "left"; 22 // binding.setData(data); 23 // binding.notifyChange(); 24 // binding.notifyPropertyChanged(R.id.value); 25 // binding.value.invalidate(); 26 if (!binding.hasPendingBindings()){ 27 binding.invalidateAll(); 28 } 29 } 30 },1000 * 1,1000 * 1); 31 } 32 public void onStopClicked(Data data){ 33 cancel(); 34 } 35 private void cancel(){ 36 if (timer != null){ 37 timer.cancel(); 38 timer = null; 39 } 40 } 41 public Click(ViewDataBinding binding){ 42 this.binding = binding; 43 } 44 }
3.3 绑定
1 package com.example.databind; 2 3 import android.os.Bundle; 4 import android.view.LayoutInflater; 5 import android.view.View; 6 import android.view.ViewGroup; 7 8 import androidx.annotation.NonNull; 9 import androidx.annotation.Nullable; 10 import androidx.databinding.DataBindingUtil; 11 import androidx.databinding.ViewDataBinding; 12 import androidx.fragment.app.Fragment; 13 14 import butterknife.ButterKnife; 15 import butterknife.OnClick; 16 17 public class MainFrgmt extends Fragment { 18 19 ViewDataBinding binding; 20 private Data data; 21 22 private void init(){ 23 data = new Data("time", 10); 24 binding.setVariable(BR.data,data); 25 binding.setVariable(BR.click,new Click(binding)); 26 } 27 @Nullable 28 @Override 29 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 30 binding = DataBindingUtil.inflate(inflater,R.layout.frgmt_main,container,false); 31 View view = binding.getRoot(); 32 ButterKnife.bind(this,view); 33 init(); 34 return view; 35 } 36 37 @OnClick(R.id.reset) 38 public void onResetClicked(View view){ 39 data.key = "time"; 40 data.value = 10; 41 binding.invalidateAll(); 42 } 43 }
其中:
- 第31行创建绑定对象 binding
- 第24、25行设置绑定的数据对象,其中的BR是绑定库生成的一个类,其中包含<data>中声明的变量的id。如data的id就是BR.data
- 第41行更新数据对象
4.Item绑定数据源
4.1 item布局
1 <?xml version="1.0" encoding="utf-8"?> 2 <layout xmlns:app="http://schemas.android.com/apk/res-auto" 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 5 <data> 6 <variable name="item" type="com.example.databind.list.ItemData" /> 7 </data> 8 9 <LinearLayout 10 android:orientation="horizontal" 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content" 13 > 14 <ImageView 15 android:id="@+id/item_icon" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_marginLeft="16dp" 19 android:layout_marginStart="16dp" 20 android:layout_marginTop="8dp" 21 android:layout_marginBottom="8dp" 22 android:layout_gravity="center_vertical" 23 android:layout_weight="1" 24 app:imageSrc="@{item.icon}" /> 25 <TextView 26 android:id="@+id/item_title" 27 android:layout_width="wrap_content" 28 android:layout_height="wrap_content" 29 android:layout_weight="5" 30 android:layout_marginLeft="32dp" 31 android:layout_marginStart="32dp" 32 android:layout_marginRight="8dp" 33 android:layout_marginEnd="8dp" 34 android:layout_gravity="center_vertical" 35 android:gravity="left|center_vertical" 36 android:textAllCaps="false" 37 android:textSize="16sp" 38 android:layout_marginTop="8dp" 39 android:layout_marginBottom="8dp" 40 android:text="@{item.title,default = item}" /> 41 </LinearLayout> 42 43 </layout>
4.2 数据类
1 package com.example.databind.list; 2 3 public class ItemData { 4 public String title; 5 public int icon; 6 }
4.3 adapter
1 package com.example.databind.list; 2 3 import android.view.LayoutInflater; 4 import android.view.View; 5 import android.view.ViewGroup; 6 import android.widget.ImageView; 7 8 import androidx.annotation.NonNull; 9 import androidx.databinding.BindingAdapter; 10 import androidx.databinding.DataBindingUtil; 11 import androidx.recyclerview.widget.RecyclerView; 12 13 import com.example.databind.R; 14 import com.example.databind.databinding.ListItemBinding; 15 16 import java.util.ArrayList; 17 import java.util.List; 18 19 public class ListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { 20 21 private List<ItemData> datas = new ArrayList<>(); 22 23 public void initData(){ 24 for (int i = 0 ; i < 32; ++i){ 25 ItemData item = new ItemData(); 26 item.title = "item" + i; 27 switch (i){ 28 case 11: case 21: case 31: 29 case 1 : item.icon = R.mipmap.icon1; break; 30 31 case 12: case 22: case 32: 32 case 2 : item.icon = R.mipmap.icon2; break; 33 34 case 13: case 23: case 33: 35 case 3 : item.icon = R.mipmap.icon3; break; 36 37 case 14: case 24: case 34: 38 case 4 : item.icon = R.mipmap.icon4; break; 39 40 case 15: case 25: case 35: 41 case 5 : item.icon = R.mipmap.icon5; break; 42 43 case 16: case 26: 44 case 6 : item.icon = R.mipmap.icon6; break; 45 46 case 17: case 27: 47 case 7 : item.icon = R.mipmap.icon7; break; 48 49 case 18: case 28: 50 case 8 : item.icon = R.mipmap.icon8; break; 51 52 case 19: case 29: 53 case 9 : item.icon = R.mipmap.icon9; break; 54 default: item.icon = R.mipmap.icon0; break; 55 } 56 datas.add(item); 57 } 58 } 59 60 @BindingAdapter({"imageSrc"}) 61 public static void setImage(ImageView view, int icon){ 62 view.setImageResource(icon); 63 } 64 65 @NonNull 66 @Override 67 public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 68 LayoutInflater inflater = LayoutInflater.from(parent.getContext()); 69 View view = inflater.inflate(R.layout.list_item,parent,false); 70 BindHolder holder = new BindHolder(view); 71 72 return holder; 73 } 74 75 @Override 76 public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 77 BindHolder bh = (BindHolder) holder; 78 ItemData item = datas.get(position); 79 ListItemBinding binding = DataBindingUtil.bind(bh.itemView); 80 binding.setItem(item); 81 } 82 83 @Override 84 public int getItemCount() { 85 return datas.size(); 86 } 87 88 89 public static class BindHolder extends RecyclerView.ViewHolder{ 90 91 public BindHolder(@NonNull View itemView) { 92 super(itemView); 93 } 94 } 95 }
5.ActivityMainBinding 等绑定类哪来的?
5.1 他们是绑定框架生成的接口
位置:
以 Frgmt1Binding 为例,它的实现在 : appuildgeneratedap_generated_sourcesdebugoutcomexampledatabinddatabinding 目录下,
comexampledatabind 是包名,如下:
默认的命名规则:
布局文件名按Pascal命名方法(忽略下划线_) + Binding , 如布局文件是 Frgmt1.xml 那么生成的类就是 Frgmt1Binding
5.4 修改生成的绑定类的名字
1 <layout xmlns:app="http://schemas.android.com/apk/res-auto" 2 xmlns:tools="http://schemas.android.com/tools" 3 xmlns:android="http://schemas.android.com/apk/res/android"> 4 <data class="AAAAAAA"> 5 <import type="android.view.View" /> 6 <import type="com.example.databind.Exts" /> 7 </data> 8 <androidx.constraintlayout.widget.ConstraintLayout 9 android:clickable="true" 10 android:background="#ffffff" 11 android:layout_width="match_parent" 12 android:layout_height="match_parent"> 13 ...
这个示例 在当前模块的 databinding
包中生成绑定类 AAAAAAA
1 <data class=".BBBBBB"> 2 … 3 </data>
这个示例 在模块包中生成绑定类 BBBBBB
1 <data class="com.example.CCC"> 2 … 3 </data>
这个 示例在 com.example
包中创建绑定类 CCC
5.3 代码
1 package com.example.databind.databinding; 2 import com.example.databind.R; 3 import com.example.databind.BR; 4 import androidx.annotation.NonNull; 5 import androidx.annotation.Nullable; 6 import android.view.View; 7 @SuppressWarnings("unchecked") 8 public class Frgmt1BindingImpl extends Frgmt1Binding { 9 10 @Nullable 11 private static final androidx.databinding.ViewDataBinding.IncludedLayouts sIncludes; 12 @Nullable 13 private static final android.util.SparseIntArray sViewsWithIds; 14 static { 15 sIncludes = null; 16 sViewsWithIds = new android.util.SparseIntArray(); 17 sViewsWithIds.put(R.id.imageView, 3); 18 sViewsWithIds.put(R.id.textView, 4); 19 } 20 // views 21 @NonNull 22 private final androidx.constraintlayout.widget.ConstraintLayout mboundView0; 23 // variables 24 // values 25 // listeners 26 // Inverse Binding Event Handlers 27 28 public Frgmt1BindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) { 29 this(bindingComponent, root, mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds)); 30 } 31 private Frgmt1BindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) { 32 super(bindingComponent, root, 0 33 , (android.widget.ImageView) bindings[3] 34 , (android.widget.TextView) bindings[1] 35 , (android.widget.TextView) bindings[4] 36 , (android.widget.TextView) bindings[2] 37 ); 38 this.key.setTag(null); 39 this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0]; 40 this.mboundView0.setTag(null); 41 this.value.setTag(null); 42 setRootTag(root); 43 // listeners 44 invalidateAll(); 45 } 46 47 @Override 48 public void invalidateAll() { 49 synchronized(this) { 50 mDirtyFlags = 0x4L; 51 } 52 requestRebind(); 53 } 54 55 @Override 56 public boolean hasPendingBindings() { 57 synchronized(this) { 58 if (mDirtyFlags != 0) { 59 return true; 60 } 61 } 62 return false; 63 } 64 65 @Override 66 public boolean setVariable(int variableId, @Nullable Object variable) { 67 boolean variableSet = true; 68 if (BR.click == variableId) { 69 setClick((com.example.databind.Click) variable); 70 } 71 else if (BR.data == variableId) { 72 setData((com.example.databind.Data) variable); 73 } 74 else { 75 variableSet = false; 76 } 77 return variableSet; 78 } 79 80 public void setClick(@Nullable com.example.databind.Click Click) { 81 this.mClick = Click; 82 } 83 public void setData(@Nullable com.example.databind.Data Data) { 84 this.mData = Data; 85 synchronized(this) { 86 mDirtyFlags |= 0x2L; 87 } 88 notifyPropertyChanged(BR.data); 89 super.requestRebind(); 90 } 91 92 @Override 93 protected boolean onFieldChange(int localFieldId, Object object, int fieldId) { 94 switch (localFieldId) { 95 } 96 return false; 97 } 98 99 @Override 100 protected void executeBindings() { 101 long dirtyFlags = 0; 102 synchronized(this) { 103 dirtyFlags = mDirtyFlags; 104 mDirtyFlags = 0; 105 } 106 java.lang.String stringValueOfDataValue = null; 107 java.lang.String dataKey = null; 108 com.example.databind.Data data = mData; 109 int dataValue = 0; 110 111 if ((dirtyFlags & 0x6L) != 0) { 112 113 114 115 if (data != null) { 116 // read data.key 117 dataKey = data.key; 118 // read data.value 119 dataValue = data.value; 120 } 121 122 123 // read String.valueOf(data.value) 124 stringValueOfDataValue = java.lang.String.valueOf(dataValue); 125 } 126 // batch finished 127 if ((dirtyFlags & 0x6L) != 0) { 128 // api target 1 129 130 androidx.databinding.adapters.TextViewBindingAdapter.setText(this.key, dataKey); 131 androidx.databinding.adapters.TextViewBindingAdapter.setText(this.value, stringValueOfDataValue); 132 } 133 } 134 // Listener Stub Implementations 135 // callback impls 136 // dirty flag 137 private long mDirtyFlags = 0xffffffffffffffffL; 138 /* flag mapping 139 flag 0 (0x1L): click 140 flag 1 (0x2L): data 141 flag 2 (0x3L): null 142 flag mapping end*/ 143 //end 144 }
5.4 可以直接使用 ViewDataBinding
1 private ViewDataBinding binding; 2 3 private void init(View view){ 4 binding = DataBindingUtil.bind(view); 5 MainActivity main = (MainActivity) getActivity(); 6 binding.setVariable(BR.data,main.data); 7 }