DataBining从基本使用到核心源码解析,看不懂的兄弟跟我一起去摆摊(1)

最后

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

以下是今天给大家分享的一些独家干货:

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

进入activity的布局文件,鼠标放到根布局上,然后同时按住alt+enter打开系统提示框。由于我们之前在build.gradle中设置了打开dataBinding,所以当前会出现Convert to data binding layout选项,顾名思义就是转换成dataBinding支持的布局模式,选择该选项即可。

选择好了该模式以后,布局文件大概就会变成这样子。布局最外层给自动加上了layout便签,包裹了我们最初的根布局。其次,系统自动生成了和之前的根布局同级。

接下来只需要在data标签里面配置好系统自动生成类的类名,以及属性即可。其中variable中name有两个作用,第一是使用该数据源,第二个是用来自动生成更新数据时调用的方法名,type指定好前面我们写好的那个bean即可。在控件上使用时只需要通过@{name.field}的方式设置在控件上即可,当然这里以TextView为例,ImageView需要特殊处理,后面会讲到。

经过以上的准备工作,接下来就要开始正式使用了。我这里打开了一个线程,然后每过一秒变化一下数据源,注意每次修改了数据源,最后都需要调用setTestVariable方法才能生效

运行效果如下:

效果符合预期,每过一秒就自动更新数据,而我们并没有对TextView进行setText操作。

我们成功地实现了在数据源变化的时候自动设置到ui上面,也仅仅是实现了功能而已,但是这种方法非常地笨拙,每次数据源更新的时候还要给dataBinding重新设置数据源,操作很麻烦,那有没有只需要写一遍就可以初始化数据源,从而一劳永逸的办法呢?答案是有的,接下来,我们来看看方式2。

方式2、利用@Bindable注解以及官方的notifyPropertyChanged方法来实现

xml文件不需要变,按照方式1的写法就可以了,只需要在Bean的字段的get方法上面加上@Bindable注解,然后在字段的set方法里调用一下notifyPropertyChanged方法即可,需要注意的是,必须要继承BaseObservable才会有该方法。其中@Bindable是告诉DataBinding在可以调用getName方法来获取name的值,notifyPropertyChanged方法是告知DataBinding当前数据已修改,快去调用get方法获取最新数据吧!所以不用重新设置数据源的原因大家也都看出来了,其实就是在给Bean设值的时候通知了DataBinding,然后DataBind自动去更新了。

接下来在使用时,就不需要重新给DataBinding设置数据源了 。

当时这种方式也有缺陷,就是Bean里面每个字段的set和get方法都需要进行修改,加上@Bindable和notifyPropertyChanged方法,否则将会自动更新失败。只要是需要人工修改的地方,那么在实际业务中不可避免地可能出现问题。那么有没有容错性更强的方式呢,当然是有的,下面说下方式3,也是最常用的方式。

方式3、使用ObservableField的方式

修改Bean即可,将属性包装成ObservableField,该属性的类型改成泛型的形式给出,如下图

在使用时的方式也需要稍加修改,需要先获取到ObservableFiled属性,然后调用其set方法来设置新值。这种方法是不是比前面那2种要好不少呢,既不需要每次赋值的时候给DataBinding赋值,也不需要记得给Bean里每个字段的get方法加上@Bindable,以及set方法上手动加上调用notifyPropertyChanged方法,这种方式也是个人觉得最好用的方式。

三、高级用法


1、数据源是Map或者List时如何使用

如果数据源是存放在Map或者List时,DataBinding提供了相应封装好的类可以直接使用,Map -> ObservableMap,List -> ObservableList,不过这两个都是接口,实例化的时候用ObservableArrayMap和ObservableArrayList即可。

我们来简单尝试一下Map进行存值和使用吧,首先在Bean中添加一个数据源为ObservableMap类型。

这里还需要相应添加一个key,因为java的字符串是不支持单引号和双引号一起使用的,在xml中使用时需要。

接下来就是对map进行赋值了,这里需要注意,map和key需要同时设置,不然在xml中绑定了是拿不到值的。

2.在xml中进行运算符操作

在使用了DataBinding的xml中可以使用简单运算符操作,比如,我们可以用取到的数字源进行操作后再设置到控件上,这里举一个例子,我们在获取的值后面加一个单位。需要注意的是,在这里拼接字符串需要使用的并不是单引号,而是数字1左边的那个键。

除了上面用到的字符串拼接以外,还支持另外一些操作,具体见下图,注意并不是所有的符号都支持。

