阅读 149

如何通过Data Binding提升撸码逼格(基础篇)

原文链接: www.apkbus.com

LZ-Says:有谁能知道所谓矫情的背后,是多少真正的心酸~!!!

图片描述

吐槽下

本文从2018年01月29日 22:41:39,开始动手编写,到今天才结束基础篇,期间经历了太多太多的事儿,有点疲惫了。

不过没关系,一篇博文,打个鸡血,重新开始~

前言

记得应该是在去年,头一次接触到Data Binding这个家伙,内容就感到诧异,这么6的家伙,肿么才知道???

不过,可能是时间紧,项目忙,也一直没能好好学习了解下,更别提真正应用了。

还记得,当年的黄油刀,一经使用,便再也停不下来,当然,针对黄油刀,LZ也专门写了篇博文,有兴趣的可以查看:

Android Study 之 初识ButterKnife(8.5.1)及简单运用

无奈的是,黄油刀,也不过是在LZ重写的一个项目中玩的嗨起,而其余的项目,也是依旧老套路,坑多的都不想填了。。。

前几天,和LZ老伙计聊起来,老伙计对这个Data Binding可谓赞不绝口,而LZ的态度如下:

图片描述

随着越听越不对劲,LZ突然感觉,我插~

图片描述

所以,嘿嘿嘿,就有了今天这篇文章~~~

图片描述

本文目标

基于Data Binding官方文档以及相对应初级到进阶的实例,让你看完本篇即可短时间内掌握Data Binding使用~

关于技术选择

这里LZ插播一条关于个人想法。

关于技术的选择,其实,不必说,每个人都有每个人的选择。

好比当年的Eclipse与现在的Android Studio想比,虽说死丢丢现在屌炸天,但是你也不可能否认当前Eclipse陪伴的岁月,更何况,Eclipse陪伴多少人的Android入门~~~

有人说,新技术虽说屌,但是可行性,稳定性没个保障,好比14年时,培训期间使用SSH,可真正面试的时候,公司大部分使用的Struts 1,在虚心请教之后,才了解到Struts 2拥有一个漏洞,但是也不可因为这个去说Struts 2并不出色。

同样,新技术的出现,可谓代表新的一波技术到来,同样我们可以在到来的过程中,学习新技术的思想,从而加强我们底层的韵味~~~

所以,新技术有新技术的爽,老技术有老技术的韵味,而关于如何抉择,还是要看大家~~~

不过,多了解,总归没有坏处,思维的拓宽所带来的益处可不是一丁半点~~~

在此引用大牛一句话:

任何一种技术的出现都是为了解决问题的,而不是为了创造出其他的问题,或者在解决一个问题的同时创造了另外的问题。

用什么技术不是目的,解决问题才是目的。

Data Binding简介

Data Binding,出来也有几年了,随着Beta版本到Release版本,直到现在,Data Binding确实为我们带来了很多让人眼前一亮的东西,But,郁闷的是,LZ如今才是真正知道从而学习,并且希望能在项目中真正应用。

希望在本文结束后,我和各位小伙伴能对此神器爱不释手~~~

首先来说,Data Binding是个什么鬼?鉴名其意,数据绑定,是谷歌对MVVM(Model-View-ViewModel)在我们Android上贴心的实现~~~

优势

  • 效率(性能)高。无侵入式,无反射;

  • 节省大量代码,提高开发效率。例如频繁出现的findViewById;

  • 功能强大,支持内容较为广泛,这点还是有待挖掘啊~

劣势

  • 增加编译出的 apk 文件的类数量和方法数量;

  • 。。。貌似还有点,但是问题不大。。。

使用前提了解

生成规则

  • Binding工具类生成规则:
    默认以布局命名规则生成activity_main.xml,例如布局文件名为:activity_main,则对应生成则为:ActivityMainBinding。

  • 控件ID生成规则:
    默认已驼峰命名法,例如控件ID为:user_id,则后续使用binding实例化点userId即可。

使用

使用之前,需要在Android Studio中build.gradle配置开启Data Binding,具体如下:

    dataBinding {
        enabled = true
    }复制代码

一、改造布局,使其支持Data Binding

Android Studio 3.0 之后默认布局如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    android:layout_height="match_parent">

</android.support.constraint.ConstraintLayout>复制代码

