SmileWeather之DataBinding篇

SmileWeather之DataBinding篇

前言

本系列文章将以SmileWeather为实例来记录一下如何开发一款简单的APP,麻雀虽小五脏俱全。项目地址

基本介绍

DataBinding也是属于Jetpack中的一部分,Google开发文档中是这样介绍的:

数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化的)将布局中的界面组件绑定到应用中的数据源。
简单来讲就是使用一些表达式来将数据与控件进行绑定,当然这就分了单向绑定和双向绑定。顾名思义单向绑定是由数据去驱动UI,双向绑定则是数据和UI可以互相驱动映射。接下来就简单的记录一下两者在项目中如何使用,和日常开发过程中会碰到哪些bug。

集成到项目

在应用模块的 build.gradle 文件中添加 dataBinding 元素

android {
        ...
        dataBinding {
            enabled = true
        }
    }
    

然后在布局文件中添加一些标签,代表这个XML是需要拿来绑定的,这些标签是最外层的。data里面我们可以引入一些包(import)和添加我们需要的一些变量(variable)。

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:bind="http://schemas.android.com/apk/res-auto"
    >
    <data>
        <import type="com.smile.weather.entity.NowEntity"/>
        <import type="android.view.View"/>

        <variable
            name="nowData"
            type="NowEntity" />
        <variable
            name="loadByCode"
            type="java.lang.Integer" />
        <variable
            name="cityName"
            type="java.lang.String" />
        <variable
            name="isLocate"
            type="java.lang.Boolean" />
    </data>
    ....
    
    </layout>

单向绑定

接下来我们要一个TextView显示城市名,关键地方就是 android:text="@{cityName}"
这样我们的数据就与TextView绑定了。

<TextView
        android:id="@+id/detail_head_address_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/detail_head_temp_content_tv"
        android:textColor="@color/white"
        android:textSize="30sp"
        android:text="@{cityName}"
        android:layout_marginLeft="10dp"
        app:layout_constraintLeft_toRightOf="@+id/detail_head_locate_img"
      />

这就完了?
不存在的,我们还可以通过数据控制View的显示和隐藏以及响应点击事件,ImageView还可以加载网络图片…
DataBinding还支持很多表达式和运算符,下面列举一下:

  • 算术运算符 + - / * %
  • 字符串连接运算符 +
  • 逻辑运算符 && ||
  • 二元运算符 & | ^
  • 一元运算符 + - ! ~
  • 移位运算符 >> >>> <<
  • 比较运算符 == > < >= <=(请注意,< 需要转义为 <)
  • instanceof
  • 分组运算符 ()
  • 字面量运算符 - 字符、字符串、数字、null
  • 类型转换
  • 方法调用
  • 字段访问
  • 数组访问 []
  • 三元运算符 ?:

接下来我们通过三元运算符来实现一个控制View的显示和隐藏

 <ImageView
        android:id="@+id/detail_head_locate_img"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:src="@drawable/icon_local"
        android:layout_marginLeft="30dp"
        android:visibility="@{isLocate ? View.VISIBLE : View.INVISIBLE }"
        app:layout_constraintTop_toTopOf="@id/detail_head_address_tv"
        app:layout_constraintBottom_toBottomOf="@id/detail_head_address_tv"
        app:layout_constraintLeft_toLeftOf="parent"/>

这些还远远不够,我们还可以自定义一些方法来实现我们的需要,比如说,我想要一个ImageView根据天气code来加载不同的icon,这就需要我们来进行自定义方法了。
关键字 @BindingAdapter("name")
代码如下:

 @BindingAdapter("loadByCode")
    @JvmStatic
    fun loadImageByCode(imageView: ImageView, code: String?) {
        if (code != null) {
            imageView.setImageResource(IconUtils.getSmallIcon(code.toInt()!!))
        }
    }
    
     <ImageView
            android:id="@+id/item_f_icon_day_img"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/s_unknown"
            bind:loadByCode="@{dailyForecast.iconDay}"
            app:layout_constraintLeft_toLeftOf="parent"
            android:layout_marginLeft="10dp"
            app:layout_constraintTop_toBottomOf="@id/item_f_time_tv" />

