前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【扔物线】关于学习 Compose,我的经验总结和建议

【扔物线】关于学习 Compose,我的经验总结和建议

作者头像
扔物线
发布2022-11-23 15:16:49
7641
发布2022-11-23 15:16:49
举报
文章被收录于专栏:HenCoderHenCoder

大家好,我是扔物线朱凯。

最近郑州疫情,我们全员居家办公。在家闷头做课程的时候,我被 Slack 上的同事要求写个介绍课程的宣传文。我觉得如果只介绍课程的话可能会没人看,那就白写了。思索过后,我决定分享一下自己学 Compose 的经验,跟大家讲一下我认为最完美的学习 Compose 的路线。如果能让读者觉得有用,酣畅淋漓地读着,一不留神被下面的广告部分吸引,岂不是两全其美?

首先:你要学 Compose 吗?

或者更确切地问:你要现在就开始学 Compose 吗?

Compose 未来一定会取代 View 系统的写法,成为 Android 开发的主流方案。但就当下来说,并不是任何人都需要现在赶紧学的。我的观点是:想做 Compose 先行者、或者公司已经在用 Compose,不学不行的,学。更具体的,大家可以看我的这个视频:

Compose 的知识体系非常庞大,我已经近乎全职地研究了快两年了,到现在课程终于尾声。下面是我对于 Compose 学习的重点总结,希望可以帮到大家。

1. 先摸一摸 Compose 怎么用

Compose 在写法上和 Android 传统的 View 方案很不一样,也就是所谓的「声明式 UI」。

声明式的写法确实很方便,但因为和我们传统的写法(所谓的「命令式」)差别非常大,所以学 Compose 的第一步,最好是先自己去写几个简单的 demo,看看 Compose 的界面代码大概是长什么样的——比如文字怎么写、按钮怎么写、线性布局怎么写、滑动布局怎么写、点击监听怎么设置。

关于「声明式 UI」的介绍,我有一个视频

最好别只看视频,自己去写个代码感受一下。不用太多太复杂,因为复杂的界面需要更深的知识,等你学完之后自然就知道怎么写了,太早纠结于「在 Compose 里这种效果要怎么写呀?让我去搜搜问问」反而会耽误你的时间。所以随便了解一下,写个大概就行了。

2. Compose 的状态机制

Compose 的声明式写法,不可避免地引入了它的状态机制。声明式 UI 最强的地方在于,通过各层级的状态变量的控制,让工程师不需要写任何的界面更新的代码(例如 nameView.text = newName 这样的),而是仅仅对各种状态的值进行更新,界面就能实现自动的变化,包括动画。

而状态机制,就是这背后的技术基石。所以在了解了 Compose 的基本用法之后,尽早把它的状态机制搞清楚,对于学习其他更全面、更深入的 Compose 知识是非常重要的。

关于 Compose 的状态机制,有几个重点:

  • MutableState 类和 mutableStateOf() 函数用法和工作原理。了解到工作原理就好,这能让你对于「自动更新」有更清楚的认识,从而对于「什么时候会自动更新、怎样可以触发自动更新」具备预判的能力。
    • 另外对于 ListMap 这些集合类,它们也有对应的 mutableStateListOf()mutableStateMapOf() 这样的函数。它们不能和普通的数据类型一样使用 mutableStateOf() 的原因和 Compose 的自动更新机制有关,如果有意往「高级」的方向突,可以了解一下,否则的话不看那么深也行。
  • remember() 函数和 Compose 的重组作用域的了解。这是一个关于性能的知识点。
  • Compose 的「无状态」的本质含义(是谁无状态?是界面组件的属性无状态,而不是界面组件无状态,这个要想明白),以及围绕它的「状态提升(State Hoisting)」和单向数据流这两个技术名词。这个懂了之后,就能写出可复用的组件了。
  • CompositionLocal。这是 Compose 里的「针对 Composable 函数调用的、具有穿透能力的局部变量」,一般用来为嵌套调用的组件提供上下文信息。也是必备知识之一。
  • 高级知识(如果对自己要求不高,可以先不深入了解):
    • Compose 在重组过程中的性能风险,以及利用 @Stable 在特殊情况下进行性能优化;
    • derivedStateOf():一个提供「状态链条」功能的函数,对于 A 状态的改变触发 B 状态的更新的场景适用,正确使用可以提高复杂场景下的性能。这个函数写起来很简单,关键是它的使用场景背后的机制要搞明白。可能会比较难想清楚,别着急,多想想。

上面这些知识全部掌握,对于 Compose 的状态机制就可以达到高手的级别了。但如果你不打算做公司的 Compose 一把手,「高级知识」那两条暂时缓缓也没问题。

3. 动画