而我们只需要将layout标签替换为根布局标签即可,剩下保持不变,如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.constraint.ConstraintLayout>
</layout>复制代码

Ok,到此完成改造第一步,下面进行具体操作过程:

二、数据绑定

一起来看下效果:

图片描述

二一: Activity中更新UI

编写布局:

<?xml version="1.0" encoding="utf-8"?><!-- 使用Data Binding 布局需要使用layout包裹 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.hlq.databindingdemo.activity.BasisUsageActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text="Activity中更新UI"
            android:textSize="16sp" />

        <EditText
            android:id="@+id/userName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入姓名" />

        <EditText
            android:id="@+id/userAge"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入年龄" />

        <TextView
            android:id="@+id/userAddress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="15dp"
            android:textColor="#fff"
            android:textSize="16sp" />

    </LinearLayout>
</layout>复制代码

Activity设置:

    // 实例化布局 初始化Binding
    ActivityBasisUsageBinding basisUsageBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_basis_usage);
    // 设置值
    basisUsageBinding.userName.setText("静心Study");
    basisUsageBinding.userAge.setText("22");
    basisUsageBinding.userAddress.setText("大好河山,张家口");复制代码

显示结果为:

图片描述

这里大家注意下:DataBindingUtil.setContentView(this, R.layout.activity_basis_usage);

我们通过拿到这个实例去操作后面的一些操作,具体关键待下一篇从源码角度分析查看。

二二: XML中直接更新UI

这里需要注意:

  • Data Binding中引入了命名空间
    具体对应的操作应为:使用< data >块,假设现在你想使用UserBean,则需要使用< variable >标签引入UserBean实体即可,具体操作如下:

改造布局:

<?xml version="1.0" encoding="utf-8"?><!-- 使用Data Binding 布局需要使用layout包裹 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.hlq.databindingdemo.bean.UserBean" />

    </data>

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.hlq.databindingdemo.activity.BasisUsageActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text="Activity中更新UI"
            android:textSize="16sp" />

        <EditText
            android:id="@+id/userName"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入姓名" />

        <EditText
            android:id="@+id/userAge"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入年龄" />

        <TextView
            android:id="@+id/userAddress"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="15dp"
            android:textColor="#fff"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text="XML中直接更新UI"
            android:textSize="16sp" />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入姓名"
            android:text="@{user.userName}" />

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入年龄"
            android:text="@{user.userAge}" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="15dp"
            android:text='@{"地址:"+user.userAddress}'
            android:textColor="#fff"
            android:textSize="16sp" />

    </LinearLayout>
</layout>复制代码

Activity中初始化实体类:

    UserBean userBean = new UserBean("HLQ_Struggle", "XML绑定年龄:22", "XML直接绑定地址");
    basisUsageBinding.setUser(userBean);复制代码

显示结果为:

图片描述

二三: 参数为空处理

这里不得不提一嘴,还记得Android中接收后台返回实体类,多少难免会有一些空值,而我们在不注意的时候直接使用则会引发空指针这个异常,那么使用Data Binding后,我们则无需关注这块内容。

这时候小伙伴就会说了,那如果我的实体压根就是空,直接使用不会异常么?

如果使用Data Binding,如果数据出现空时,则默认显示当前类型的默认值。

引入布局:

<?xml version="1.0" encoding="utf-8"?><!-- 使用Data Binding 布局需要使用layout包裹 -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="stu"
            type="com.hlq.databindingdemo.bean.StudentBean" />
    </data>

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.hlq.databindingdemo.activity.BasisUsageActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text="参数为空处理"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text='@{"String参数为空:"+stu.stuName}'
            android:textColor="#fff"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text='@{"int参数为空:"+stu.stuAge}'
            android:textColor="#fff"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="15dp"
            android:text='@{"boolean参数为空:"+stu.stuIsPer}'
            android:textColor="#fff"
            android:textSize="16sp" />

    </LinearLayout>
</layout>复制代码

Activity直接设置值:

basisUsageBinding.setStu(new StudentBean());复制代码

显示结果为:

图片描述

三、设置事件

当配合使用Data Binding后,我们原有的一些设置事件与我们之前的使用方式有有一些出入,具体如下:

  • 首先需要定义一个Presenter内部类(当然,如果你想抽取出去也无可厚非);

  • 接着,bind提供我们如下几种方式使用事件:

    • 复写原有系统提供事件,例如:onClick等;

      • 直接绑定内部方法;

      • Lambda 表达式。

  • 最后,我们需要给Bind设置setPersenter或者setVariable。