3.双向绑定

什么是双向绑定?

前面的实现都是单向的绑定操作,也就是说数据源改变以后会自动更新到控件上。但是,如果想让控件的值被用户手动输入变化后也能自动地同步到数据源里,这个操作就叫做双向绑定。

双向绑定以后可以实现什么效果?

为了演示一下双向绑定的效果,我给页面上加了一个输入框以及一个文本框,它们都使用的同一个数据源。当用户给输入框删除或者输入新数据以后,这二者虽然使用的同一份数据源,但是由于输入框输入的内容并没有同步到数据源中,所以文本框的数据并不会自动地刷新成用户在输入框中输入的值。

而双向绑定以后是这样的,输入框不管是删除还是输入,都会立即自动把结果同步到文本框上

如何实现双向绑定呢?

双向绑定具体做法是将@{}改成@={}。如下图,在将EditText的值与数据源双向绑定了以后,当用户在输入框中输入内容以后,由于输入框的值会同步到二者共用的数据源中,下面的TextView内容就会自动跟着变化。

4.通过BindingAdapter来自定义Setter

有些控件的属性并不是直接显示在控件上的,而是需要经过处理甚至是第三方API的处理才能使用。又或者是想要覆盖系统自身的属性,也可以,总之就是这里定义的对属性的BindingAdapter注解处理方式会覆盖系统的方式。

比如想要将url设置到ImageView上,需要通过Picasso或者Glide来将url下载转成bitmap来设置,这个时候就需要特殊处理。

首先在Bean里新增一个String类型的url字段,然后在ImageView上自定义一个url属性,将其绑定到url字段上。也可以定义多个,这里多定义了一个error属性。

最后处理一下实际的将url设置到ImageView上的操作

5.设置数据到列表控件中

以RV为例,接下来说一下如何将数据设置到列表控件中,这也是非常常见的操作了。由于列表控件是有Adapter的,相当于设置数据时多传了一层。

这是一个并没有使用DataBinding的简单RecyclerView示例,里面有3种不同布局,当然这里代码写得很不规范,因为这个不是重点。

运行以后大致的效果如下,为了区分三种不同的Item,我将每个Item显示的TextView个数进行了区分,方便大家看懂这是不同的item。

接下来我们要做让它绑定到DataBinding,实现当数据源变化时不调用nofigyDataSetChanged方法来实现Item数据发生变化。

使用步骤参照之前基本使用来即可,第一步新建一个Bean作为DataBinding获取数据库的地方。第二步将那3个item都变成layout包裹的DataBinding布局。

第三步是将当前布局交给DataBinding进行托管

第四步是需要修改一个ViewHolder的实现,由于之前是将View托管到了ViewDataBinding中,所以需要View的时候可以从托管平台DataBinding来获取。这里保存下binding是为了在onBindViewHolder中绑定数据用,要记住现在需要数据都是从ViewDataBiding中获取了,而不能直接从values数组里去获取,不然数据修改了是刷新不了的。这里实现一个接口,是为了偷懒,在onBindViewHolder中统一处理。

最后在onBindViewHolder中调用系统的setVariable方法对DataBinding进行赋值,相比最初的根据position进行判断布局,然后再进行赋值是不是节省了很多代码哈哈,请看下图:

最后,验证一下数据修改时,会自动刷新到rv上吧,我这里开了个线程,每秒修改一下rv上的数据源,注意我只是修改了数据源并没有notifyDataSetChanged。

[图片上传失败…(image-3a755f-1592979137700)]

让我们来看一下最终的效果,符合预期。

上源码(划重点)


1、DataBindingUtil是个什么类?

在使用DataBinding的时候,一般第一件事情是初始化DataBinding,代码大概如下:

final DataBindingTest viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

final TestBean testBean = new TestBean();

viewDataBinding.setTestVariable(testBean);

那么DataBinding究竟是何方神圣呢,我们打开该类的属性和方式视图,如下:

这里mapper是调用DataBindingUtil.setContentView初始化DataBinding时用来将layoutid和实际实现类进行映射的一个DataBinderMapperImpl对象,在下面讲解DataBinding初始化的时候还会讲到。sDefaultComponent是预留给开发人员设置的一个属性,默认为null,也会体现在后面DataBinding初始化的过程中。

其中inflate方法是初始化DataBinding方法的另一种方式,在后面给recyclerview上使用DataBinding的例子中会详细讲到,具体代码大概是这样子的:

