Jetpack之DataBinding,h5开发移动

</androidx.constraintlayout.widget.ConstraintLayout>

绑定传递数据源

给DataBinding中的User对象赋值

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//此时可以通过DataBindingUtil来设置Activity的页面布局。此时会返回一个ActivityMainBinding对象。这个是编译时根据xml布局文件中的数据绑定自动生成的实现类。
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.lifecycleOwner = this
binding.user = User(‘张三’) //完成数据绑定

//如果是在列表中使用,则可以如下编写 。ActivityMainBinding是根据activity_main布局文件自动生成的
val binding = ActivityMainBinding.inflate(layoutInflater, null, false)
binding.user = User(‘张三’)
}

如何实现数据变化的视图自动更新呢?

想要实现数据变化的视图自动更新我们只需要让实体类User集成BaseObservable。当User中字段发生变更,只需要调用user.notifyPropertyChanged就可以让UI刷新。

public class User extends BaseObservable {
public String name;
//当使用name字段发生变更后,若想UI自动刷新,我们需要给它写个get方法并且标记Bindable注解。

@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
//最后调用notifyPropertyChanged方法即可
//该字段变更了,通知UI刷新数据
notifyPropertyChanged(org.devio.as.main.BR.user);
}

//除此之外也可以使用ObserverableBoolean…observerableInt来监听数据的变化
ObserverableBoolean success = new ObserverableBoolean<>();
}

DataBinding也支持在布局文件中使用数组、List、Set和Map,且在布局文件中都可以通过List[index]的形式来获取元素。因为xml的特性,在声明List之类的数据类型时,需要使用尖括号的转义字符,来看下面的代码:

<?xml version="1.0" encoding="utf-8"?> //List 其中< 和 >俩字符需要转义 //Map //Set //SparseArray



DataBinding在xml中数据绑定支持的语法表达式也是非常丰富的,支付在布局文件中使用一下运算符、表达式和关键字:

  • 算术运算符:+ - * / %;
  • 字符串连接运算符:+;
  • 逻辑运算符:&& ||;
  • 二元运算符:& | ^;
  • 一元运算符: + - ! ~;
  • 位移运算符: >> >>> <<;
  • 比较运算符: == > < >= <= (需要被转义);
  • 判断是否是类的实例:instanceof;
  • 分组运算符:();
  • 字面量运算符 - 字符,字符串、数据、null;
  • 类型转换、方法调用;
  • 字段访问;
  • 数组访问: [];
  • 三元运算符:?:;
  • 不支持以下操作:this super new 显示泛型调用。

4.BataBinding如何扩展View属性

我们知道,以前想要给ImageView增加几个属性,必须要写个自定义的ImageView在构造函数中一顿解析。那看看使用DataBinding如何扩展View属性。

public class HiImageView extends ImageView{

//需要使用BindingAdapter注解并标记在public static方法上。
//value中的字段随意添加和方法参数一一对应即可。
@BindingAdapter(value = {“image_url”, “isCircle”})
public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle) {
view.setImageUrl(view, imageUrl, isCircle, 0);
}
//requireAll = false代表是否以下三个属性在xml中同时使用才会调用到该方法
//为false的话,只要有一个属性被使用就能调用到该方法
@BindingAdapter(value = {“image_url”, “isCircle”, “radius”}, requireAll = false)
public static void setImageUrl(PPImageView view, String imageUrl, boolean isCircle, int radius) {

}
}

//在布局文件中如下使用,便能实现图片圆角和资源Url绑定的功能
<org.devio.as.main.HiImageView

app:image_url ="@{user.avatar}"
app:radius="@{50}">
</org.devio.as.main.HiImageView>

5.DataBinding使用的建议

  • 如fragment_layout_my.xml布局,在编译时会生成FragmentLayoutMyImpl.java实现类,我们可以搜索这种类debug跟进解决问题。

  • 不建议在列表中乱用,因为DataBinding数据绑定是延迟一帧的,如果列表中的ItemView的宽高需要计算后才能正确展示,不建议使用DataBinding操作。否则会看到列表ItemView明显的撑开动画,体验不好。

