声明式 UI?Android 官方怒推的 Jetpack Compose 究竟是什么

视频先行

若是你方便看视频,直接去 哔哩哔哩 或者 YouTube 看视频就好,下面的文章就不用看了。若是你不方便看视频,下面是视频内容的脚本整理稿。android

开始

声明式 UI;更简单的自定义;实时的、带交互的预览功能;还有更强的性能和功能。这就是 Android 官方全新推出的 UI 框架——Jetpack Compose。web

你们好,我是扔物线朱凯。算法

2019 年中,Google 在 I/O 大会上公布了 Android 最新的 UI 框架:Jetpack Compose。Compose 能够说是 Android 官方有史以来动做最大的一个库了。它在 2019 年中就公布了,但要到今年也就是 2021 年才会正式发布。这两年的时间 Android 团队在干吗?在开发这个库,在开发 Compose。一个 UI 框架而已,为何要花两年来打造呢?由于 Compose 并非像 RecyclerViewConstraintLayout 这种作了一个或者几个高级的 UI 控件,而是直接抛弃了咱们写了 N 年的 ViewViewGroup 那一套东西,从上到下撸了一整套全新的 UI 框架。直白点说就是,它的渲染机制、布局机制、触摸算法以及 UI 的具体写法,全都是新的。数组

Compose 的写法

Compose 从一出现,最受到官方推崇以及关注者赞赏的就是它实现了声明式 UI,说它比咱们传统写法的「命令式 UI」怎么怎么好——咱们传统的 ViewViewGroup 那一套系统的写法叫「命令式」。可是对于大多数 Android 开发者来讲,咱们的第一个问题就是:什么是「声明式 UI」?markdown

在讲「声明式 UI」以前,咱们先看一下 Compose 的代码长什么样。Compose 是用 Kotlin 来写的,它的每一个控件都是一个函数调用。好比你要显示一块文字,你就这么写:框架

Text("Hello")
复制代码

看起来好像只是调用构造函数建立了一个对象,但这么写就已经能够显示出一块文字来了。ide

另外——这其实也并无建立对象,这个 Text() 也不是一个构造函数,而是一个普通函数。svg

Text("Hello")

...

@Composable
fun Text(...) {
    ...
}
复制代码

普通函数用大写开头干吗?很简单,为了辨识度。Compose 规定了这种大写开头的命名方式,这样咱们就能一眼认出来:哦,这是个 Compose 的函数——或者用更官方的叫法:这是一个 Composable。函数

到这儿有人可能就会想:这个 Text() 它实质上是个什么?是个 TextView 吗?不是的。刚才我说过一次,Compose 的渲染机制、布局机制、触摸机制全都是新写的,因此这个 Text() 的底层不是 TextView,也不是任何一个原生控件,而是直接调用了更下层的绘制 API,也就是 Canvas 那一套东西。同理,Compose 里的各个组件,都是独立的新实现。oop

好继续说。一个函数调用是一个组件;两个函数调用就是两个组件;

Text("Hello")
Image()
复制代码

多个函数组合起来,就是一个完整的界面:

Column {
    Text("Hello")
    Image()
}
复制代码

这,就是 Compose 的写法。看完它的写法,咱们就能够回到刚才的问题:什么是「声明式 UI」?这段代码怎么就「声明式」了?它和咱们一直以来的写法有什么区别?

首先,咱们通常怎么写 UI 的?xml 文件,对吧?好比这个界面,上下排列的一块文字和一个图片,它的等价传统写法是这样的:

<!-- 代码通过必定简化 -->
<LinearLayout>
  <TextView android:text="Hello" />
  <ImageView />
</LinearLayout>
复制代码

一个 LinearLayout,里面包着一个 TextView 和一个 ImageView

看了之后什么感受?大同小异是吧?除了名字换换、格式变变,大致上是同样的。对吧?

那为何左边就叫命令式,右边就叫声明式呢?xml 命令谁了?以及,右边这写法怎么就更优秀了?我为何要学一个看起来并无什么本质区别的新写法来为难本身?

其实所谓「声明式 UI」,指的是你只须要把界面给「声明」出来,而不须要手动更新。关键在于「不须要手动更新」。好比左边这个布局里的 TextView,若是它对应的数据改变了,我要怎么把新的文字更新到它?很简单:findViewById()setText() 对吧?

findViewById()
setText()
复制代码

而若是用 Compose 呢?怎么更新?不用更新。由于 Compose 的界面会随着数据自动更新。

