开始

声明式UI:最简略的界说;实时的、代交互的预览功用;还有更强的功能和功用。这便是Android官方推出的UI结构——Jetpack Compose。

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」?

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

Text("Hello")

看起来如同只是调用结构函数创立了一个新目标,但这么写就已经显现出一块文字来了。

其他——这企事业并没有创立目标,这个Text()也不是一个结构函数,而是一个一般函数。

Text("Hello")
...
@Composable
fun Text(...) {
    ...
}

一般函数用大写最初干嘛?很简略,为了辨识度。Compose规定了这种大写最初的命名方法,这样咱们就能一眼认出来:哦,这是个Compose的函数——或许用更官方的叫法:这是一个Composable。

到这儿有人或许就会想:这个Text()它实质上是个什么?是个TextView吗》不是的。刚才我说的过一次,Compose的烘托机制、布局机制、接触机制全都是新鞋的,所以这个Text()的底层不是TextView,也不是任何一个原生控件,而是直接调用了更下层的绘制API,也便是Canvas那一套东西。同理,Compose里的各个组件,都是独立的新完成。

好持续说。一个函数调用是一个组件;两个函数调用便是两个组件;

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 mutbaleStateOf("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已经是一种趋势了。

版权声明

本文首发于:声明式 UI?Android 官方怒推的 Jetpack Compose 究竟是什么

微信大众号:扔物线