此处可以使用dataBinding.executePendingBindings()快速渲染布局解决

  • 实体类配合BaseObservable可以友好的解决数据双向绑定的问题。

6.ViewBinding又是什么?

Android Studio更新到3.6之后,多了一个ViewBinding的功能,看到这个名字就感觉和DataBinding很相似,那么它们有什么区别呢?

  • DataBinding可以将View和界面上的数据进行双向绑定,ViewBinding不行,也就是不能再xml中绑定数据,若要使用则需要在Gradle中开启如下配置:

viewBinding {
enabled = true
}

  • 如果你想要实现双向数据绑定,那么可以选择DataBinding;

  • ViewBinding主要是帮我们省却了findViewById的过程,但是它在编译阶段比DataBinding耗时更短;

  • 如果你已经使用了Kotlin,那其实ViewBinding就没必要使用了。

7.DataBinding源码分析

布局文件的加载确认

XML分离后XML文件位置

开发者编写的布局

<?xml version="1.0" encoding="utf-8"?>






绑定了@{}的View添加一个tag

app/build/imtermediates/data_binding_layout_info_type_merge/debug/activity_main-layout.xml

<?xml version="1.0" encoding="utf-8" standalone="yes"?>














false









false






app/build/imtermediates/incremental/mergeDebugResources/stripped.dir/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>


DataBindingUtil类

setContentView

DataBindingUtil.setContentView(Activity activitiy,int layoutId);
—> bindToAddedViews(bindingComponent,contentView,startChildren,layoutId)9;
—>bind(compent,children,layoutId); //使用DataBindingUtil.inflate也是一样走到这里
//sMapper的实现类是APT生成的DataBinderMapperImpl类
—>sMapper.getDataBinder(dindingComponent,root,layoutId);

APT生成的DataBinderMapperImpl类

在app/build/generated/source/kapt/debug/com.xxx.xxx/DataBinderMapperImpl下,他是sMapper.getDataBinder(…)的实现。

其中可以根据layoutId,拿到每个布局的Binding实现。

//DataBinderMapperImpl
@Overridepublic ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {
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_ACTIVITYMAINTEST: {
if (“layout/activity_main_test_0”.equals(tag)) {
return new ActivityMainTestBindingImpl(component, view);
}
throw new IllegalArgumentException("The tag for activity_main_test is invalid. Received: " + tag); }
}
}

布局的Binding的Java实现

在app/build/generated/source/kapt/debug/com.xxx.xxx/databinding/ActivityMainTestBindingImpl,它是布局页面activity_main_test.xml的具体实现,但是在调用的时候我们用的是ActivityMainTestBinding

public class ActivityMainTestBindingImpl extends ActivityMainTestBinding {

public ActivityMainTestBindingImpl(@Nullable androidx.databinding.DataBindingComponent bindingComponent, @NonNull View root) {
this(bindingComponent, root, mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds));
}

private ActivityMainTestBindingImpl(androidx.databinding.DataBindingComponent bindingComponent, View root, Object[] bindings) {
super(bindingComponent, root, 1
, (android.widget.TextView) bindings[1]
, (android.widget.TextView) bindings[2]
);
this.mboundView0 = (android.widget.LinearLayout) bindings[0];
this.mboundView0.setTag(null);
this.tv1.setTag(null);
this.tv2.setTag(null);
setRootTag(root);
// listeners
invalidateAll();
}
}

ViewDataBinding

//在这里对XML文件信息读取,并存入数组中
protected static Object[] mapBindings(DataBindingComponent bindingComponent, View root,
int numBindings, IncludedLayouts includes, SparseIntArray viewsWithIds) {
Object[] bindings = new Object[numBindings];
mapBindings(bindingComponent, root, bindings, includes, viewsWithIds, true);
return bindings;
}

