Android Kotlin Jetpack Compose UI框架到底好不好用?_android compose ui 不好用

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

你还可以在YouTube上观看【教学视频】

或者到gitHub下载【demo集合】,官方提供了很多demo示例,见下方示例图:

98098-yv3otge0svl.png

2.4 Compose编程思想

了解了如何搭建,以及如何编写demo,最终应用到项目之前,你还需要了解一些必备且重要的Compose基础。

首当其冲的是 编程思想

2.4.1 声明性编程范式

在Compose之前,我们最常见的界面更新方式是使用 findViewById() 方法找到UI控件,并通过调用 button.setText(String)container.addChild(View)img.setImageBitmap(Bitmap) 等方法更新控件。

这种手动操做布局的方式,可以提高代码可读性,但也容易出错。比如:A控件被移除后紧接着在另一段代码中又给A布局赋值。这可能会导致代码异常。

在过去的几年中,整个行业已开始转向声明性界面模型,该模型大大简化了与构建和更新界面关联的工程设计。该技术的工作原理是在概念上从头开始重新生成整个屏幕,然后仅执行必要的更改。此方法可避免手动更新有状态视图层次结构的复杂性。

简单点说,就是声明性布局可以做到只更新需要更新的布局,而不会因为一个小改动刷新整个屏幕,这是性能提升的一大进步。

Compose 是一个声明性界面框架。

重新生成整个屏幕所面临的一个难题是,在时间、计算能力和电池用量方面可能成本高昂。为了减轻这一成本,**Compose 会智能地选择在任何给定时间需要重新绘制界面的哪些部分。**这会对你设计界面组件的方式有一定影响,下面会说到。

2.4.2 简单的可组合函数

使用 Compose,你可以通过定义一组接受数据而发出界面元素的可组合函数来构建界面。

下面一个简单的示例是 Greeting 组件,它接受 String 类型文案,而发出一个显示问候消息的 Text 组件。

47696-71jthhgl94i.png

Greeting组件解析:

1.此函数带有 @Composable 注释。所有可组合函数都必须带有此注释;此注释可告知 Compose 编译器:此函数旨在将数据转换为界面。

2.微件接受一个 String,因此它可以按名称问候用户。

3.此函数可以在界面中显示文本。为此,它会调用 Text() 可组合函数,该函数实际上会创建文本界面元素。可组合函数通过调用其他可组合函数来发出界面层次结构。

4.此函数不会返回任何内容。发出界面的 Compose 函数不需要返回任何内容,因为它们描述所需的屏幕状态,而不是构造界面微件。

5.此函数快速、幂等且没有副作用。

  • 5.1 使用同一参数多次调用此函数时,它的行为方式相同,并且它不使用其他值,如全局变量或对 random() 的调用。
  • 5.2 此函数描述界面而没有任何副作用,如修改属性或全局变量。
2.4.3 声明性范式转变

在以往的 XML 布局编程时,通常会通过增加 XML 布局文件来实现布局扩张,每个 View 内部会维护自己状态,并且提供 gettersetter 方法,允许逻辑与 View 进行交互。

在 Compose 的声明性方法中, View 相对无状态,并且不提供 setter 或 getter 函数。

实际上, View 不会以对象形式提供。

你可以通过调用带有不同参数的同一可组合函数来更新界面。这使得向架构模式(如 ViewModel)提供状态变得很容易,如应用架构指南中所述。

然后,可组合项函数 负责在每次可观察数据更新时将当前应用状态转换为界面。

(下图示例:一个数据源像下传递,应用到每个布局,需要刷新界面时,只需要刷新数据源)

66403-uw4wckawpp.png

(下图示例:一个字布局出发点击事件时,事件向上传递,最后更改数据源,界面得以刷新)

88852-bnf1vohdnms.png

2.4.4 动态内容

由于可组合函数是用 Kotlin 而不是 XML 编写的,因此它们可以像其他任何 Kotlin 代码一样动态。例如,假设你想要构建一个界面,用来问候一些用户

@Composable
fun Greeting(names: List) {
for (name in names) {
Text(“Hello $name”)
}
}
复制代码

此函数接受名称的列表,并为每个用户生成一句问候语。