上面简单列举了下理论,下面着重说明一些关键:

首先需要在XML中引入命名空间,也就是我们定义的事件内部类:

    <variable
        name="persenter"
        type="com.hlq.databindingdemo.activity.BindListenerActivity.Presenter" />复制代码

其中,调用事件的方式有俩种,分别如下:

  • android:onClick="@{persenter.onClick}"

  • android:onClick="@{persenter::onClick}" 官方更为推崇使用这种。

而Lambda 表达式使用方式如下:

  • android:onClick="@{() -> persenter.getUserClick()}"

  • android:onClick="@{() -> persenter.showUserName(user)}"

而最后,关于我们初始化我们这个事件内部类,方式同样有俩种,如下:

  • mBindListenerBinding.setPersenter(new Presenter());

  • mBindListenerBinding.setVariable(BR.persenter,new Persenter());

那么到此,开始我们愉快的撸码之旅吧~

图片描述

首先,在我们的Activity中定义事件类以及进行初始化:

package com.hlq.databindingdemo.activity;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.hlq.databindingdemo.R;
import com.hlq.databindingdemo.bean.UserBean;
import com.hlq.databindingdemo.databinding.ActivityBindListenerBinding;

/**
 * author : HLQ
 * e-mail : 925954424@qq.com
 * time   : 2018/02/13
 * desc   : 事件绑定
 * version: 1.0
 */
public class BindListenerActivity extends AppCompatActivity {

    private BindListenerActivity selfActivity = BindListenerActivity.this;

    private ActivityBindListenerBinding mBindListenerBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mBindListenerBinding =
                DataBindingUtil.setContentView(this, R.layout.activity_bind_listener);
        mBindListenerBinding.setPersenter(new Presenter());
//        mBindListenerBinding.setVariable(BR.persenter,new Persenter());

        mBindListenerBinding.setUser(new UserBean("贺大大", "22", "China"));
    }

    public class Presenter {
        public void onClick(View view) {
            Toast.makeText(selfActivity, "点到了哦~", Toast.LENGTH_SHORT).show();
        }

        public void getUserClick() {
            Toast.makeText(selfActivity, "有人在呼唤你~", Toast.LENGTH_SHORT).show();
        }

        public void showUserName(UserBean userBean) {
            Toast.makeText(selfActivity, "看看是谁:" + userBean.getUserName(), Toast.LENGTH_SHORT).show();
        }
    }

}
复制代码

接着,布局文件引入命名空间,进行相关调用:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.hlq.databindingdemo.bean.UserBean" />

        <variable
            name="persenter"
            type="com.hlq.databindingdemo.activity.BindListenerActivity.Presenter" />
    </data>

    <android.support.v7.widget.LinearLayoutCompat xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="15dp"
        tools:context="com.hlq.databindingdemo.activity.BindListenerActivity">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{persenter.onClick}"
            android:text="测试监听器绑定." />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{persenter::onClick}"
            android:text="测试监听器绑定:" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> persenter.getUserClick()}"
            android:text="测试方法绑定" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> persenter.showUserName(user)}"
            android:text="测试方法绑定传值" />

    </android.support.v7.widget.LinearLayoutCompat>
</layout>复制代码

最后,一起来看结果如何?

图片描述

四、一起来探表达式

表达式,在我们的Data Binding中也有所体现,下面简单描述一些常用的表达式。

表达式 Desc
< > = 小于 大于 等于
? : 三元运算符
?? 三元运算符

首先,我们实例化数据源:

    ActivityExpressionBinding binding =
            DataBindingUtil.setContentView(this, R.layout.activity_expression);
    binding.setUser(new UserBean("贺大大", "22", "河北", 0));复制代码