这里有个踩坑记录一下,由于Kotlin没有static的特性和null-safe检查所以需要添加@JvmStatic注解并且在参数后面加?规避空指针异常,记得引入命名空间。
xmlns:bind="http://schemas.android.com/apk/res-auto"

双向绑定

顾名思义就是View层对数据进行了操作会改变数据源里面的内容,数据源改变了会影响到View层,单向绑定和双向绑定表达方式很像,就是"@{}“改成了”@={}"。项目中的一个地方就使用到了,就是搜索城市的时候,我们输入了城市名后不需要我们再去手动获取EditText里面的内容,双向绑定就是数据驱动视图,视图也能驱动数据。

      <EditText
            android:id="@+id/search_input_et"
            android:layout_width="0dp"
            android:layout_height="40dp"
            app:layout_constraintLeft_toRightOf="@id/search_return_img"
            app:layout_constraintTop_toTopOf="@id/search_return_img"
            app:layout_constraintBottom_toBottomOf="@id/search_return_img"
            app:layout_constraintRight_toLeftOf="@id/search_img"
            android:background="@drawable/shape_search_input"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:text="@={ viewModel.mInputCity }"
            android:maxLines="1"
            />

好了,开始进阶双向绑定,就是SwipeRefreshLayout的使用,在项目中有使用到下拉刷新更新数据,我们知道可以通过setRefreshing(boolean b)来控制SwipeRefreshLayout的状态,关闭和开启,所以可以使用一个LiveData来驱动视图(SwipeRefreshLayout),重点是如何通过View来驱动数据。
其实只要我们获取到View的状态就可以来驱动LiveData数据。
代码先列出来,慢慢讲解

object SwipeRefreshLayoutHandler {

    @BindingAdapter("app:bind_swipeRefreshLayout_refreshing")
    @JvmStatic
    fun setSwipeRefreshLayoutRefreshing(
        swipeRefreshLayout: SwipeRefreshLayout,
        newValue: Boolean
    ) {
        if (swipeRefreshLayout.isRefreshing != newValue)
            swipeRefreshLayout.isRefreshing = newValue!!
    }

    @JvmStatic
    @InverseBindingAdapter(
        attribute = "app:bind_swipeRefreshLayout_refreshing",
        event = "app:bind_swipeRefreshLayout_refreshingAttrChanged"
    )
    fun isSwipeRefreshLayoutRefreshing(swipeRefreshLayout: SwipeRefreshLayout): Boolean? =
        swipeRefreshLayout.isRefreshing


    @BindingAdapter(
        "app:bind_swipeRefreshLayout_refreshingAttrChanged",
        requireAll = false
    )
    @JvmStatic
    fun setOnRefreshListener(
        swipeRefreshLayout: SwipeRefreshLayout,
        bindingListener: InverseBindingListener?
    ) {
        if (bindingListener != null)
            swipeRefreshLayout.setOnRefreshListener {
                bindingListener.onChange()
            }
    }
}
  • 第一个方法很容易理解就是给view使用的,将view与数据进行绑定。
  • 第二个方法应该后面再讲,他主要是用来桥接第一个和第三个方法,所以我就放在中间了。
  • 第三个方法是拿来观察view的状态变化,SwipeRefreshLayout每次的刷新状态都是通过这个来进行监听,然后告诉InverseBindingListener去通知DataBinding。
  • 开始就说了第二个方法是拿来桥接的有没有看到
event = "app:bind_swipeRefreshLayout_refreshingAttrChanged"

与第三个方法的注解内容一样。
整个流程就是用户下拉刷新的时候 InverseBindingListener通知DataBinding,LiveData就会从SwipeRefreshLayout.isRefreshing中获取状态,并且对数据进行同步。
对了,这个还有一个关键点,就是view驱动数据,数据又驱动view就成了死循环了,所以我们需要对状态进行一个判断,状态改变了才会去驱动,第一个方法就有做了处理。

if (swipeRefreshLayout.isRefreshing != newValue)
            swipeRefreshLayout.isRefreshing = newValue!!

结尾

差不多记录了一些DataBinding的基本用法,当然这都是一些比较简单的操作,日常开发过程中还会有更加复杂的业务逻辑,所以需要后面还得继续探索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值