可组合函数可能非常复杂。你可以根据功能,使用kotlin进行任意逻辑改造,所有这些动态切定制的内容,是 Compose对比传统xml的优势。

2.4.5 重组

在命令式界面模型中(XML界面模型),如需更改某个View,你可以在该View上调用 setter 以更改内部状态。

Compose 中,你可以使用新数据再次调用可组合函数。

这样做会导致函数进行重组 – 系统会根据需要使用新数据重新绘制函数发出的View。

Compose 框架可以智能地仅重组已更改的组件。

例如,假设有以下可组合函数,它用于显示一个按钮:

@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
Button(onClick = onClick) {
Text(“I’ve been clicked $clicks times”)
}
}
复制代码

以上代码,每次点击该按钮时,调用方都会更新 clicks 的值。Compose 会再次调用 lambdaText 函数以显示新值;此过程称为“重组”。不依赖于该值的其他函数不会进行重组。

重组整个界面树在计算上成本高昂,因为会消耗计算能力并缩短电池续航时间。

Compose 根据新输入重组时,它仅调用可能已更改的函数或 lambda,而跳过其余函数或 lambda。通过跳过所有未更改参数的函数或 lambdaCompose 可以高效地重组

切勿依赖于执行可组合函数所产生的附带效应,因为可能会跳过函数的重组。

附带效应:是指对应用的其余部分可见的任何更改。

例如,以下操作全部都是危险的附带效应:

  • 写入共享对象的属性
  • 更新 ViewModel 中的可观察项
  • 更新共享偏好设置

举个例子:

以下代码会创建一个可组合项以更新 SharedPreferences 中的值。

@Composable
fun SharedPrefsToggle(
text: String,
value: Boolean,
onValueChanged: (Boolean) -> Unit
) {
Row {
Text(text)
Checkbox(checked = value, onCheckedChange = onValueChanged)
}
}
复制代码

该可组合项不应从共享偏好设置本身读取或写入,于是此代码将读取和写入操作移至后台协程中的 ViewModel。应用逻辑会使用回调传递当前值以触发更新。

2.4.6 使用Compose的注意事项

以下是在 Compose 中编程时需要注意的事项:

  • 可组合函数可以按任何顺序执行。
  • 可组合函数可以并行执行。
  • 重组会跳过尽可能多的可组合函数和 lambda。
  • 重组是乐观的操作,可能会被取消。
  • 可组合函数可能会像动画的每一帧一样非常频繁地运行。

在每种情况下,最佳做法都是使可组合函数保持快速、幂等且没有附带效应。

可组合函数可以按任何顺序执行。

例如,假设有如下代码,用于在标签页布局中绘制三个屏幕:

@Composable
fun ButtonRow() {
MyFancyNavigation {
StartScreen()
MiddleScreen()
EndScreen()
}
}
复制代码

StartScreenMiddleScreenEndScreen 的调用可以按任何顺序进行。这意味着,举例来说,你不能让 StartScreen() 设置某个全局变量(附带效应)并让 MiddleScreen() 利用这项更改。相反,其中每个函数都需要保持独立。

可组合函数可以并行执行。

Compose 可以通过并行运行可组合函数来优化重组。 这样一来,Compose 就可以利用多个核心,并以较低的优先级运行可组合函数(不在屏幕上)。

这种优化意味着,可组合函数可能会在后台线程池中执行。

假设多个可组合函数调用了 ViewModel 里的方法A,那么方法A会被多个线程调用,需要做好线程同步工作。

也因为可并行执行的特点,调用可能发生在与调用方不同的线程上。因此,所有可组合函数都不应有附带效应(比如修改一个全局变量),而应通过始终在界面线程上执行的 onClick 等回调触发附带效应。

以下示例展示了一个可组合项,它显示一个列表及其项数:

@Composable
fun ListComposable(myList: List) {
Row(horizontalArrangement = Arrangement.SpaceBetween) {
Column {
for (item in myList) {
Text(“Item: $item”)
}
}
Text(“Count: ${myList.size}”)
}
}
复制代码

此代码没有附带效应,它会将输入列表转换为界面。此代码非常适合显示小列表。不过,如果函数写入局部变量,则这并非线程安全或正确的代码:

