鸡你太美之 Kotlin 和 Databinding

栏目: IOS · Android · 发布时间: 5年前

内容简介:编不下去了... 其实就是之前的一些项目采用了 Databinding,后面考虑用 Kotlin 重新写一遍,特此记录过程中一些比较 tricky 的点。本文假设读者已经具备一定的 databinding 和 Kotlin 语法基础。databinding 的引入需要在 app 模块下的

编不下去了... 其实就是之前的一些项目采用了 Databinding,后面考虑用 Kotlin 重新写一遍,特此记录过程中一些比较 tricky 的点。

本文假设读者已经具备一定的 databinding 和 Kotlin 语法基础。

引入

databinding 的引入需要在 app 模块下的 build.gradle 中加入:

android {
    dataBinding.enabled true
    ...
}
复制代码

同时为了 Kotlin 能够正常使用 databinding 相关的注解,需要同时在 build.gradle 中引入相应插件:

apply plugin: 'kotlin-kapt'

android {
    dataBinding.enabled true
    ...
}
复制代码

BindingAdapter

我们知道,在 databinding 中,我们经常会使用 BindingAdapter 来为 widget 添加更多的自定义属性,从而以更丰富的手段来将数据绑定到 widget 上。

一般地,比如我们在为 View 设置可见性时,以 Java 编写的话,会有如下的代码:

public class MyBindingAdapter() {
    @BindingAdpater("visible")
    public static setVisible(View v, boolean visible) {
        v.setVisibility(visible ? View.VISIBLE : View.GONE);
    }
}
复制代码

用 Koltin 编写的话,要省事许多:

@BindingAdapter("visible")
fun setVisible(v: View?, visible: Boolean) {
    v?.visibility = if (visible) View.VISIBLE else View.GONE
}
复制代码

或者可以直接将该方法作为控件的扩展方法:

@BindingAdapter("visible")
fun View.setVisible(visible: Boolean) {
    this.visibility = if (visible) View.VISIBLE else View.GONE
}
复制代码

Kotlin 编写的话,不需要多余的类,也不需要多余的静态声明,同时更具有可读性。

ObservableField

我们知道,对于绑定到 layout 中的数据,在更新之后,需要调用 notifyPropertyChanged 来触发 UI 更新。

比如下面这样一个 layout,引用了两个数据字段:

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

    <data>
         <variable
            name="id"
            type="Integer" />

        <variable
            name="name"
            type="String" />
    </data>
    
    <FrameLayout></FrameLayout>
</layout>
复制代码

对应的 Model 的实现,Java 形式如下:

public class UserVM extends BaseObservable {

    private int id = 0;
    private String name = "";

    @Bindable
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
        notifyPropertyChanged(BR.id);
    }

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

}
复制代码

Kotlin 利用 Property 的语法糖可以稍微简洁一些:

class UserVM : BaseObservable() {

    @get:Bindable
    var id = 0
        set(id) {
            field = id
            notifyPropertyChanged(BR.id)
        }

    @get:Bindable
    var name = ""
        set(name) {
            field = name
            notifyPropertyChanged(BR.name)
        }

}
复制代码

但很多时候,我们不想做到单独每个字段都在 <data></data> 中去声明一次,更多地,我们想绑定 UserVM 即可,其字段可以用诸如 @{user.id}@{user.name} 等表示:

<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
         <variable
            name="user"
            type="com.packagename.appname.vm.UserVM" />
    </data>
    
    <TextView
        android:width="wrap_content"
        android:height="wrap_content"
        android:text="@{user.name}"/>
        
</layout>
复制代码

这样一来,notify UserVM 更新就不能让 layout 中使用到 name 字段的 UI 更新,所以在这种场景下我们一般会使用 ObservableField 来单独对字段小粒度实现:

public class UserVM extends BaseObservable {

    public ObservableField<Integer> id = new ObservableField<>(0);

    public ObservableField<String> name = new ObservableField<>("");

}
复制代码

只要直接修改 ObservableField 的值,就可以触发 UI 更新:

id.set(10);
name.set("Bob");
复制代码

上面的实现以 Kotlin 编写的话,如下:

class UserVM : BaseObservable() {

    var id = ObservableField(0)

    var name = ObservableField("")

}
复制代码

修改的话,跟 Java 类似:

id.set(10)
name.set("Bob")
复制代码

