DataBinding的引出:三种架构模式常见架构模式
-
MVC:在整个Android开发文档中没有显示指出
- 在2011 2012时期广泛使用
-
为什么是MVC模式
-
由工程结构决定:
- res/layout中的xml:作View 层,
- src中的Activity作控制层,所以是MVC架构
-
-
缺点:
-
Activity(控制层): 过于臃肿
- 本意是只做控制的但是还要承担View,模型
- 没有单一职责导致耦合度高、扩展性不好、管理混乱
-
-
MVP:Activity只做View层,引入P层(Presenter)
-
特点:
- 分层清晰
-
实现过程:View->P->View:
-
实例:
- 做登录功能,用户点击按钮后,Activity将信息提交给P层,P层调用服务器拿到模型数据,通过接口回调将模型数据回馈给View层(Activity)
-
-
弊端:
- 接口地狱:凡是P层的模型数据返回给View层均需要通过接口回调
-
-
MVVM:Activity:View层;P层换成VM层(ViewModel层):
-
工作表现细节:(View层)Activity访问VM层(ViewModel实现),VM层访问仓库拿到模型数据,这个数据怎么回馈给View层:使用DataBinding
-
引用DataBinding实现V层与VM层的双向绑定
- 这是两条单向的路:点击V层会触发VM层;修改VM层会影响V层
-
细节一:DataBinding属于MVVM上?
- DataBinding是一个工具集,独立于架构模式(每种架构模式都可以用DataBinding);
- 只不过大部分情况下,MVVM都会使用DataBinding;小部分情况下MVP也可以使用DataBinding;但是MVC基本就不用
- DataBinding比MVVM出现得早
-
细节二:JetPack组件库中的ViewModel和MVVM中的VM层是两回事
- MVVM架构模式中的VM层与JetPack组件库中的ViewModel是两回事,只是说VM层实现了ViewModel组件库会更好用,不实现也无所谓
- 前者是组件库,是工具集;
- 后者是架构之一
-
细节三:那个设计模式最好?
-
没有最好,只有适合;
-
MVC:适合与小项目,一两个人的,在Activity中分得细,能处理好就行
-
MVP:项目太大(例如:编译就有8分钟),需要分层非常清晰(开发的人太多了)
-
MVVM:界面更新频繁(网易云音乐,腾讯视频)
- 可以启用DataBinding+ViewModel+LiveData+LifeCycle实现数据驱动开发
-
-
细节四:MVI架构模式
-
DataBinding_Java版本实现数据驱动UI
-
设计效果:实现数据驱动UI
-
分层:
- Activity作为控制层
- JavaBean作为Model层
- 布局文件作为View层
-
实现效果:在Activity中开启对JavaBean属性修改,同时界面更新
-
-
环境准备:打开dataBinding开关
-
gradle技术
-
第一种打开方式:
// 打开DataBinding开关,访问闭包 // 第一种方式 dataBinding { enabled true }
-
第二种打开方式:
// 第二种方式,直接去拿属性 dataBinding.enabled=true
-
-
-
工程结构:
-
编写Model层(JavaBean):User类(后面会使用LiveData进行处理,不需要这样去写)
-
继承BaseObservable类:这个类就拥有DataBinding的能力了
public class User extends BaseObservable
-
编写字段:待会就更新这个
private String name; private String pwd;
-
添加对应的get:@Bindable(会生成字段对应的数值标记)
@Bindable public String getName() { return name; } @Bindable public String getPwd() { return pwd; notifyPropertyChanged(BR.pwd); }
-
在set方法内添加:notifyPropertyChanged(BR.具体属性)
public void setName(String name) { this.name = name; notifyPropertyChanged(BR.name); // APT又是主接处理器技术 BR文件 } public void setPwd(String pwd) { this.pwd = pwd; notifyPropertyChanged(BR.pwd); }
- 会自动生成BR文件(但是要等编译了才能用)
- APT技术:字节码插桩
这个地方BR.name,name属性会报错,因为没有在data标签中指定,下文解决了的
-
完整代码:User
package com.wasbry.myapplication; import androidx.databinding.BaseObservable; import androidx.databinding.Bindable; public class User extends BaseObservable { 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; notifyPropertyChanged(BR.name); // APT又是主接处理器技术 BR文件 } @Bindable public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; notifyPropertyChanged(BR.pwd); } }
-
-
使用DataBinding管理布局:
-
引入DataBinding管理布局:
- 未引入DataBinding时:VIew层(Activity去处理xml布局文件),
- 将DataBInding作为Activity与xml的中间层
-
使用DataBinding:管理布局中的控件
-
使用:将鼠标悬停在activity_main.xml文件的最外标签上,点击第一个提示
-
点击后的效果:将原式布局(activity_main.xml拆分成了两部分)
-
-
-
DataBinding布局拆分细节:管理布局中所有的控件
-
分层:DataBinding 管理了整个布局文件,但是将其拆分成了两部分
-
拿给DataBinding内部使用:就是这个data标签
<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> </data>
-
拿给Android做View绘制使用
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
-
-
分层后存储为位置:
-
上面那节:交个DataBinding内部使用
-
实现细节:在这个拆出来的布局文件中,会添加tag属性
<Target tag="layout/activity_main_0"
- 路径:app/build/intermediates/data_binding_layout_info_type_merge/debug/out中的activity_main-layout.xml
<?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.wasbry.myapplication" rootNodeType="androidx.constraintlayout.widget.ConstraintLayout"> <Targets> <Target tag="layout/activity_main_0" view="androidx.constraintlayout.widget.ConstraintLayout"> <Expressions /> <location endLine="26" endOffset="55" startLine="12" startOffset="4" /> </Target> </Targets> </Layout>
-
-
下面那节:拿给Android做View绘制使用
-
细节 :DataBinding为每一个布局文件都增加了tag属性
android:tag="layout/activity_main_0"
拆出来的两个布局文件的tag属性是相同的,上面的那节通过tag寻找到下面那节
- 路径:app/build/intermediates/incremental/mergeDeBugResources/strpped.dir/layout中的activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!--DataBinding 管理了整个布局文件,但是将其拆分成了两部分--> <!--拿给DataBinding内部使用--> <!-- 拿给Android做View绘制使用--> <androidx.constraintlayout.widget.ConstraintLayout 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:tag="layout/activity_main_0" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
-
-
-
-
将布局文件映射到DataBinding内部:上面那节的data标签内加东西
<data> <variable name="user" type="com.wasbry.myapplication.User" /> </data>
此时JavaBean中的BR.name也不会报错了
-
此时实现了双向绑定:
-
DataBinding指向了Model层(JavaBean):因为data标签内部已经指定了
-
DataBinding指向了View层:
- View层就是layout中的布局文件,这个是交给DataBinding管理的
-
-
细节:type里面虽然使用了全类名,但这个不是反射
-
-
修改布局文件中下面那一节:实现View层到Model层的一向绑定
-
核心代码:
android:text="@{user.name}"
-
完整代码:
<!-- 拿给Android做View绘制使用--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.name}" android:textSize="50sp" /> <TextView android:id="@+id/tv2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.pwd}" android:textSize="50sp"/> </LinearLayout>
-
-
修改控制层代码:实现Model层--->View层的一向绑定
-
实现细节为什么可以去掉原有的setContentView(R.layout.activity_main)
-
使用ActivityMainBinding指代布局文件:
final ActivityMainBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
-
证明指代:点击它(CTRL+H鼠标左键),发现直接都将绘制的布局文件自动全选
-
-
废除原有的setContentView语句
// setContentView(R.layout.activity_main);
-
实际上还是在内部调用了的
-
-
-
模拟Model数据:假设是从网络上过来的json数据,解析成了JavaBean
-
示意图:
-
为什么binding里面有set函数?
binding.setUser(user);//此时就完成了MainActivity--->Model层绑定,并且binding之前就完成了对View层的绑定;此时就实现了DataBinding作为中间媒介实现View层与Model层的双向绑定
因为在data标签内部的name属性中指定了 user;name属性指定什么,DataBinding内部依托APT注解处理器技术,扫描布局文件时,就会生成一个对应的set什么方法
<data> <variable name="user"//就是这个, type="com.wasbry.myapplication.User" /> </data>
-
-
真机测试一波:
-
-
使用线程自动触发JavaBean属性更新实现数据驱动UI效果
-
双向绑定:
-
Model--->view
binding.setUser(user);//建立与DataBinding的绑定关系,否则没有任何效果
-
View--->Model
android:text="@{user.name}"
-
-
线程的作用:修改Model层(JavaBean)数据
//Model--->View new Thread(new Runnable() { @Override public void run() { for(int i = 0;i < 10;i++){ try { Thread.sleep(1000); user.setName("第"+i+"次改变用户名"); user.setPwd("第"+i+"次改变用户名"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();
-
真机测试:确实是实现了的
-
-
总结:
-
经验教训:
-
当使用DataBinding时,只要报错了,90%以上都是布局文件写错了
-
当布局检查好后,还在报错:一般就是Android Studio的问题了(重启可以解决)
点一下,全部重新加载,就行了
-
-
Android Studio快捷键:在查看源码的时候,一行非常长,此时就需要格式化代码了
- 格式化代码:CTRL+ALT+L(需要退出PC端QQ),不然会导致冲突
-
DataBinding使用起来会卡:项目越大越卡,造成性能问题
- 手机是越来越好的,软件是越来越大的
-