接下来,我们开始编写xml玩物:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="user"
            type="com.hlq.databindingdemo.bean.UserBean" />

        <variable
            name="view"
            type="android.view.View" />
    </data>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.hlq.databindingdemo.activity.ExpressionActivity">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                style="@style/contentStyle"
                android:text="一起来探表达式" />

            <TextView
                style="@style/titleStyle"
                android:text="模拟数据:姓名:贺大大 年龄:22 地址:河北 个数:0" />

            <TextView
                style="@style/titleStyle"
                android:text="一、三元运算符(用户名不等于空则正常显示,反之显示贺大宝)" />

            <TextView
                style="@style/contentStyle"
                android:text='@{user.userName!=null?user.userName:"贺大宝"}' />

            <TextView
                style="@style/titleStyle"
                android:text="二、三元运算符替代者(相比第一条,简单省事儿)" />

            <TextView
                style="@style/contentStyle"
                android:text='@{user.userName??"贺大宝"}' />

            <TextView
                style="@style/titleStyle"
                android:text="三、比较(例如比较个数等于0显示反之隐藏)" />

            <TextView
                style="@style/contentStyle"
                android:text="看看我是被隐藏还是显示呢"
                android:visibility="@{user.userCount==0?view.VISIBLE:view.GONE}" />
        </LinearLayout>

    </ScrollView>
</layout>复制代码

这里需要注意一点,Data Binding中如果使用View,需要将View引入一下,这里作用类似引入命名空间一样。

    <variable
        name="view"
        type="android.view.View" />复制代码

下面我们一起来看下效果:

图片描述

五、ViewStub Study

ViewStub,这个小神器,这个在Data Binding也有所变换,如下:

首先编写一个简单的layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        style="@style/contentStyle"
        android:text="今天上班了" />

    <TextView
        style="@style/titleStyle"
        android:text="难受想哭,心里默默MMP" />

</LinearLayout>复制代码

效果如下:

图片描述

编写布局:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.hlq.databindingdemo.activity.ViewStubActivity">

        <TextView
            style="@style/contentStyle"
            android:text="ViewStub示例" />

        <ViewStub
            android:id="@+id/viewStub"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout="@layout/layout_view_stub" />

    </LinearLayout>
</layout>复制代码

Activity进行初始化:

    ActivityViewStubBinding viewStubBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_view_stub);
    viewStubBinding.viewStub.getViewStub().inflate();复制代码

一起来看效果:

图片描述

六、include Study

include,使用时,与上面ViewStub引用类似,那么如果现在需要给include内容设置数据,又该如何操作呢?

简单过一遍代码,之后进行简单总结。

首先,编写一个layout,由于我们需要将数据源也传递过去,所以这里需要提前声明下实体。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.hlq.databindingdemo.bean.UserBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            style="@style/titleStyle"
            android:text='@{"姓名:"+user.userName}' />

        <TextView
            style="@style/titleStyle"
            android:text='@{"年龄:"+user.userAge}' />

        <TextView
            style="@style/titleStyle"
            android:text='@{"地址:"+user.userAddress}' />
    </LinearLayout>
</layout>复制代码

Activity中初始化数据源:

    ActivityIncludeBinding binding =
            DataBindingUtil.setContentView(this, R.layout.activity_include);
    binding.setUser(new UserBean("静心Study", "22", "帝都"));复制代码

在所对应的Activity layout中通过bind绑定数据源即可:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="user"
            type="com.hlq.databindingdemo.bean.UserBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.hlq.databindingdemo.activity.IncludeActivity">

        <TextView
            style="@style/contentStyle"
            android:text="include引用布局示例" />

        <include
            layout="@layout/include_item_layout"
            bind:user="@{user}" />

    </LinearLayout>
</layout>复制代码

那么我们来稍稍的总结一下吧:

首先,使用时,需要引用对应的实体类,也就是引入命名空间。其次,如果如有include内容需要传递数据源时,那么这个时候仅仅需要在include标签通过使用bind:数据源别名="@{数据源别名}"即可,那么如果需要多个include时,同理即可,下面举个例子:
    <include
        layout="@layout/include_item_layout"
        bind:user="@{user}"
        bind:bean="@{bean}"/>复制代码

实例化如下:

    ActivityIncludeBinding binding =
            DataBindingUtil.setContentView(this, R.layout.activity_include);
    binding.setUser(new UserBean("静心Study", "22", "帝都"));
    binding.setBean(new ClassBean("哇哈哈"));复制代码

效果如下:

图片描述

到此为止,Data Binding基础篇截稿~

GitHub查看地址

github.com/HLQ-Struggl…

参考资料

  1. Data Binding官方文档

  2. 从零开始的Android新项目7 - Data Binding入门篇

  3. 从零开始的Android新项目8 - Data Binding高级篇