• 声明式UI是否会成为Android开发的主流?


    从Windows到Web,到Android,到iOS,我们编写应用 (App) 最初的方式都是命令式的 (Imperative Style) 。但随着声明式 (Declarative Style) 的出现,情况正在发生极速的变化。我们先看看命令式和声明式分别怎么写UI。

    命令式 vs 声明式

    命令式 (Imperative Style)

    命令式的方式是一种自然而然想到的方式,我们写代码构建整个UI (view tree),在需要更新UI时 (比如获取到网络数据后或者用户改变UI元素后) 从view tree里查找到对应的元素,再更新它。比如在Android开发中,我们通过findViewById查找到某文本元素,再给它设置显示的内容:

    1. TextView tv = findViewById(R.id.text_view);
    2. tv.setText("Hello World");

    声明式 (Declarative Style)

    命令式的方式是一种最朴素的方式,它要求我们针对每一次UI的变更都亲自写代码修改view tree,且这些代码分散在各个地方。那么我们有没有方法把我们从对view tree的频繁操作中解放出来呢?有。Facebook的React框架就是一个知名的先驱。声明式的方式集中在一个地方声明UI的结构,UI的变化通过数据来驱动。我们只需要修改数据,剩下的修改view tree的工作交给框架来做。

    声明式发展历程

    其实声明式的UI布局方式已经有比较长的历史,下面简单介绍一些比较知名的

    Facebook React

    前端开发在React之前,比较常用的是jQuery这种命令式的框架。下面是一个jQuery的例子,用ajax发起一个网络请求,请求返回结果后在DOM树里搜索到节点 ($("#weather-temp")) ,再将结果设置给节点显示出来。

    1. $.ajax({
    2. url: "/api/getWeather",
    3. data: {
    4. zipcode: 97201
    5. },
    6. success: function( result ) {
    7. $("#weather-temp").html( "<strong>" + result + "</strong> degrees" );
    8. }
    9. });

    Facebook内部从2011年开始开发FaxJS,也就是React的原型,并于2013年开源了React。下面是一个React声明式UI的例子

    1. class MarkdownEditor extends React.Component {
    2. constructor(props) {
    3. super(props);
    4. this.md = new Remarkable();
    5. this.handleChange = this.handleChange.bind(this);
    6. this.state = { value: 'Hello, **world**!' };
    7. }
    8. handleChange(e) {
    9. this.setState({ value: e.target.value });
    10. }
    11. getRawMarkup() {
    12. return { __html: this.md.render(this.state.value) };
    13. }
    14. render() {
    15. return (
    16. <div className="MarkdownEditor">
    17. <h3>Input</h3>
    18. <label htmlFor="markdown-content">
    19. Enter some markdown
    20. </label>
    21. <textarea
    22. id="markdown-content"
    23. onChange={this.handleChange}
    24. defaultValue={this.state.value}
    25. />
    26. <h3>Output</h3>
    27. <div
    28. className="content"
    29. dangerouslySetInnerHTML={this.getRawMarkup()}
    30. />
    31. </div>
    32. );
    33. }
    34. }
    35. root.render(<MarkdownEditor />);

    渲染效果: 

     我们将某个Component的UI描述全部放在render方法里,这个Component有它自己的状态 (state)。当用户在textarea输入内容时,handleChange会被调用,它会从textarea取出当前值,赋给value这个state变量,调用setState方法告诉React框架需要更新UI,然后框架就会完成剩下的所有工作。对开发者来说非常的简单高效。

    Facebook React Native

    Facebook在2015年开源了React Native这个跨平台的框架,它可以让你通过前端技术栈开发Android和iOS应用。它声明UI的方式和React一模一样,如下所示

    1. import React from 'react';
    2. import {Text, View} from 'react-native';
    3. import {Header} from './Header';
    4. import {heading} from './Typography';
    5. const WelcomeScreen = () => (
    6. <View>
    7. <Header title="Welcome to React Native"/>
    8. <Text style={heading}>Step One</Text>
    9. <Text>
    10. Edit App.js to change this screen and turn it
    11. into your app.
    12. </Text>
    13. <Text style={heading}>See Your Changes</Text>
    14. <Text>
    15. Press Cmd + R inside the simulator to reload
    16. your app’s code.
    17. </Text>
    18. <Text style={heading}>Debug</Text>
    19. <Text>
    20. Press Cmd + M or Shake your device to open the
    21. React Native Debug Menu.
    22. </Text>
    23. <Text style={heading}>Learn</Text>
    24. <Text>
    25. Read the docs to discover what to do next:
    26. </Text>
    27. </View>
    28. );

    Facebook Litho

    Facebook在2017年开源了Litho这个高性能的Android UI开发框架,在开源之前已在内部主要App广泛使用,如Facebook等。受React的影响,它也采用了声明式的UI布局方式。下面是一个例子

    1. class PostStyledKComponent(val post: Post) : KComponent() {
    2. override fun ComponentScope.render(): Component {
    3. return Column {
    4. child(
    5. Row(alignItems = YogaAlign.CENTER, style = Style.padding(all = 8.dp)) {
    6. child(
    7. Image(
    8. drawable = drawableRes(post.user.avatarRes),
    9. style = Style.width(36.dp).height(36.dp).margin(start = 4.dp, end = 8.dp)))
    10. child(Text(text = post.user.username, textStyle = Typeface.BOLD))
    11. })
    12. child(
    13. Image(
    14. drawable = drawableRes(post.imageRes),
    15. scaleType = ImageView.ScaleType.CENTER_CROP,
    16. style = Style.aspectRatio(1f)))
    17. }
    18. }
    19. }

    渲染效果:

    Google Flutter

    Google在2018年发布了Flutter的第一个稳定版本v1.0.0。它采用了声明式的布局方式,下面是一个简单的例子

    1. class MyApp extends StatelessWidget {
    2. const MyApp({super.key});
    3. @override
    4. Widget build(BuildContext context) {
    5. return MaterialApp(
    6. title: 'Welcome to Flutter',
    7. home: Scaffold(
    8. appBar: AppBar(
    9. title: const Text('Welcome to Flutter'),
    10. ),
    11. body: const Center(
    12. child: Text('Hello World'),
    13. ),
    14. ),
    15. );
    16. }
    17. }

    Apple SwiftUI

    Apple在2019年发布了SwiftUI的第一个版本,它让你用声明式的方式来写iOS应用。下面是一个简单的例子

    1. import SwiftUI
    2. struct AlbumDetail: View {
    3. var album: Album
    4. var body: some View {
    5. List(album.songs) { song in
    6. HStack {
    7. Image(album.cover)
    8. VStack(alignment: .leading) {
    9. Text(song.title)
    10. Text(song.artist.name)
    11. .foregroundStyle(.secondary)
    12. }
    13. }
    14. }
    15. }
    16. }

    Google Jetpack Compose

    Google在2021年7月发布了Jetpack Compose的第一个稳定版本v1.0,像Flutter一样采用声明式UI布局方式,但Compose的设计比Flutter更合理一些。在Flutter里,你要给一个Widget设置宽高等属性都需要在外面套一层Container来实现,这非常的臃肿,而其它声明式的框架,包括Compose,大都可以通过节点的属性来设置。下面是一个Compose的简单例子

    1. import androidx.compose.foundation.layout.Spacer
    2. import androidx.compose.foundation.layout.height
    3. import androidx.compose.foundation.layout.padding
    4. import androidx.compose.foundation.layout.size
    5. import androidx.compose.foundation.layout.width
    6. import androidx.compose.foundation.shape.CircleShape
    7. import androidx.compose.ui.Modifier
    8. import androidx.compose.ui.draw.clip
    9. import androidx.compose.ui.unit.dp
    10. @Composable
    11. fun MessageCard(msg: Message) {
    12. // Add padding around our message
    13. Row(modifier = Modifier.padding(all = 8.dp)) {
    14. Image(
    15. painter = painterResource(R.drawable.profile_picture),
    16. contentDescription = "Contact profile picture",
    17. modifier = Modifier
    18. // Set image size to 40 dp
    19. .size(40.dp)
    20. // Clip image to be shaped as a circle
    21. .clip(CircleShape)
    22. )
    23. // Add a horizontal space between the image and the column
    24. Spacer(modifier = Modifier.width(8.dp))
    25. Column {
    26. Text(text = msg.author)
    27. // Add a vertical space between the author and message texts
    28. Spacer(modifier = Modifier.height(4.dp))
    29. Text(text = msg.body)
    30. }
    31. }
    32. }

    渲染效果:

    展望

    我们看到2017年的Litho,2018年的Flutter,2019年的SwiftUI,2021年的Jetpack Compose都采用了声明式的UI布局方式,已呈燎原之势,声明式的UI布局方式未来会像前端领域的React一样成为主流吗?

  • 相关阅读:
    【web-利用信息泄露】(10.2)收集公布的信息、使用推论
    单商户商城系统功能拆解23—用户标签
    HOOPS学习笔记
    QEMU TCG研究
    Java-回调函数
    博彦科技JAVA笔试题及答案
    《进阶篇第9章》学习vuex知识点后练习:把求和案例改成vuex版代码
    工作效率-十五分钟让你快速学习Markdown语法到精通排版实践备忘
    驱动获取设备树节点信息
    【校招VIP】前端计算机网络之webSocket相关
  • 原文地址:https://blog.csdn.net/clcwcxfwf/article/details/125389574