声明性的UI框架正在稳步取代其当务之急。十年前,网络和反应一直在开创这一运动。 Google Cross Platform Framework Flutter(第一个稳定版本出现在2018年底)是通过设计声明的。苹果在2019年WWDC期间引入了Swiftui。Android有点晚了。直到2020年夏天才向公众发布第一个alpha版本。
在MacOS,Windows和Linux上,情况有些不同。只有Apple严格在用于Mac应用程序的工具,框架和编程语言上。其他两个平台让开发人员决定。这引导并仍然导致风格混合。喜欢或讨厌这是一种口味问题。重要的是:大多数已建立的桌面框架可以追溯到很多年前,甚至可以追溯到他们运行的操作系统的早期。换句话说:他们的建筑已经有几十年了。相反,这不一定是不好的。它证明了基本的思想和概念是合理的,灵活的和可持续的。
然而,如果这是真的,为什么要有新的范式?更重要的是,桌面仍然相关吗?自从网络和浏览器变得无处不在以来,它已被宣布死亡多次。我们可以专注于一个平台,即浏览器,而不是为Windows,MacOS和Linux编写应用程序。当然,使用Java,我们可以编写在不同平台上运行的图形用户界面。但是AWT过于简单,至少在一开始就摆动,还不够快。当然,在某个时间点,我们学会了如何使用Swing编写快速,美丽,优雅的跨平台应用程序,但是到那时,网络已经 - 老实说 - 已经胜利了。因此,Adobe Air,Javafx等人没有机会。那么,让我重申我的问题,为什么还是要开发桌面?好吧,我们确实想保留一些本地的东西,这可能是由于安全性或性能注意事项或简单缺乏网络访问所致。更重要的是:不受浏览器的链条的约束,通常会使开发变得更加容易。本地文件访问,有人吗?最后,重新发现一个知名的平台可能会令人鼓舞。
命令与声明性
大多数流行的桌面UI框架都是面向对象的,基于组件的。在运行时,应用程序的用户界面由一个或多个对象树组成。每个对象代表UI元素或一个容器,它也取决于用户界面的一部分(例如,窗口,菜单栏或对话框)。实际上,所有框架都知道安排和大小的孩子的特殊容器。为了添加,删除或更改用户界面的部分,通过添加或删除分支或修改叶子(对象),在运行时,组件(对象)树会更改。这就是为什么这样的框架被称为命令的原因。任何更改,无论多么小或微不足道,都必须明确编码。
问题:用户界面获得的越复杂,进行右更改就越复杂。那是因为最命令的框架的UI元素过着自己的生活。现在是提醒您1990年代后期组件的合适时机。但这本身就是一篇文章。因此,让我们举一个例子。文本输入字段不仅可以监视键盘,而且还知道剪切,复制和粘贴操作。它也可以验证和过滤输入。文本字段在属性中存储其状态(至少是当前文本和光标位置),实际上必须在应用程序源代码中访问。
这就是原因:仅仅因为变量的内容更改,文本字段没有神奇地更新。同样,输入或删除字符不一定会更改相应的变量。组件属性和变量都必须同步,并且要这样做是开发人员的工作。每个组件框架都有其喜欢的工具,技术或机制来实现这一目标,例如回调或绑定。但是程序员必须使用这些工具。事实证明,将两个组件属性保持OND程序变量同步可能会迅速变得乏味且容易出错。在时间的过程中,出现了许多设计模式,可以显着增强可读性和可维护性。尽管如此,数据和用户界面(即代表UI的组件)生活在不同世界(需要同步)的事实仍然如此。
这是脱发 UI框架的作用不同。我们 descript (嗯,声明)在“源代码”中,我们并没有在艰苦地更新组件树时,用户界面在当前数据中的外观(不:更改)。实际上,所有声明性的UI框架都知道状态的概念。状态是可能随着时间而变化的数据。重要的是:状态变化会自动触发UI更改。如果需要重建或重新绘制某些东西,则由框架决定。不是开发人员。不在应用程序源代码中。数据是开发的核心。
在JetPack组成,Google的Android声明性UI框架中,看起来像这样:
@Composable
@Preview
fun CounterDemo() {
var counter by remember { mutableStateOf(0) }
Column(
horizontalAlignment = CenterHorizontally,
modifier = Modifier.padding(16.dp)
) {
Box(
contentAlignment = Center,
modifier = Modifier.height(200.dp)
) {
if (counter == 0) {
Text(
text = "Not yet clicked",
softWrap = true,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h3
)
} else {
Text(
text = "$counter",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h1
)
}
}
Button(
onClick = { counter += 1 }
) {
Text(text = "Klick")
}
}
}
UI元素出现在源代码中,作为kotlin函数,用@Composable
注释。 @Preview
在IDE内部呈现一个可组合函数(可组合功能)。
应用程序的用户界面是通过嵌套自定义和预构建(即:由框架提供的)组合功能创建的。 CounterDemo()
将Box()
和一个Button
放入Column()
。样本中的Box()
始终具有子元素。这是由if (counter == 0)
确定的。如果条件为真,则尚未单击。如果是false
,则显示可变counter
的内容。 counter
是一个州。在JetPack Compose state 中通常是用mutableStateOf
创建的,并使用remember
记住。如果值更改(counter += 1
),则该框架确保更新了使用此状态的所有组合物。这称为重新分配。
您是否注意到UI元素没有引用或指示?与命令框架不同,不需要操纵或更改任何物体或树结构。因此,不再需要对分支或叶子进行参考。这消除了在错误指针引起的运行时难以发现的所有这些崩溃。如果状态更改需要更改内部数据结构,则在框架内处理所有内容。应用开发人员不必打扰。
合并功能的名称以大写字母开头,这使我们想起了类或数据结构。这种惯例的冲突是有意的。毕竟,组合代表UI元素(组件)。因此, deklarative 并不意味着不再存在组件。但是,组件现在减少了重量级。有很多小零碎的碎片,应该结合在一起。基本原理称为继承的组成。请注意,声明性不一定表示函数。例如,颤动是建立在课堂和继承的基础上。但是,从Java Swing或Javafx中您知道的组件的切割方式不同。根据声明的框架,例如Align
,Size
或Padding
组件。在当务之急的世界中,这些可能是属性。
我们如何显示CounterDemo()
?在Android上看起来像这样:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = stringResource(id = R.string.app_name))
}
)
}
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentAlignment = Center
) {
CounterDemo()
}
}
}
}
}
}
在这里,CounterDemo()
包裹在其他一些可复合功能中,MaterialTheme()
,Scaffold()
和TopAppBar()
,最终使用setContent { }
。 p>
桌面获得声明性
您想尝试编写声明性用户界面,但不在乎移动平台吗?这已经有一段时间了。例如,您可以构建一个React应用程序,并使用电子在桌面上运行它。但是,您需要了解JavaScript/Typescript并进行反应。也有颤音。该跨平台框架首先发布用于移动开发,但如今也适用于台式机和网络。颤抖非常受欢迎。但是,您需要学习飞镖编程语言。仍然很新的是组成多平台。它的创建者Jetbrains将其宣传为用于桌面和Web用户界面的快速,反应性Kotlin框架。它旨在简化和加快网络和桌面应用程序的UI开发。
从概念上讲,构图由组成,用于桌面, web 和 kotlin Multiplatform 。您可能会熟悉移动开发中的后者。基本想法是在Kotlin中编写业务逻辑,并将其与本机用户界面结合使用。 Google和Jetbrains将JetPack移植到桌面上。这是可能的,因为JetPack组合仅与Android平台松散结合。 UI元素是使用开源2D图形库SKIA渲染的,顺便说一句,该元素也用于Chrome,Chromeos和Flutter中。在桌面上,组合用户界面托管在Java秋千窗口内。当前在JVM内运行的撰写应用程序。这是一件好事,因为您不仅可以使用所有Java和Kotlin库,还可以使用构建Java本机图像的工具。要在桌面上显示CounterDemo()
,只需要几行代码:
fun main() = application {
Window(
title = TITLE,
onCloseRequest = ::exitApplication,
) {
MaterialTheme {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = TITLE)
}
)
}
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Center
) {
CounterDemo()
}
}
}
}
}
您是否注意到此代码片段与Android版本非常相似?我们可以将许多部分重构为一个函数。只需要以另一种方式进行访问的字符串(使用Android stringResource()
)。
结论
您肯定记得我说桌面上有一种风格混合物。好吧,撰写多平台增加了另一种风味 - 材料设计。如果您想尝试撰写综合式介绍,还应查看附带的Googles Design系统附带的文档。这将使您穿越喷气背包的旅程变得更加容易。要开发该应用程序,您将使用Intellij作为IDE和Gradle作为构建系统。 JetPack仅适用于Kotlin,但是应用程序的所有其他部分都可以使用Java(或任何其他JVM语言)实现。
组成乘法的某些方面,例如构建和运行用户界面,已经感觉稳定且成熟。其他部分看起来仍在进行中。特别是,与主机系统的集成需要增强。例如,拖放和文件关联只能使用解决方案进行。有趣的是,在进一步的开发过程中,Jetbrains仍将提供哪些缺少的功能。编写桌面的撰写应用程序肯定很有趣。
链接
- https://www.jetbrains.com/de-de/lp/compose-mpp/
- https://github.com/tkuenneth/imperative_vs_declarative_uis
本文的德语版本首先出现在Informatik Aktuell。