DataBinding概述
- DataBinding主要是将数据与UI控件的绑定,你无需手动调用视图来 set 新状态,你只需 set 数据本身。
- 使用方式可以参考https://www.jianshu.com/p/a294e1ea3327
DataBinding使用方式
1、在主Module的build.gradle android模块中添加配置
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "xxxxx"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
//使用databinding
dataBinding{
enabled true
}
新版本使用
buildFeatures {
dataBinding = true
}
}
定义模型
public class User {
private String name;
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Bindable
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
修改布局文件
<?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.example.ysj.User"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:textSize="50sp"
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:textSize="50sp"
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.pwd}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</layout>
在Activity设置DataBinding
public class MainActivity extends AppCompatActivity {
ActivityMainBinding binding;
//从model层来的数据
User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
//完成UI初始化
binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
user=new User("测试用户","123456");
//设置值
binding.setUser(user);
}
}
更多详细使用可以参考以下网址
https://www.jianshu.com/p/70316eb4e0f8
DataBinding原理 DataBindingUtil.setContentView
布局文件会被解析拆分成两个布局文件
//这个文件主要是记录布局文件基本信息并将布局文件每个组件都包装成Target
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="app\src\main\res\layout\activity_main.xml"
isBindingData="true" isMerge="false" layout="activity_main"
modulePackage="com.example.databindingdemo_20210117" rootNodeType="android.widget.LinearLayout">
<Variables name="user" declared="true" type="com.example.ysj.User">
<location endLine="8" endOffset="41" startLine="6" startOffset="8" />
</Variables>
<Targets>
<Target tag="layout/activity_main_0" view="LinearLayout">
<Expressions />
<location endLine="38" endOffset="18" startLine="11" startOffset="4" />
</Target>
<Target id="@+id/tv1" tag="binding_1" view="TextView">
<Expressions>
<Expression attribute="android:text" text="user.name">
<Location endLine="21" endOffset="38" startLine="21" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="21" endOffset="36" startLine="21" startOffset="28" />
</Expression>
</Expressions>
<location endLine="25" endOffset="55" startLine="16" startOffset="8" />
</Target>
<Target id="@+id/tv2" tag="binding_2" view="TextView">
<Expressions>
<Expression attribute="android:text" text="user.pwd">
<Location endLine="32" endOffset="37" startLine="32" startOffset="12" />
<TwoWay>false</TwoWay>
<ValueLocation endLine="32" endOffset="35" startLine="32" startOffset="28" />
</Expression>
</Expressions>
<location endLine="36" endOffset="55" startLine="27" startOffset="8" />
</Target>
</Targets>
</Layout>
//第二个布局文件就通过tag和第一个布局文件关联
<?xml version="1.0" encoding="utf-8"?>
<!--这里面就用来定义数据源-->
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:tag="layout/activity_main_0">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="binding_1"
android:textSize="50sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="binding_2"
android:textSize="50sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
public class DataBindingUtil {
private static DataBinderMapper sMapper = new DataBinderMapperImpl();
public static <T extends ViewDataBinding> T setContentView(@NonNull Activity activity,
int layoutId, @Nullable DataBindingComponent bindingComponent) {
//设置布局文件
activity.setContentView(layoutId);
View decorView = activity.getWindow().getDecorView();
//获取contentView 布局文件
ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content);
return bindToAddedViews(bindingComponent, contentView, 0, layoutId);
}
private static <T extends ViewDataBinding> T bindToAddedViews(DataBindingComponent component,
ViewGroup parent, int startChildren, int layoutId) {
final int endChildren = parent.getChildCount();
final int childrenAdded = endChildren - startChildren;
if (childrenAdded == 1) {
final View childView = parent.getChildAt(endChildren - 1);
return bind(component, childView, layoutId);
} else {
final View[] children = new View[childrenAdded];
for (int i = 0; i < childrenAdded; i++) {
children[i] = parent.getChildAt(i + startChildren);
}
return bind(component, children, layoutId);
}
}
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View[] roots,
int layoutId) {
//DataBinderMapperImpl
return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId);
}
static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,
int layoutId) {
return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);
}
}
···
//类似map映射
public class DataBinderMapperImpl extends DataBinderMapper {
private static final int LAYOUT_ACTIVITYMAIN = 1;
//映射对象,里面存了布局文件解析后的key(布局文件)和value(编号)
private static final SparseIntArray INTERNAL_LAYOUT_ID_LOOKUP = new SparseIntArray(1);
static {
INTERNAL_LAYOUT_ID_LOOKUP.put(com.example.databindingdemo_20210117.R.layout.activity_main, LAYOUT_ACTIVITYMAIN);
}
@Override
public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
//通过传进来的layoutId,解析对应的tag,然后从映射里面找对应的布局文件,然后找到了就新建对应的ViewDatabinding对象,然后返回
int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId);
if(localizedLayoutId > 0) {
final Object tag = view.getTag();
if(tag == null) {
throw new RuntimeException("view must have a tag");
}
switch(localizedLayoutId) {
case LAYOUT_ACTIVITYMAIN: {
if ("layout/activity_main_0".equals(tag)) {
return new ActivityMainBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main is invalid. Received: " + tag);
}
}
}
return null;
}
}
public class ActivityMainBindingImpl extends ActivityMainBinding {
//解析对应的组件然后放到数组里面
public ActivityMainBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}
private ActivityMainBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.TextView) bindings[1]
, (android.widget.TextView) bindings[2]
);
//为每个组件设置tag
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tv1.setTag(null);
this.tv2.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
}
}
到此时DataBindingUtil 的 setContentView方法分析完毕
DataBinding原理 DataBindingUtil.setData
public void setUser(@Nullable com.example.ysj.User User) {
//更新监听注册
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
notifyPropertyChanged(BR.user);
super.requestRebind();
}
public abstract class ViewDataBinding extends BaseObservable implements ViewBinding {
...
//创建监听器类
private static final CreateWeakListener CREATE_PROPERTY_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(ViewDataBinding viewDataBinding, int localFieldId) {
//创建属性监听器,当属性发生改变的时候可以被监听
return new WeakPropertyListener(viewDataBinding, localFieldId).getListener();
}
};
//更新监听注册,localFieldId为BR中的ID,它会为每一个属性生成一个编号
protected boolean updateRegistration(int localFieldId, Observable observable) {
return updateRegistration(localFieldId, observable, CREATE_PROPERTY_LISTENER);
}
private boolean updateRegistration(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return unregisterFrom(localFieldId);
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
registerTo(localFieldId, observable, listenerCreator);
return true;
}
if (listener.getTarget() == observable) {
return false;//nothing to do, same object
}
unregisterFrom(localFieldId);
registerTo(localFieldId, observable, listenerCreator);
return true;
}
protected void registerTo(int localFieldId, Object observable, ViewDataBinding.CreateWeakListener listenerCreator) {
if (observable != null) {
ViewDataBinding.WeakListener listener = this.mLocalFieldObservers[localFieldId];
if (listener == null) {
listener = listenerCreator.create(this, localFieldId);
this.mLocalFieldObservers[localFieldId] = listener;
if (this.mLifecycleOwner != null) {
listener.setLifecycleOwner(this.mLifecycleOwner);
}
}
listener.setTarget(observable);
}
}
}
网友评论