这时候有人问了,“啊那这样 layout 那边拿到的不是 ObservableField 类型的吗,那是不是 widget 在使用的时候,是不是会自动执行一次 toString 将对象转换成字符串,那这样返回的就是对象的 hash 值了,会有问题的。”

其实不然,databinding 在这块对 ObservableField 做过处理,存在 boxunbox 的行为,就有点像 Integer 对象和 int 一样,读者有兴趣的话,可以自行再去深入了解下。

ObservableField 优化

留意到,每个字段都得写长长的 ObservableField 和无谓的默认值,这是一个可优化的地方。

创建 ComOb 类,继承 ObservableField ,同时实现几个较常用的类型:

open class ComOb<T>(defaul : T?) : ObservableField<T>() {

    class String(default: kotlin.String = "") : ComOb<kotlin.String>(default)
    
    class Int(default: kotlin.Int = 0) : ComOb<kotlin.String>(default)
    
    class Boolean(default: kotlin.Boolean = false) : ComOb<kotlin.Boolean>(default)
    
}
复制代码

这样的话,我们在声明时,就可以更加简洁:

class UserVM : BaseObservable() {
    var id = ComOb.Int()
    var name = ComOb.String()
}
复制代码

同时,我们留意到,Kotlin 对于 ObservableField 的值的修改方式,还是不够 Kotlin 化 。我们知道,诸如 Java 中的 view.setVisibility(xxx) 在 Kotlin 中已经被统一改造为 view.visibility = xxx 。Kotlin 的设计是更偏向于属性驱动,而非事件驱动。 //个人理解,不喜勿喷 :)

那么,我们有没有改造的可能?是有的,借助 Kotlin Property 的特性,我们可以做到:

open class ComOb<T>(defaul : T?) : ObservableField<T>() {

    var value: T? = default
        set(value) {
            field = value
            this.set(value)     //注意,这个this.set才是ObservableField原有的方法,即我们之前直接调用的方法
        }

    class String(default: kotlin.String = "") : ComOb<kotlin.String>(default)
    
    class Int(default: kotlin.Int = 0) : ComOb<kotlin.String>(default)
    
    class Boolean(default: kotlin.Boolean = false) : ComOb<kotlin.Boolean>(default)
    
}
复制代码

这里的做法有点 tricky,是在 ComOb 中制造了一个“傀儡”属性 value ,然后将其以 property 的形式暴露出去。

这样一来,我们就可以通过修改 value 来修改 ObservableField 的内部数值(以修改 value 的间接方式):

class UserVM : BaseObservable() {
    var id = ComOb.Int()
    var name = ComOb.String()
    
    fun foo() {
        id.value = 10   //相当于 id.set(10)
        name.value = "Bob"  //相当于 name.set("Bob")
    }
}
复制代码

总结

话就说这么多了,好久没写文章,后续希望能够多将实际开发和优化中遇到的问题和解决办法分享给大家。

嗯?所以跟鸡你太美有什么关系???

———————————————

个人博客:mindjet.github.io

最近在 Github 上搞事的项目:

  • LiteWeather [一款用 Kotlin 编写,基于 MD 风格的轻量天气 App] ,对使用 Kotlin 进行实际开发感兴趣的同学可以看看,项目中会使用到 Kotlin 的委托机制、扩展机制和各种新奇的玩意。
  • Oros [闲来无事做的守望先锋英雄展示 App]
  • LiteReader [一款基于 MD 的极轻阅读 App,提供知乎日报、豆瓣电影等资源] ,项目主要使用了 MVVM 设计模式,界面遵循 Material Design 规范,提供轻量的阅读体验。

欢迎 star(唱)/ fork(跳)/ issue(rap)/ PR(篮球)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图解深度学习

图解深度学习

[日] 山下隆义 / 张弥 / 人民邮电出版社 / 2018-5 / 59.00元

本书从深度学习的发展历程讲起,以丰富的图例从理论和实践两个层面介绍了深度学习的各种方法,以及深度学习在图像识别等领域的应用案例。内容涉及神经网络、卷积神经网络、受限玻尔兹曼机、自编码器、泛化能力的提高等。此外,还介绍了包括Theano、Pylearn2、Caffe、DIGITS、Chainer 和TensorFlow 在内的深度学习工具的安装和使用方法。 本书图例丰富,清晰直观,适合所有对深......一起来看看 《图解深度学习》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具