动画的知识对于 UI 来说非常重要,但它的理解需要以状态机制的知识作为基础,所以搞定了状态机制之后,你可以去学动画。

Android 传统的 UI 里,属性动画是个历史悠久的话题,也是非常重要的一块知识。到了 Compose 里,动画依然重要。原因很简单,因为这是软件开发的硬需求。

Compose 的动画写起来很简单。它的核心知识包括这几块:

  • 基本的 animateXxxAsState() 函数。这个初看会觉得方便到离谱的程度,把属性用 animateXxxAsState() 的方式赋值,它们改变的时候就会自动用动画的方式来呈现了。背后的原理是悄悄地开启了协程。
  • Animatable 类。它是 animateXxxAsState() 的底层实现类,而由于 animateXxxAsState() 的「自动」,所以我们只能用它来写状态转移时自动渐变状态的动画,而不能精确地定制动画流程。而这个更底层的 Animatable,就可以帮我们做精确定制的动画。
  • AnimationSpec:对于动画的速度曲线的限制,传统属性动画用的是 Interpolator,而在 Compose 用的是这个 AnimationSpec
  • 对于消散型动画(例如惯性滑动场景),有个专门的动画函数 animateDecay()AnimationSpec 也不适用于它,它要用专门的 DecayAnimationSpec
  • 动画还有个叫做 Transition 的东西,它是用于设置多属性的状态切换型动画的,比如一个头像点击之后一边放大一边移动一边旋转,用 animateXxxAsState() 就没有 Transition 用起来方便。另外,Compose 还提供了一些更方便的 Transiton 延伸的 Composable 函数:
    • AnimatedVisibility():自动的出现和消失。
    • Crossfade():省事版的 AnimatedVisibility(),效果是预设的淡入淡出。
    • AnimatedContent():更复杂的批量控制组件的出现和消失。

动画部分没有很难的单点知识,主要是要掌握各个技术点的定位,以及几种动画实现方式的知识结构:Animatable 是最底层,它上面是 TransitionanimateXxxAsState()

4. Modifier

Modifier 是 Compose 里最大的一块知识,没有之一,Compose 里大多数的功能都是靠 Modifier 来实现的。

Modifier 本质上是 Compose 里的一种提供 UI 组件的通用属性的方式。所谓通用属性,即任何一个组件都可能、都可以拥有的属性,例如尺寸;与之相对的是个性化属性,例如文字组件里的字符串,个性化属性是用 Composable 函数的参数来提供的。

而 Compose 里的 Modifier 提供的属性,不仅限于「组件所要显示的内容」,而是包括各种与组件的显示和交互有关的属性。例如点击监听,用的是 Modifier.clickable() 函数;设置类似传统写法里的 LinearLayout 里的 weight 属性的效果,用的是 Modifier.weight() 函数。

Modifier 的功能很强大,但同样也是因为 Modifier 的强大,所以它的相关知识也很多。我翻遍了 Compose 的相关源码,总结出 Modifier 的知识大致分类如下:

  • Modfiier.then():用于合并(或者说连接)两个 Modifier。更下层的两个相关类型是 CombinedModifierModifier.Element,了解这两个东西会对 Modifier 的工作逻辑以及在各种场景下的性能表现有更清楚的了解。
  • Modifier.composed():用于把一个 Modifier 包进一个工厂函数,以便复用。比较实用,但就算不用也可以实现某些需求,直到有一天你发现「卧槽,竟然还有个这么方便的东西!」所以一定要花时间了解一下。
  • LayoutModifier:看起来像是「Compose 里的自定义布局」的工具,其实只是用于对单个 Composable 组件进行尺寸修饰的。类似传统写法里对自定义 View 重写 onMeasure() 来进行尺寸自定义,但比这种写法的功能更弱一些。
  • DrawModifier:用于绘制的 Modifier。所有组件的绘制都是用的它,自定义绘制也是用的它。
  • PointerInputModifier:用于设置触摸反馈逻辑的 Modifier。所有组件的触摸都是用它写的逻辑,自定义触摸反馈也是用的它。需要注意的是,Compose 里使用的协程来设置触摸反馈逻辑的,所以和自定义 View 的触摸反馈是完全不一样的写法,但其实思维逻辑简单得多,只是需要你换个思考方式而已。所以上手的时候不要害怕,试一试你会发现复杂的自定义触摸逻辑你用 Compose 来写并不是很难(起码比 onTouchEvent() 简单)。
  • ParentDataModifier:用于处理具有父子关系的组件的界面的 Modifier,给父组件提供子组件的信息,来辅助测量。例如前面提到的 Modifier.weight(),内部实现所有的就是一种 ParentDataModifier
  • SemanticsModifier:用于提供语义树设置的Modifier。所谓语义树,其实就是组件树,只不过在某些情况下我们可以对某个子树进行合并,例如「把一个内部包含图片和文字的按钮,合并成一个组件」,来简化语义逻辑。主要用于两种场景:开发测试和无障碍功能的功能优化。这个我感觉比较难用几句话说清楚,但我的课程里的这节课也没开放试听,大家可以试着上网搜一下这个词。
  • OnRemeasuredModifier:重新测量的监听器,在重新测量发生之后会触发它的回调函数。
  • OnPlacedModfier:重新布局的监听器,在重新布局(即位置摆放)发生之后,它的回调函数会被触发。
  • ModifierLocal:以及跟它相关的 ModifierLocalProviderModfieirLocalConsumer:用于提供和 CompositionLocal 类似的「特殊版本的局部变量」的功能,相当于 Modifier 中的、具有穿透功能的属性。同样适用于提供上下文,不过是 Modifier之间的上下文。例如对于 WindowInsets 相关的 Modifier,可以在多层 Modifier 之间共享消耗掉的、剩余可用的 WindowInsets;再如对于嵌套滑动的布局里,对于已消耗的手指滑动唯一的内外层共享。这个东西有点难,但是非常有用,最好搞明白,可以让你把很多事的做法变得简单得多。

