Jetpack之DataBinding绑定布局文件是怎样实现的?
dataBinding的实现原理是什么?dataBinding是怎么进行数据双向驱动的?
时间从来不语,却回答了所有。——致自己
本文从定义,用法,原理分析,由浅到深对DataBinding的实现原理进行挖掘,方便各位读者理解。篇幅较长,请耐心阅读。定义:使用声明形式将布局中的界面组件绑定到应用中的数据源。数据的改变直接驱动UI的变化。 视图绑定: 该模块的 build.gradle 文件中将 dataBinding 构建选项设置为 true。android { ... //第一种 dataBinding { enabled = true } //第二种 dataBinding.enabled = true //第三种 buildFeatures { viewBinding true } } 将xml布局中的根布局改成layout<?xml version="1.0" encoding="utf-8"?> 在Activity中使用视图绑定 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //第一种 直接绑定 val binding: ActivityMainBinding = DataBindingUtil.setContentView( this, R.layout.activity_main) //第二种 可以使用 LayoutInflater 获取视图 val binding: ActivityMainBinding = ActivityMainBinding.inflate (getLayoutInflater()) setContentView(binding.root) } 在Fragment中使用视图绑定
如果要在 Fragment、 ListView 或 RecyclerView 适配器中使用数据绑定项,使用绑定类或 DataBindingUtil 类的 inflate() 方法,如以下代码示例所示: val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false) // or val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
3 数据双向绑定
DataBinding除了可以进行布局绑定之外,还可以对布局view进行数据绑定。<?xml version="1.0" encoding="utf-8"?>
在根标签layout中添加一个data标签,name为数据bean的别名,type为数据bean的全类名。然后通过给TextView的text属性,布局中的表达式使用" @{}"语法写入特性属性中 进行数据绑定。android:text="@{user.firstName}",。在这里, TextView 文本被设置为 user 变量的 firstName 属性。
数据对象User data class User(val firstName,val lastName)
然后再Activity中,通过binding.user = User("","")将user变量绑定到布局视图上。 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding: ActivityMainBinding = DataBindingUtil.setContentView( this, R.layout.activity_main) binding.user = User("Test", "User") }
这样我们就完成了一个简单是数据到布局视图上的绑定,如果我们修改了user中的某个属性值,视图也会更新吗?我们接着往下看。
Databinding不仅提供了绑定视图的功能,还提供了动态更新的功能。通过使用可观察的数据对象,通知布局自动更新。 class User { val firstName = ObservableField() val lastName = ObservableField() val age = ObservableInt() }
首先我们将User对象中的数据定义为可观察的对象属性,当我们修改其中某个变量值得时候,会主动通知布局更新。除此之外还有没有其他办法实现?这个当然有,我们接着往下看。 class User : BaseObservable() { @get:Bindable var firstName: String = "" set(value) { field = value notifyPropertyChanged(BR.firstName) } @get:Bindable var lastName: String = "" set(value) { field = value notifyPropertyChanged(BR.lastName) } }
通过实现Observable接口将User对象变成一个可观察的对象,以便它们接收有关可观察对象的属性更改的通知。 Observable 接口具有添加和移除监听器的机制,但何时发送通知必须由您决定。为便于开发,数据绑定库提供了用于实现监听器注册机制的 BaseObservable 类。实现 BaseObservable 的数据类负责在属性更改时发出通知。具体操作过程是向 getter 分配 Bindable 注释,然后在 setter 中调用 notifyPropertyChanged() 方法,如以下示例所示: class User : BaseObservable() { @get:Bindable var firstName: String = "" set(value) { field = value notifyPropertyChanged(BR.firstName) } @get:Bindable var lastName: String = "" set(value) { field = value notifyPropertyChanged(BR.lastName) } }
数据绑定在模块包中生成一个名为 BR 地类,该类包含用于数据绑定的资源的 ID。在编译期间, Bindable 注释会在 BR 类文件中生成一个条目。如果数据类的基类无法更改, Observable 接口可以使用 PropertyChangeRegistry 对象实现,以便有效地注册和通知监听器。
4 原理分析
我们首先来看一下DataBindingUtil是如何绑定xml布局的:DataBindingUtil.setContentView(this, R.layout.activity_main ) public static T setContentView(@NonNull Activity activity, int layoutId, @Nullable DataBindingComponent bindingComponent) { //调用当前activity的setContentView方法 activity.setContentView(layoutId); View decorView = activity.getWindow().getDecorView(); //通过findViewById获取根布局 ViewGroup contentView = (ViewGroup) decorView.findViewById(android.R.id.content); return bindToAddedViews(bindingComponent, contentView, 0, layoutId); } private static T bindToAddedViews(DataBindingComponent component, ViewGroup parent, int startChildren, int layoutId) { //获取子view个数 final int endChildren = parent.getChildCount(); //添加了多少个view final int childrenAdded = endChildren - startChildren; //当只有一个子view时,直接获取ziview if (childrenAdded == 1) { final View childView = parent.getChildAt(endChildren - 1); return bind(component, childView, layoutId); } else { //当数量大于1个时,创建view数组用来接收子view final View[] children = new View[childrenAdded]; for (int i = 0; i < childrenAdded; i++) { children[i] = parent.getChildAt(i + startChildren); } return bind(component, children, layoutId); } }
从源码中可以看出不管是只有一个子View还是多个子View,最终都是调用bind()方法,我们接着往下看。 static T bind(DataBindingComponent bindingComponent, View[] roots, int layoutId) { return (T) sMapper.getDataBinder(bindingComponent, roots, layoutId); }
通过调用bind方法,我们看到sMapper.getDataBinder 返回一个DataBinding对象,那这个getDataBinder方法是怎么返回的呢,我们点进去发现调用到DataBinderMapper.getDataBinder,DataBindingMapper是个抽象类,那我们只能从其子类DataBinderMapperImpl入手。public ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) { int localizedLayoutId = INTERNAL_LAYOUT_ID_LOOKUP.get(layoutId); if (localizedLayoutId > 0) { //获取布局 Object tag = view.getTag(); if (tag == null) { throw new RuntimeException("view must have a tag"); } switch (localizedLayoutId) { case 1: //如果tag与这个标记相等 就new一个ActivityMainBindingImpl 返回 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; }
从DataBinderMapperImpl的getDataBinder中,我们终于看到了ActivityMainBindingImpl被创建,ActivityMainBindingImpl是ActivityMainBinding的实现类,至此我们终于知道val activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main )是如果被创建返回的了。
看到这里很多人有疑问了? "layout/activity_main_0".equals(tag) 这判断是怎么来的,明明自己没有在布局中没有设置tag标签,那这个tag是从哪来的?带着这个疑问我们进一步深入研究一下。首先我们要知道Databinding是基于APT技术动态生成的,比如上面的ActivityMainBindingImpl等代码都是通过编译自动生成。那么有没有一种可能 这个tag标签也是自动生成插入的。我们往下看。
我们找到编译后的activity_main-layout.xml文件,看一下编译器为我们做了哪些工作?<?xml version="1.0" encoding="utf-8" standalone="yes"?> //编译器自动为我们插入一个tag
我们从编译后的布局文件中可以看到,编译器在第八行 自动为我们插入了一个tag="layout/activity_main_0" 标签。用来代替我们在layout根标签的布局文件。至此我们知道了"layout/activity_main_0".equals(tag)这个判断添加是怎么来的了 ,由编译器自动为我们生成而来。
喜欢这篇文章的小伙伴,欢迎评论区留言,麻烦点个关注或收藏哦,您的支持就是小编创作的最大动力!
知名景区宣布关闭!上海人纷纷赶来打卡,网红店又开始人挤人去年年底,有公告称召稼楼景区于今年1月21日关闭要知道召稼楼的风格简单直接充满了本地特色的吃和买网红美食店可以一整天都排长队可以说是上海最闹猛的古镇了于是大家都着急去打卡就怕再也吃
便便臭味大粘马桶,是怎么回事?提醒先别碰这2类食物谣零零计划参考资料1中国慢性便秘诊治指南(2013,武汉)J。胃肠病学,2013,18(10)605612。2王佩佩,罗雯,禹铮,李明玮。慢性便秘的研究进展J。中国全科医学,201
胆不好,记住这6点,让胆更健康平常出现消化不良,很多朋友都一定认为是胃出了问题。但消化这一功能,其实还需要胆的协助。很多人因为不了解而忽视了他,甚至不知不觉中伤害了他,最终导致肝胆乃至胃肠的疾病出现。所以,生活
文案丨20句适合元宵节发朋友圈的高级文案欢迎来到山月文案馆吖首先祝大家元宵快乐呀!赶紧的收藏马上发起来吧1灯与月依旧,人与人长久2为你花开满城,为你明灯三千。3取月色二两,揉碎星月,包成元宵4浮空明焰,千树琉璃水上灯莲,
元宵节今年这些花灯一定没见过,灯火通明,光彩绚丽!元宵节也称灯节,闹花灯是元宵节的传统节日习俗,从古至今灯火之风盛行,并流传于后世。元宵节的花灯种类有很多,像龙灯荷花灯走马灯兔子灯花篮灯树地灯礼花灯等等。形态各异玲珑剔透,让人赏心
元宵节赏灯热,释放旅游经济向好势头北京日报客户端记者潘福达今天是元宵节,多家旅游平台近日发布的元宵节旅游报告显示,今年春节至元宵节期间,传统文化元素出圈叠加异地流动政策的利好,包括自贡灯会在内的多地新春灯会热度持续
春节假期,怒江福贡老姆登景区人气火爆人间烟火处,年味正浓时。春节假期,全国各地旅游景区人气火爆,游客出行热情高涨。近日,云南省怒江傈僳族自治州福贡县匹河怒族乡老姆登景区内人山人海热闹非凡,不少外地游客慕名而来,逛村寨
元宵节点亮一盏面灯热热闹闹过完春节,最盼望的就是正月十五赶紧到来。这是我们小时候共同的心愿。那原因再简单不过白面馒头肉馅饺子的美好生活已经结束,红芋饭红芋馍的时代宣告回归,而正月十五那天却可以抓住春
妖刀魏锐和武林风外籍功夫之王碰面,对手现场说出这番话!2月3日4日,河南卫视与唐山文旅集团联合举办的武林风将连续举办两场大赛。2月3日,将是一次中泰对决,由妖刀魏锐带领的中国战队对阵泰拳王子播求带领的泰国战队。其中,杨明位威阳等中国拳
去董宇辉化导致股价暴跌,为何新包装上的方脸老师不见了?如果说新东方的灵魂是俞敏洪,那东方甄选的灵魂是董宇辉。不客气的说,东方甄选离开了董宇辉啥都不是。为啥很多粉丝都等着董宇辉直播的时候下单?一是对他表示支持,第二就是他的直播才能一直看
怎一个惨字了得!年销量最低两位数这几个汽车品牌可能倒在2023年大浪淘沙,是车市不变的规律,车市发展越是成熟,马太效应越是明显。2022年,激烈的市场竞争中,一批曾经熟悉的汽车品牌相继告别中国市场,其中,包括广汽菲克广汽讴歌宝沃等品牌,跟中国车