ViewDataBinding viewDataBinding = DataBindingUtil.inflate(MainActivity2.this.getLayoutInflater(),R.layout.rv_layout1,parent,false);

其中bind()方法也和上面的Inflate方法类似,也是用来初始化DataBindingUtil的方式,和setContentView方法依然类似,这三者功能基本是一样的。

下面的findBinding方法和getBinding方法都是对外提供的static方法,用来返回生成的ViewDataBinding类的,通过这些方法我们可以根据View对象或者布局Id来获取绑定好的ViewDataBinding对象。

convertByIdToString后面也会讲到,是系统提供给我们的对外接口,具体很少使用,看注释是有利于打日志。

bindToAddedViews是调用DataBindingUtil.setContentView()进行绑定的时候会调用的中间方法,目的是为了区分当前是不是只有一个控件,进行一些特殊处理,在讲DataBinding初始化的时候也会讲到。

2、DataBinding初始化的时候都做了什么事情

我们在Activity中使用DataBinding的时候,需要进行初始化,代码大致是这么写的:

DataBindingUtil.setContentView(this, R.layout.activity_main);

我们点进去看下都做了什么操作,可以看到很简单,首先调用了Activity的setContentView方法,这个是不使用DataBinding的时候也需要调用的,其次是调用了bindToAddedViews方法,调用的时候将最外层的Framlayout容器以及布局的LayoutId给传了进去。

在bindToAddedViews方法中,不管走哪个判断分支,是走到了bind方法,其中第一个参数component默认是null,除非咱们手动去设置。第二个参数是所有view的数组,第三个是布局文件Id。

接着往下面看,是调用到了一个Mapper对象的getDataBinder方法

Mapper对象是一个DataBinderMapper抽象类的子对象,这个抽象类代码如下:

实现了DataBinderMapper抽象类的具体子类是DataBinderMapperImpl类,该类的getDataBinder()方法具体代码如下:

ViewDataBinding getDataBinder(DataBindingComponent component, View view, int layoutId) {

switch(layoutId){

case LAYOUT_AAA:

return new AaaBindingImpl(component, view);

case LAYOUT_BBB:

return new BbbBindindImpl(component, view)

}

}

这段代码十分地简单,就是初始化了相应的DataBinding类,没什么好说的,还有就是保存好了所有的布局文件id、Variable名、到一个map中,一直findUseage你会发现最终是在DataBindingUtil类中的convertByIdToString方法使用,该方法是暴露给开发人员的public方法,说明是预留给我们使用的功能,查看方法提示大概是可以用来和日志有关系?

3、setVariable数据更新流程是怎么样的

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的Android开发中高级必知必会核心笔记,共计2968页PDF、58w字,囊括Android开发648个知识点,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

[外链图片转存中…(img-7gBtkpTS-1715264203453)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个问题与上一个问题的唯一区别在于,相邻格子不可同时摆摊,这个约束条件可以用“钻石型”搜索来实现。 具体解法如下: 1. 从左上角开始遍历整个地图,找到第一个可以摆摊的格子。 2. 针对该格子,尝试在上、下、左、右四个方向分别摆摊。如果某个方向可以摆摊,则将该格子标记为已摆摊,并继续遍历下一个格子。 3. 如果所有方向都无法摆摊,则回溯到上一个格子,尝试另一个方向。 4. 在回溯的过程中,需要判断当前格子是否与已经摆摊的格子相邻。如果相邻,则跳过该方向的尝试。 5. 如果遍历完整个地图,则表示找到了一种摆摊方案,计数器加1。 6. 继续遍历其他格子,直到所有方案都被遍历完。 在钻石型搜索中,每个格子的遍历顺序如下: ``` * *A* * * * A ``` 即先尝试上、下、左、右四个方向,然后再尝试左上、右上、左下、右下四个方向。这样可以保证每个格子只与其周围的8个格子相邻,而不与更远的格子相邻。 在实际实现中,可以使用一个二维数组来记录已经摆摊的格子,一个计数器来记录摆摊方案的数量。同时,为了避免重复计数,可以在遍历每个格子时,判断其是否已经被标记为已摆摊,如果是,则直接跳过。 需要注意的是,在回溯过程中需要将已经标记为已摆摊的格子恢复为未摆摊的状态,以便尝试其他方案。同时,为了避免重复计数,需要在每个格子尝试摆摊时判断其周围是否已经有摆摊的格子。 由于该问题的搜索空间非常大,因此对于较大的地图,该算法的时间复杂度可能会非常高,需要进行优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值