Modifier 的难点主要在于两点:搞明白 Modifier 和函数参数在适用场景上的区别(上面说了,Modifier 适用于通用属性,函数参数适用于个性化属性);Modifier 有很多种,每一种都是一个单独的知识,并且还有几类是有一些理解难度的,所以总的知识量比较大。但是当你把这一块的知识学完,你的 Compose 水平就已经在比较高的位置了。

5. Side Effects(副作用、附带效应)和协程

Compose 的界面刷新靠的是一种叫做「重组」的机制,它本质上就是把界面元素的生成逻辑重新执行一遍,因为 Compose 里的所有界面组件其实都是函数,而界面的生成和刷新靠的也是这些界面函数的调用。但这种「重新执行」会导致性能问题,即没有发生改变的界面组件也被重新执行,导致了执行效率的下降,最终结果就是界面刷新的缓慢、用户操作和动画的卡顿。所以 Compose 的「i重组」机制里,加入了很多的优化,例如局部的重新调用,而非整个界面的重新调用。

而这种「局部重新调用」的发生时机是不可预知的,并且发生的次数也是无可预知的,这就导致如果我们在 Composable 组件的代码里加入对外界有影响的逻辑(例如把某个外部变量的值加一)——这个在编程领域被称作 side effect,或者中文叫副作用——有可能发生我们意想不到的结果。

但有时候我们又会有一些「界面的显示触发某些外部改变」的情况,这就是 Compose 的 side effect 相关的函数的用处所在。这类函数一共有三个:

  • SideEffect()
  • DisposableEffect()
  • LaunchedEffect()
    • LaunchedEffect() 就是在 Composable 函数内部开启协程的方式,所以学习 Compose 的时候,这个函数一定不能漏——实际上,这三个函数都不能漏。也不多,一个个学呗?
  • 另外对于「需要关联 Composable 函数的声明周期,但不希望在 Composable 函数的内部触发」的协程,需要用 rememberCoroutineScope() 函数来提供一个可供外部使用的 CoroutineScope,这样就可以在任何时候(例如点击监听器里)手动触发协程。所以这个 rememberCoroutineScope() 也需要看看。
  • 最后就是,有一个叫做 rememberUpdatedState() 的函数,它是对于 DisposableEffect()LaunchedEffect() 的一个非常有用的辅助。它和前面提到的 derivedStateOf() 的特点有点像:写起来很容易,重点是知道它怎么用。所以,不要小看它,花点时间研究一下它的用法。

6. 其他知识

例如:

  • Compose 和自定义 View 的交互;
  • Compose 和其他 Jetpack 库的搭配使用;
  • Compose 的深度原理;
  • Compose 的自定义主题;
  • 等等等等。

总结

以上就是我在近乎全职地研究了两年 Compose 之后,对于「应该如何学习 Compose」做出的总结和建议。你按照这份总结和建议把里面的知识刷了(看官方文档、看源码、看网上的博客、看我的公开视频,加上自己的思考和练习),应该可以得到不错的结果。如果这篇文章帮到了你,还请帮忙点赞转发一个,让更多人看到。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-11-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 扔物线 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 首先:你要学 Compose 吗?
  • 1. 先摸一摸 Compose 怎么用
  • 2. Compose 的状态机制
  • 3. 动画
  • 4. Modifier
  • 5. Side Effects(副作用、附带效应)和协程
  • 6. 其他知识
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档