https://developer.android.com/develop/ui/compose/documentation
幂等 side-effect free 只接收输入, 不输出 不修改内部变量
重组
- 跳过不需要重组的
- 乐观的, 可能打断, 重新开始重组
- 可能频繁被调用
- 并行 一个 composable 方法可能在多个线程执行 可能运行在与调用者不同的线程 调用 composable 方法应该在 UI 线程调用
- 顺序不定 一个 composable 方法内部有多个 composable 方法, 这几个 composable 方法的执行顺序不能保证
初次, Composition 之后, Recomposition
贯穿多次重组的可组合项实例
使用 key 可组合项帮助 Compose 识别组合中的可组合项实例。当从同一个调用点调用多个可组合项,且这些可组合项包含附带效应或内部状态时,这一点非常重要。
@Composable
fun MoviesScreenWithKey(movies: List<Movie>) {
Column {
for (movie in movies) {
key(movie.id) { // Unique ID for this movie
MovieOverview(movie)
}
}
}
}
@Composable
fun MoviesScreenLazy(movies: List<Movie>) {
LazyColumn {
items(movies, key = { movie -> movie.id }) { movie ->
MovieOverview(movie)
}
}
}
附带效应是指发生在可组合函数作用域之外的应用状态的变化。由于可组合项的生命周期和属性(例如不可预测的重组、以不同顺序执行可组合项的重组或可以舍弃的重组),可组合项在理想情况下应该是无附带效应的。
-
LaunchedEffect:在某个可组合项的作用域内运行挂起函数
-
rememberCoroutineScope:获取组合感知作用域,以便在可组合项外启动协程
onClick = {
// Create a new coroutine in the event handler to show a snackbar
scope.launch {
snackbarHostState.showSnackbar("Something happened!")
}
}
-
rememberUpdatedState:在效应中引用某个值,该效应在值改变时不应重启
-
isposableEffect:需要清理的效应
-
SideEffect:将 Compose 状态发布为非 Compose 代码
-
produceState:将非 Compose 状态转换为 Compose 状态
-
derivedStateOf:将一个或多个状态对象转换为其他状态
?
Compose 有 3 个主要阶段:
- Composition 组合:要显示什么样的界面。Compose 运行可组合函数并创建界面说明。
- Layout 布局:要放置界面的位置。该阶段包含两个步骤:测量和放置。对于布局树中的每个节点,布局元素都会根据 2D 坐标来测量并放置自己及其所有子元素。
- Drawing 绘制:渲染的方式。界面元素会绘制到画布(通常是设备屏幕)中。
第 3 阶段:绘制 绘制代码期间的状态读取会影响绘制阶段。常见示例包括 Canvas()、Modifier.drawBehind 和 Modifier.drawWithContent。当状态值发生更改时,Compose 界面只会运行绘制阶段。
在 Compose 中,界面是不可变的,在绘制后无法进行更新。您可以控制的是界面的状态。每当界面的状态发生变化时,Compose 都会重新创建界面树中已更改的部分。可组合项可以接受状态并公开事件,例如 TextField 接受值并公开请求回调处理程序更改值的回调 onValueChange。
对 value 所做的任何更改都会安排对读取 value 的所有可组合函数进行重组。
在可组合项中声明 MutableState 对象的方法有三种:
val mutableState = remember { mutableStateOf(default) }var value by remember { mutableStateOf(default) }val (value, setValue) = remember { mutableStateOf(default) }
这些声明是等效的,以语法糖的形式针对状态的不同用法提供。您选择的声明应该能够在您编写的可组合项中生成可读性最高的代码。
虽然 remember 可帮助您在重组后保持状态,但不会帮助您在配置更改后保持状态。为此,您必须使用 rememberSaveable
其他可观察类型。在 Compose 中读取其他可观察类型之前,您必须将其转换为 State Flow, withLivecycle LiveData RxJava
Compose 中的状态提升,是一种将状态移至可组合项的调用方,使可组合项变成无状态的模式。Jetpack Compose 中的常规状态提升模式是将状态变量替换为两个参数:
- value: T:要显示的当前值
- onValueChange: (T) -> Unit:请求更改值的事件,其中 T 是建议的新值
状态下降、事件上升的这种模式称为“单向数据流”。
remeber (key) {}
- Material
- Foundation
- UI
- Runtime
例如,如果您想为自己的自定义组件添加手势支持,可以使用 Modifier.pointerInput 从头开始构建;但在此之上还有其他更高级别的组件,它们可以提供更好的起点,例如 Modifier.draggable、Modifier.scrollable 或 Modifier.swipeable。
Compose 提供了 CompositionLocal,可让您创建以树为作用域的具名对象,这可以用作让数据流经界面树的一种隐式方式。
CompositionLocal 元素通常在界面树的某个节点以值的形式提供。该值可供其可组合项的后代使用,而无需在可组合函数中将 CompositionLocal 声明为参数。
custom Modifier and custom Layout
parents measure before their children, but are sized and placed after their children. measurement , being sized are different
Compose achieves high performance by measuring children only once
Order of modifiers matters
requiredSize 不考虑 parent 的限制
offset 不影响 measurement 修饰符顺序很重要 没有外边距修饰符,而只有 padding 修饰符
- size
- requiredSize
- width, height
- sizeIn
- wrapContentSize
- fillMaxSize
- clip
- padding
只有直接 children 可以用 matchParentSize in Box 不影响 parent 的大小 fillMaxSize
weight in Row and Column
尤其动画等需要多次调用的场景 unscoped modifiers 可以用于所有 scoped modifiers 只能作用于直接的 children 限定作用的方法不生效
Append infix fun then(other: Modifier): Modifier
For example, when you chain a clip and a size modifier, the clip modifier node wraps the size modifier node, which then wraps the Image layout node
To summarize:
- Modifiers wrap a single modifier or layout node.
- Layout nodes can lay out multiple child nodes.
The layout phase follows a three-step algorithm to find each layout node's width, height, and x, y coordinate:
- Measure children: A node measures its children, if any.
- Decide own size: Based on those measurements, a node decides on its own size.
- Place children: Each child node is placed relative to a node's own position.
Type of constraints
- Bounded - a maximum and minimum width and height
- Unbounded - not constrained to any size
- Exact - an exact size requirement. The minimum and maximum bounds are set to the same value
- Combination - width, height respectly constrained type
size - preferred size of the content adapt to match constraints or set constraints to x,y = exact type requiredSize - override incoming constraints - Bounded width,height - a fixed width or height - = size, exact type sizeIn - set exact minimum and maximum constraints for width and height - Bounded
clip does not change constraints
reported size to be perceived by parent clip act by reported size
padding change the canvas size
两种方法:
- A modifier factory
- wrap existing APIs
- concatenate with then
- CompositionLocal 工厂方法 call site 的 local 值, 而不是被拼接的时候的
- 工厂方法也是 Composable 方法,不能被提升到 out of Composable
- A modifier element - Modifier.Node
包含三部分
- Modifier.Node - stateful - implementation that holds the logic and state of your modifier.
- ModifierNodeElemen - stateless - that creates and updates modifier node instances.
- An optional modifier factory as detailed above
class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node()
data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>()
implement draw() -> drwaCircle()
class FixedPaddingNode : LayoutModifierNode, Modifier.Node()
data object FixedPaddingElement : ModifierNodeElement<FixedPaddingNode>()
implement measure()
TODO
挨个试
Laying out each node in the UI tree is a three step process. Each node must:
- Measure any children
- Decide its own size
- Place its children
- Use the layout modifier
- Create custom layouts
TODO
TODO
TODO
TODO Navigation with Compose
- Color
- Primary, Secondary, Tertiary, Neutral
- isLight
- Dynamic color make use of device wallpaper colors
- Typography display, headline, title, body, label(Large, Medium, Small)
- Shape extraSmall, small, medium, large, extraLarge
- style text
- style paragraph
- maxLine and overflow
- Value-based -> State-based
- selectable
- clickable
Painter
- Bitmap BitmapPainter
- Vector VectorPainter
painterResource
- Content scale
- Clip
- border
- aspect ratio
- Color filter
- tint
- color matrix
- blur
- Painter
- DrawModifier The above custom Painter can also be implemented using a DrawModifier. If you need to influence measurement or layout, then you should use a Painter. If you are only expecting to render in the bounds you are given, then you should use a DrawModifier instead.
- onDraw() { drawImage() }
- Only load the size of the bitmap you need
- Use vectors over bitmaps where possible
- Supply alternative resources for different screen sizes
- When using ImageBitmap, call prepareToDraw before drawing
- Prefer passing a Int DrawableRes or URL as parameters into your composable instead of Painter
- Don’t store a bitmap in memory longer than you need it
- Don’t package large images with your AAB/APK file
-
modifier, DrawScope Modifier.drawWithContent, Modifier.drawBehind, and Modifier.drawWithCache Canvas = Spacer(modifier.drawBehind(onDraw)) drawWithCache 是 Modifier 的方法 CacheDrawScope 中还有 onDrawContent, onDrawBehind()
-
Coordinate system
Modifier.drawWithContent, Modifier.drawBehind 包含 DrawScope, DrawScope 有下列方法 画基本图形和基本的形变
below is DrawScopes
- Basic transformation
- scale
- translate
- rotate
- inset
- Multi transformation
- Common drawing operations
- draw text measure text
- draw image
- Draw basic shapes
- Draw path
- Accessing Canvas object - to native canvas DrawScope.drawIntoCanvas()
interface DrawScope : Density
DrawScope 是用于绘制图形的 API, GraphicsLayerScope 是与图层变换和效果相关的 API. DrawScope 可以对不同的内容进行不同的变换, GraphicsLayerScope 的变换是应用于整体内容.
- Drawing modifiers
- Modifier.drawWithContent: Choose drawing order - 绘制顺序 call drawContent() in it 把 drawConent() 放在自定义绘制的内容上或下
- Modifier.drawBehind: Drawing behind a composable Canvas: a convenient wrapper around Modifier.drawBehind
- Modifier.drawWithCache: Drawing and caching draw objects 如果要 draw 中创建对象, 使用这个方法; 对象被cached, 只要大小不变, 对象不会重新创建 drawWithCache 的接收者 CacheDrawScope 并没有绘制方法/变形方法; 需要调用 onDrawBehind, onDrawContent 并在里面进行绘制 or using remember, outside of the modifier 或者自己使用 remember
TODO
- Graphics modifiers
-
Modifier.graphicsLayer 通过属性来实现
- scale, translate, rotation, Origin,
- clip and shape, alpha fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true) clip() 与 clip = ture, shap= 可以拼接
-
Compositing strategy // TODO
- Auto (default), Offscreen, ModulateAlpha
-
interface GraphicsLayerScope : Density
-
Write contents of a composable to a bitmap
- create a GraphicsLayer using rememberGraphicsLayer()
- using drawWithContent() and graphicsLayer.record{}
-
Custom drawing modifier
- DrawModifier 重写 ContentDrawScope.draw()
it determines the color(s) that are drawn in the drawing area (i.e. a circle, square, path) such as LinearGradient, RadialGradient or a plain SolidColor brush; Brushes can be used with Modifier.background(), TextStyle, or DrawScope draw calls
- Gradient brushes
- horizontalGradient(), verticalGradient(), linearGradient(), sweepGradient(), radialGradient()
- colorStops, Change distribution of colors
- TileMode, Repeat a pattern
- Change brush Size, custom Brush
- Use an image as a brush
- background, TextStyle, drawXX(shaderBrush)
- Advanced example: Custom brush
- AGSL RuntimeShader brush // TODO
graphics-shapes library: morphing between these polygon shapes.
- Create polygons RoundedPolygon
- Morph shapes A Morph object is a new shape representing an animation between two polygonal shapes.
- Use polygon as clip
- Morph button on click
- Animate shape morphing infinitely
- Custom polygons
-
Start an animation on launch of a composable LaunchedEffect
-
连续动画 LaunchedEffect 内部多个动画 (suspend) 是阻塞的
-
并发动画 LaunchedEffect 内部为每个多个动画 (suspend) 创建一个协程, 即可实现并发
-
Optimize animation performance deferring reads, 重组过程中靠后的阶段
-
Change animation timing - animationSpec
- tween, spring
- keyframes
- snap
- infiniteRepeatable, repeatable(RepeatMode)
-
Built-in animated composables
- Animate appearance and disappearance with AnimatedVisibility (Transition)
- 一个(组)元素的显示隐藏 visible, enter, exit
- Animate based on target state with AnimatedContent
- 两(多)个元素的转换, 或者子组件内容有变化
- Animate child enter and exit transitions
- Animate between two layouts with Crossfade
- 淡入淡出;交叉渐变
- Animate appearance and disappearance with AnimatedVisibility (Transition)
-
Built-in animation modifiers
- Animate composable size changes with animateContentSize
-
List item animations
-
Animate a single value with animate*AsState animateValueAsState -> State() AnimationSpec
- Out of the box, Compose provides animate*AsState functions for Float, Color, Dp, Size, Offset, Rect, Int, IntOffset, and IntSize.
- support for other data types by providing a TwoWayConverter to animateValueAsState that takes a generic type.
- customize AnimationSpec (value-time)
-
Animate multiple properties simultaneously with a transition Transition 就是同时动画多个属性 transitionSpec : transitionSpec: @Composable Transition.Segment[S].() -> FiniteAnimationSpec
- updateTransition, transition.animateXX
- Use transition with AnimatedVisibility and AnimatedContent
- AnimatedVisibility 内容的显示和隐藏, 可以指定入场出厂动画
- AnimatedContent 不同内容的转换,
- 通过 transition 调用的 AnimatedVisibility 和 AnimatedContent, 把动画提升到外部, 可以作用其它组件共享动画
- Encapsulate a transition and make it reusable
-
Create an infinitely repeating animation with rememberInfiniteTransition
- rememberInfiniteTransition, infiniteRepeatable, RepeatMode
-
Low-level animation APIs
- Animatable: Coroutine-based single value animation
TwoWayConverter, animate*AsState 的基础
- animateTo()
- snapTo()
- decayTo()
- Animation: Manually controlled animation
Animation should only be used to manually control the time of the animation
- TargetBasedAnimation
- DecayAnimation Decay animations are often used after a fling gesture to slow elements down to a stop
- Animatable: Coroutine-based single value animation
TwoWayConverter, animate*AsState 的基础
- AnimatedVectorDrawable
android/views https://developer.android.com/develop/ui/views/animations
// TODO after learning gesture
-
Customize animations with the AnimationSpec parameter
- spring, tween,
- keyframe, keyframeWithSpine,
- RepeatableSpec, InfiniteRepeatableSpec
- SnapSpec
-
Set a custom easing function - Easing
-
Animate custom data types by converting to and from AnimationVector ( ~ PropertyValueHolder)
pointer, pointerEvent, gestures
- Component support onClick = {}, Modifier.clickable {}
- Add specific gestures to arbitrary composables with modifiers
- taps and presses
- Modifier.horizontalScroll,
- draggable, swipable
- multi-touch gestures
- Add custom gesture to arbitrary composables with pointerInput modifier
- Modifier.pointerInput() {}
hit-testing, sharePointerInputWithSiblings, event propagation logic(same chain of composables).
- 链条上的所有组件都可以手动事件,即使已被其它组件消费
- pointer changes are passed to each composable that it hits
- In the Initial pass
- In the Main pass
- In the Final pass
- Modifier.clcikable {}
- Modifier.combinedClickable {}
- Modifier.pointerInput { detectTapGestures { } }
- rememberScrollState
- rememberScrollableState
- Nested scrolling
- Modifier.nestedScroll(nestedScrollConnection)
- Nested scrolling interop
Layout Inspector Composition tracing
- Use remember
- Use lazy layout keys
- Use derivedStateOf
- Defer reads as long as possible
- Avoid backwards writes
- Default arguments
- Higher-order functions and lambda expressions
- Trailing lambdas
- Scopes and receivers
- Delegated properties by
- Destructing data classes
- Singleton objects object class
- Type-safe builders and DSLs
- Kotlin coroutines
- Windown insets
- Cutouts
- Picture-in-picture
- Predictive back