Compose 会对界面中用到的数据自动进行订阅——不论是字符串仍是图像仍是别的什么,Compose 所有可以自动订阅——这样当数据改变的时候,Compose 会直接把新的数据更新到界面。

var text = "Hello"

...

Column {
    Text(text)
    Image()
}
复制代码

这个「自动订阅」的功能很容易使用,你只要在初始化的时候加上一个 by mutableStateOf() ,剩下的全都由 Compose 自动搞定。

var text by mutableStateOf("Hello")

...

Column {
    Text(text)
    Image()
}
复制代码

这个神奇的功能是利用 Kotlin 的 Property Delegation 属性委托来实现的。这也在必定程度上回答了一个问题: 为何 Compose 只能用 Kotlin 写,而不能用 Java?由于它用了大量的 Kotlin 特性,而这些特性用 Java 不能简单实现。注意,虽然 Kotlin 和 Java 是兼容的,Kotlin 能作到的事 Java 也能作到,可是有些东西它「不能简单实现」就约等于不能实现了,由于不实用啊!对吧?因此 Android 自称永远不放弃对 Java 的支持,他们就这么一说,你就这么一听,不要真的就不学 Kotlin,否则会愈来愈难受。你看这 Compose 不是已经在逼着咱们用 Kotlin 了吗?

好拐回来,这就是所谓的「声明式 UI」:你只要声明界面是什么样子,不用手动去更新,由于界面会自动更新。而传统的写法里,数据发生了改变,咱们得手动用 Java 代码或者 Kotlin 代码去把新数据更新到界面。你给出详细的步骤,去命令界面进行更新,这就是所谓的「命令式 UI」。

那么如今咱们再往回拐:传统的 xml 写法和 Compose 的 Kotlin 写法,为何一个是「命令式」,一个是「声明式」?这个问题其实自己就是错的。单单一段 xml 代码并不能称做是命令式 UI。传统写法的「命令式」并不在于 xml 部分,而在于 Java 部分:Java 代码去指挥、去命令界面更新,这才是「命令式」的含义所在;而 Compose 经过订阅机制来自动更新,因此不须要作这种「命令」,因此是「声明式」。

因此你看,不论是声明式仍是命令式,跟 xml 和 Kotlin 是无关的,它们并非语言角度的定义,也不是写法角度的定义,而是——功能角度。一个 UI 框架,若是可让开发者只声明出界面的样子,而不用去写各类界面更新的代码,它就是一个声明式的 UI 框架。换句话说,若是 Android 可让咱们用 xml 写的界面也和数据作关联,让界面自动更新而不须要开发者手写更新代码,那么它就也是声明式 UI。声明式 UI 是一种强大的功能,而不是一种优秀的代码风格。

哎?数据和界面作关联,界面跟着数据自动更新,这不就是数据绑定吗?Android 已经有这样的官方库了啊!就叫 Data Binding,是吧?我用它不就得了,为何要费这么大劲去用 Compose 呢?

首先,对!Data Binding 和 Compose 本质上都是经过界面对数据进行订阅来实现了界面的自动更新,但!它们是有关键区别的。区别就在于,Data Binding 经过数据更新的只能是界面元素的值,而 Compose 能够更新界面中的任何内容,包括界面的结构。好比你用一个 Boolean 类型的变量控制界面中某个元素是否显示,

var text = ...
var showImage = ...
Column {
    Text(text)
    if (showImage) {
        Image()
    }
}
复制代码

当你把变量的值从 true 变成 false 的时候,

var text = ...
var showImage = ...
Column {
    Text(text)
    if (showImage) {
        Image()
    }
}

...

showImage = false
复制代码

这个元素会从界面中彻底消失,就像历来没有出现过同样,而不是用 setVisibility(GONE) 这种方式从视觉上隐藏。这两种策略看起来好像区别不大,那是由于我举的例子简单,实际上这是一种机制的改变,而这种机制的改变给界面开发带来的灵活性和性能的提高是很是大的。你想一下,是否是?

总结

因此「声明式 UI」还真的不是个噱头,它让 Compose 比传统的 UI 系统强大得多。并且如今除了 Android 的 Compose 以外,iOS 的 SwiftUI 以及跨平台的 Flutter 也都是声明式的。声明式 UI 已是一种趋势了。

固然了无论怎么说,客观地讲,Compose 确实是一套比较难学的东西,由于它毕竟太新也太大了,它是一个完整的、全新的框架,确实让不少人感受「学不动」,这也是个事实。那怎么办呢?学不动怎么办呢?

个人建议是——

学。

相关文章
相关标签/搜索