源码分析

核心原理从setVariable(id,value)开始分析

//ActivityMainTestBindingImpl
@Override
public boolean setVariable(int variableId, @Nullable Object variable) {
boolean variableSet = true;
if (BR.user == variableId) {
setUser((com.nearme.plugin.pay.activity.User) variable);
}
else {
variableSet = false;
}
return variableSet;
}

public void setUser(@Nullable com.nearme.plugin.pay.activity.User User) {
//1.注册监听器
updateRegistration(0, User);
this.mUser = User;
synchronized(this) {
mDirtyFlags |= 0x1L;
}
//2.调用监听器回调
notifyPropertyChanged(BR.user);
super.requestRebind();
}

1.注册监听

//ViewDataBinding
/**

  • @hide
    */
    protected boolean updateRegistration(int localFieldId, Observable observable) {
    //更新观查者模式发通知需要相关的信息,CREATE_PROPERTY_LISTENER是一个回调接口
    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;
}
//1.删除监听器
//调用WeakPropertyListener的unregister方法,然后把刚才建立的联系取消掉
unregisterFrom(localFieldId);
//2.注册监听器
//listenerCreator就是

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

之前的那个CREATE_PROPERTY_LISTENER
registerTo(localFieldId, observable, listenerCreator);
//3.注册完成
return true;
}

//注销监听
protected boolean unregisterFrom(int localFieldId) {
//调用WeakPropertyListener的unregister方法,然后把刚才建立的联系取消掉
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener != null) {
return listener.unregister();
}
return false;
}

//注册监听
protected void registerTo(int localFieldId, Object observable,
CreateWeakListener listenerCreator) {
if (observable == null) {
return;
}
WeakListener listener = mLocalFieldObservers[localFieldId];
if (listener == null) {
//调用create方法就是调用 return new WeakPropertyListener(viewDataBinding, localFieldId).getListener()
listener = listenerCreator.create(this, localFieldId);
mLocalFieldObservers[localFieldId] = listener;
if (mLifecycleOwner != null) {
listener.setLifecycleOwner(mLifecycleOwner);
}
}
//688
listener.setTarget(observable);
}

public void setTarget(T object) {
unregister();
mTarget = object;
if (mTarget != null) {
//1404
mObservable.addListener(mTarget);
}
}

private interface ObservableReference {
WeakListener getListener();
//1379
//- 1446 WeakPropertyListener中的target.addOnPropertyChangedCallback(this);
//- 40 Observable中的addOnPropertyChangedCallback(this);
//- 32 BaseObservable中addOnPropertyChangedCallback
//- 38 mCallbacks.add(callback);ViewDataBinding保存在一个List中
void addListener(T target);
void removeListener(T target);
void setLifecycleOwner(LifecycleOwner lifecycleOwner);
}

2.调用监听器回调
1.通知回调notifyPropertyChanged

//BaseObservable
public void notifyPropertyChanged(int fieldId) {
synchronized (this) {
if (mCallbacks == null) {
return;
}
}
//1.
mCallbacks.notifyCallbacks(this, fieldId, null);
}

//PropertyChangeRegistry
public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
mNotificationLevel++;
//2.notifiRecurse()->1.notifyRemainder()->notifyFirst64()-> notifyCallbacks()
//->2.mNotifier.onNotifyCallback()
notifyRecurse(sender, arg, arg2);
mNotificationLevel–;
if (mNotificationLevel == 0) {
if (mRemainderRemoved != null) {
for (int i = mRemainderRemoved.length - 1; i >= 0; i–) {
final long removedBits = mRemainderRemoved[i];
if (removedBits != 0) {
removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
mRemainderRemoved[i] = 0;
}
}
}
if (mFirst64Removed != 0) {
removeRemovedCallbacks(0, mFirst64Removed);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值