@Composable
@Deprecated(“Example with bug”)
fun ListWithBug(myList: List) {
var items = 0

Row(horizontalArrangement = Arrangement.SpaceBetween) {
Column {
for (item in myList) {
Text(“Item: $item”)
items++ // Avoid! Side-effect of the column recomposing.
}
}
Text(“Count: $items”)
}
}
复制代码

在本例中,每次重组时,都会修改 items。这可以是动画的每一帧,或是在列表更新时。但不管怎样,界面都会显示错误的项数。因此,Compose 不支持这样的写入操作;通过禁止此类写入操作,我们允许框架更改线程以执行可组合 lambda。

重组会跳过尽可能多的可组合函数和 lambda。

如果界面的某些部分无效,Compose 会尽力只重组需要更新的部分。这意味着,它可以跳过某些内容以重新运行单个按钮的可组合项,而不执行界面树中在其上面或下面的任何可组合项。

每个可组合函数和 lambda 都可以自行重组。以下示例演示了在呈现列表时重组如何跳过某些元素:

/**

  • Display a list of names the user can click with a header
    */
    @Composable
    fun NamePicker(
    header: String,
    names: List,
    onNameClicked: (String) -> Unit
    ) {
    Column {
    // this will recompose when [header] changes, but not when [names] changes
    Text(header, style = MaterialTheme.typography.h5)
    Divider()

// LazyColumn is the Compose version of a RecyclerView.
// The lambda passed to items() is similar to a RecyclerView.ViewHolder.
LazyColumn {
items(names) { name ->
// When an item’s [name] updates, the adapter for that item
// will recompose. This will not recompose when [header] changes
NamePickerItem(name, onNameClicked)
}
}
}
}

/**

  • Display a single name the user can click.
    */
    @Composable
    private fun NamePickerItem(name: String, onClicked: (String) -> Unit) {
    Text(name, Modifier.clickable(onClick = { onClicked(name) }))
    }
    复制代码

这些作用域中的每一个都可能是在重组期间执行的唯一一个作用域。当 header 发生更改时,Compose 可能会跳至 Column lambda,而不执行它的任何父项。此外,执行 Column 时,如果 names 未更改,Compose 可能会选择跳过 LazyColumnItems

执行所有可组合函数或 lambda 都应该没有附带效应。当你需要执行附带效应时,应通过回调触发。

重组是乐观的操作,可能会被取消。

重组是乐观的操作,也就是说,Compose 预计会在参数再次更改之前完成重组。如果某个参数在重组完成之前发生更改,Compose 可能会取消重组,并使用新参数重新开始。

取消重组后,Compose 会从重组中舍弃界面树。

如有任何附带效应依赖于显示的界面,则即使取消了组成操作,也会应用该附带效应。这可能会导致应用状态不一致(导致状态错乱,或重复赋值)。

可组合函数可能会像动画的每一帧一样非常频繁地运行。

在某些情况下,可能会针对界面动画的每一帧运行一个可组合函数。如果该函数执行成本高昂的操作(例如从设备存储空间读取数据),可能会导致界面卡顿。

如果可组合函数需要数据,它应为相应的数据定义参数,从参数中获取。

你可以将成本高昂的工作移至组成操作线程之外的其他线程,并使用 mutableStateOfLiveData 将相应的数据传递给 Compose

**总结:**可组合函数应尽量写成纯函数,数据仅从参数中获取,更改数据仅从用户操作事件中进行。所有异步数据需要先准备好,再传入函数的参数中。

3 Compose是否值得一试

前面讲到Compose的特性,优缺点,以及如何快速入门、如何正确使用。

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。这也是我这些年来养成的习惯,一定要学会把好的东西,归纳整理,然后系统的消化吸收,这样才能极大的提高学习效率和成长进阶。碎片、零散化的东西,我觉得最没有价值的。就好比你给我一张扑克牌,我只会觉得它是一张废纸,但如果你给我一副扑克牌,它便有了它的价值。这和我们收集资料就要收集那些系统化的,是一个道理。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

我只会觉得它是一张废纸,但如果你给我一副扑克牌,它便有了它的价值。这和我们收集资料就要收集那些系统化的,是一个道理。

[外链图片转存中…(img-aW4vxy5Y-1713648804170)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-5cxQ2ZKN-1713648804171)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值