DataBinding实现双向绑定,达到数据驱动UI效果(Java版本)

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
        
  • 工程结构:

    image-20211218160634554

  • 编写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文件的最外标签上,点击第一个提示

        image-20220302134648924

      • 点击后的效果:将原式布局(activity_main.xml拆分成了两部分)

        image-20220302134719151

  • 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鼠标左键),发现直接都将绘制的布局文件自动全选

          image-20220302142835568

      • 废除原有的setContentView语句

         //        setContentView(R.layout.activity_main);
        
        • 实际上还是在内部调用了的

          image-20220302143238048

          image-20220302143307420

          image-20220302143411006

    • 模拟Model数据:假设是从网络上过来的json数据,解析成了JavaBean

      • 示意图:

      图片.png

      • 为什么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>
        
    • 真机测试一波:

      image-20220302145914739

  • 使用线程自动触发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的问题了(重启可以解决)

        image-20220302150353710

        点一下,全部重新加载,就行了

        image-20220302150418254

    • Android Studio快捷键:在查看源码的时候,一行非常长,此时就需要格式化代码了

      • 格式化代码:CTRL+ALT+L(需要退出PC端QQ),不然会导致冲突
    • DataBinding使用起来会卡:项目越大越卡,造成性能问题

      • 手机是越来越好的,软件是越来越大的

猜你喜欢

转载自juejin.im/post/7070401592511430687