快速上手
条目 | 内容 |
---|---|
是什么 | 原生Android界面构建工具包 |
优势 | -更少的代码 -直观(状态变化,自动更新界面) -可以预览 -Material design |
什么是可组合函数 | -用函数描述外观和数据依赖 -不关注构建过程 |
预览 | @Preview 函数不能带参数 |
布局 | -Row水平排列 -Column垂直排列 |
修饰器 | 大小,外观,高级互动(例如使元素可以点击) 深色浅色主题切换 |
Material design | 颜色,排版,形状 |
列表和状态,动画 | https://developer.android.google.cn/jetpack/compose/themes/material?hl=zh-cn 颜色,字体Style(什么字体,多大)和形状. 状态 动画 |
深入详解Compose优化UI构建 -Compose解决的问题 -Compose函数剖析 -声明式UI -组合VS继承 -重组 | 解决的问题: 减少耦合增加内聚 .将界面和数据用相同的语言来编写,一些隐式的依赖就会更加明显. 命令式需要自己更新UI, 声明式是数据绑定,当数据发生变化,自动更新UI. 组合VS继承 重组,将界面发生变化时,只进行部分重绘 状态改变时,自动重绘. 没有回调函数了,也不需要订阅了. |
总结: |
布局
条目 | 内容 |
---|---|
标准布局组件 | Column(垂直) Row(水平) Box(层叠) |
修饰符 | 类似CSS 大小,布局,行为,外观 添加信息,例如无障碍标签 处理用户输入 高级互动,例如可点击,可滚动,可拖动,或可缩放 点击时显示黑色背景波纹(clickable的作用) clickable修饰组件内部,padding修饰组件外部. |
Slots(卡槽) API | TopAppBar Scaffold 提供topAppBar,bottomAppBar,FloatingActionButton,Drawer的槽位 |
使用列表 | Column Row LazyColumn lazyRow 默认是不支持滚动的,需要支持滚动(状态变化才会重绘,也叫重组,没有状态是不能滑动的) 让jetpack compose支持从网络加载图片(需要添加网络访问权限) coil ScrollToTop ScrollToBottom |
自定义布局 | 自定义修饰符 案例:firstBaseline ToTop 文字底部到父元素的距离 layout 修饰符来修改元素的测量和布局方式 MyOwnColumn (这个应该叫,自定义容器) StaggeredGrid(交错网格-棋盘) |
约束布局 | 引用-createRefsor createRefFor parent是一个现有的引用 约束条件 constrainAs linkto 解耦API - 将布局和约束分离,根据屏幕修改约束条件.两个约束集之间可以添加动画效果. BoxWithConstraints 可以获取父容器的大小,比如例子中的maxWidth 和 maxHeight. |
Intrinsics(内部函数) | Compose只测量子元素一次,测试两次会引发运行时异常.但是,有时在测量子元素之前,我们需要一些有关元素的信息. Intrinsic允许在实际测量之前查询子项 (min,max)intrinsicWidth:鉴于此高度,您可以正确绘制内容的最小和最大宽度是多少 (min,max)intrinsicHeight:鉴于此高度,您可以正确绘制内容的最小和最大高度是多少 |
隐式传参 CompositionLocal
A()
{
var i =45
B(i)
}
拿出中等透明度这个值. CompositionLocalProvider中的所有元素,都会使用相同的透明度.
状态
条目 | 内容 |
---|---|
无状态组件 | |
非结构化状态 (传统view) | 缺点 - 测试: UI和代码混在一起,需要手动操作软件来测试,而不是通过函数调用测试 - 部分状态更新: 屏幕有很多事件时,可能忘记部分状态 - 部分UI更新: 每次状态改变后要手动更新UI,可能忘记更新某些控件的显示 - 代码复杂性: 代码难以阅读 |
单向数据流(传统view 的解决方式) | 为了解决非结构化状态的问题,引入了ViewModel和LiveData. 将状态从Activity移动到了ViewModel,在Viewmodel中,状态用LiveData 表示. LiveData是一种可观察的容器,在界面中使用observe方法,可以在状态变化时更新界面. LiveData的set方法是protected,所以name是只读的. 不能通过name修改_name;写操作必须通过指定的函数来做;这就是单向数据流 事件向上流动,状态向下流动 - 分离状态和UI, 测试容易,直接调用onNameChanged函数就可以测试. - 状态封装 ,状态只能在viewmodel中更新,随着UI增长,不容易引入部分状态更新问题. - UI一致性,所有的状态更新都通过使用可观察状态持有者立即反映在UI中 |
Compose的状态管理 | 把状态放到viewModel里面(放到父函数里面也行),这种操作叫做状态提升,将状态转移给调用Compose的调用方.而组合本身是无状态的. compose函数有两个输入参数:1. 数据2. 对数据的操作函数. |
重组和remember | 每次添加新元素,现有的透明度也会跟着改变,以为发生了重组.所以需要引入remember. 系统会将由remember计算的值存储在组合树种,只有当remember的键发生变化时才会重新计算. 使用remember会是的该可组合项有状态; 调用方不需要控制状态,而且不需要管理状态时,"有状态"非常有用,但有内部状态的可组合项往往不易重复使用,更难测试 将由状态的可组合项改成无状态,一种简单的做法是使用状态提升. |
MutableState | |
总结 | * 先做了一个静态界面 * 状态 * 传统view的更新方法 - 单向数据流(ViewModel和LiveData),单向数据流指的是事件向上流动和状态(数据)向下流动 - 将状态管理放到ViewModel也叫状态提升(函数需要两个输入参数:value和onValueChange函数) * Compose的remember * MutableState 状态变化时重组 |
xx | yy |
TodoList的案例
条目 | 内容 |
---|---|
弹出菜单-动画效果的实现 | |
配置软键盘 | TextField(keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done))//将Enter替换为完成按钮 ImeAction.Done;类似的还有ImeAction.Go |
添加带动画阴影和内容动画 | |
编辑模式 | |
保存和恢复状态 | 在重新创建Activity或者进程(被杀死)后,可以使用rememberSaveable恢复界面状态. 存储格式 * Bundle * Parcelize * mapSaver * ListSaver |
总结 |
Run 标准函数
data class Person(var name: String, var age: Int)
val person = Person("John", 30)
val result = with(person) {
name = "Jane"
age = 28
"New name: $name, new age: $age"
}.let { it }
在这个例子中,我们首先使用 with 函数在 person 对象的上下文中执行一段代码块。然后,我们将 with 的返回值传递给 let 函数。由于 let 函数返回其代码块的最后一个表达式,我们可以将其简化为 it。这样,我们就实现了类似 run 的功能。
CompositionLocal
条目 | 内容 |
---|---|
简介 | 显示传参和隐式传参 显示传参 1. 繁琐 2. 隔离较好 隐式传参 1. 方便 2. 一改全改(解决思路:提供provider,修改全局变量,并在函数末尾将数值改回来) 对于广泛使用的常用数据这样传参很麻烦.Compose 提供了CompositionLocal来隐式传递参数.创建以树为作用域的具名对象,让数据流经界面树. MaterialTheme是一个单例. |
主题 | 这个函数修改的是LocalContentAlpha.current,修改为ContentAlpha.medium. |
FruitText(理解current,当前上下文) | 为了访问strings.xml,需要resource,访问resource需要上下文 |
创建自己的CompositionLocal | Elevation(海拔,海拔越高,阴影越大) 提供一些默认值 动态创建和静态创建的区别 compositionLocalOf ,如果修改提供的值,会引起读取该current值的组件重绘. staticCompositionLocalOf ,更改绘导致提供CompositionLocal的整个content lambda被重组,而不仅仅是读取current值的组件. |
主题
条目 | 内容 |
---|---|
是什么 | 1. 定了了很多Color: Primary,secondary,surface - Color.kt 2. 定义了文本样式:h1 h2 h3 Subtitle1 body1 Type.kt 3. 形状:定义了3种类别:小型,中型和大型;每个都可以定义要使用的形状,自定义样式(切割和圆角)和大小-Shape.kt |
定义主题 | 参考创建项目时自动生成的主题 |
使用颜色 | ContentColor为文本颜色 |
处理文本 | Button的默认样式"这里的当前,就是指最近的祖先的值." 一行文字使用不同的样式. |
处理形状 | 举例:Button的形状:大中小; 有些组件会根据主题修改适应上下文,默认情况下TextFiled使用小型形状主题,但它对底角应用零边角大小(就是直角). |
动画和手势
条目 | 内容 |
---|---|
简单值动画 | 将背景色从粉色改成绿色 直接改变背景色也可以,但是没有一个渐变的效果. 任务目标:给颜色改变添加渐变动画 动画对于compose来说,就是一个不断变化的state,连续的重组. 内部是一个协程. 可以打animate看看Android studio 提示,看看还有哪些简单值动画. |
可见性动画 | 可见<->不可见 之间的动画,举例:向上滚动式显示FloatingButton的文字. 另一个案例:消息从顶部滑入滑出 |
内容大小动画 | |
多值动画 | 实现弹性动画 |
重复动画 | |
手势动画 | 实现滑动删除 第一步: 元素跟随手指滑动 第二步: 手指松开,元素由于惯性,继续滑动(根据滑动速度,手指滑动结束位置,最终决定是否要删除 ) 这是一个单行函数的写法,返回值类型为Modifier,这是一个扩展函数. composed是一个高阶函数,它的作用是组合多个 Modifier,并将它们应用于当前的 Modifier。 处理元素滑动 记录滑动速度 记录滑动位置和时间,就可以计算得到速度. 计算投掷速度 由于摩擦力的原因,速度会逐渐衰减. 样条衰减 指数衰减 设置滑动边界 |
手势
条目 | 内容 |
---|---|
点击 | clickable |
滚动 | 可滚动修饰符,scrol lable,可以检测滚动手势,但是不会偏移其内容,需要ScrollableController 才能正常运行. 嵌套滚动,简单的嵌套滚动无需执行任何操作,启动滚动操作的手势会自动从子元素到父元素,子元素无法进一步滚动时,就会由父元素处理. |
拖动 | |
滑动 | 滚动,拖动,滑动的区别 - 滚动,子元素大小超过父容器,只能一部分一部分的看,需要滚动. - 拖动,给元素换坐标 - 滑动 获得推进力后,自动惯性跑一段. 设置阈值,超过30%到下一个锚点. |
多点触控 | 平移,缩放,旋转使用transformable修饰符,不会转换元素,只会检测手势. |
和传统view集成
从XML 创建可组合 | 基于SunFlower项目(官方案例) 把植物详情改成Compose 我们使用了Databinding,所以可以在代码中直接通过ID访问元素. dimen是在values文件夹中中定义的尺寸信息dimens.xml .获取单复数形式. |
在Compose中使用View | |
共用主题 | 使用传统view 的styles.xml |
导航
条目 | 内容 |
---|---|
集成导航 | Overview ,Count,bills |
传统的navigation | - 一定要有一个navigation graph 导航图 - 要有一个NavHostFragment,指定navGraph - 在navigation标签中指定startDestination - 跳转通过findNavController.navigate |
Compose 中的导航 | 这里写的是导航对应的目标页 设置导航部件 支持跳转 |
参数传递 | 案例: 有多个账户,可以跳转到对应账户的页面 |
深层链接 | 在一个应用中打开第三方应用的指定页面 . |
Compose和View 的关系 | 传统view 调用的是SetContentView compose 调用的是 SetContent,内部实际调用的还是SetContentView,传入参数为ComposeView,它集成自ViewGroup,内部东西怎么画的,dispatchDraw->Canvas. jetpack compose的组件没有继承原来view 的那套体系,是通过画布配置的. |
附带效应(副作用)
条目 | 内容 |
---|---|
什么是附带效应 | 不是纯函数 组合函数也分为有副作用和无副作用的. 组合函数的特点: - 执行顺序不定 - 可以并行执行 - 可能频繁的运行(状态改变,就会重组) 可组合函数是为了做声明式UI的,而我们会在UI做一些和界面描述不相关的操作. 处理副作用 * 虽然我们不希望函数执行中出现副作用,但显示是有一些逻辑只能作为副作用来处理,比如IO操作,计时,日志埋点等等,这些都是会对外界或者受到外界影响的逻辑,不能无限制的反复执行.所以compose需要能够合理的处理一些副作用: * 副作用(比如下载)的执行时机是明确的,例如在Recomposition时等 * 副作用的执行次数的可控的,不应该随着函数反复执行. * 副作用(比如组合函数从组件树移除时,如果该组件发了网络请求,应该取消接收)不会造成泄漏,例如对于注册要提供适当的时机取消注册. 危险的附带效应 * 写入共享对象的属性(写全局变量) * 更新ViewModel中的可观察项(数据在多个组件中共享) * 更改共享偏好设置 SharePerference 这些数据都是全局的,共享的 使用Effect API,可以用可预测的方式执行那些副作用.Effect API是和compose的生命周期关联的.何时执行?比如是compose加入到组件树的时候.什么时候释放资源? compose从组件树移除的时候.副作用也是根据这3个时间点来配合的. |
LaunchedEffect & rememberCoroutineScope | 如果要在可组合函数中进行耗时操作,就需要将耗时操作放到协程,协程需要在协程作用域中创建, 所以提供了LaunchedEffect用于创建协程. 副作用一般都是耗时操作,一般用协程来完成. 案例:带动画的消息提示窗(动画是耗时操作,一般在协程中执行) LaunchedEffect 有一个key,key变化它的代码块就会重启. 如果上一个协程没做完,又需要重新执行代码块,就会自动把上个协程取消. 从组件树上移除,会取消协程. |
rememberCoroutineScope | LaunchedEffect 只能在可组合函数中使用,非组合函数可以使用rememberCoroutineScope,该函数可以在可组合函数外启动协程,且需要对这个协程存在作用域限制,以便在协程退出组合后自动取消.协程作用域取消后,它内部的所有协程也会取消. |
rememberUpdatedState & DisposableEffect | |
SideEffect & produceState | |
derivedStateOf & snapshotflow |