Skip to content

Latest commit

 

History

History
712 lines (492 loc) · 20.7 KB

File metadata and controls

712 lines (492 loc) · 20.7 KB

https://developer.android.com/develop/ui/compose/documentation

Introduction

Thinking in Compose

幂等 side-effect free 只接收输入, 不输出 不修改内部变量

重组

  • 跳过不需要重组的
  • 乐观的, 可能打断, 重新开始重组
  • 可能频繁被调用
  • 并行 一个 composable 方法可能在多个线程执行 可能运行在与调用者不同的线程 调用 composable 方法应该在 UI 线程调用
  • 顺序不定 一个 composable 方法内部有多个 composable 方法, 这几个 composable 方法的执行顺序不能保证

UI archetecture

Lifecycle

初次, 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)
        }
    }
}

Side-effects

附带效应是指发生在可组合函数作用域之外的应用状态的变化。由于可组合项的生命周期和属性(例如不可预测的重组、以不同顺序执行可组合项的重组或可以舍弃的重组),可组合项在理想情况下应该是无附带效应的。

  • 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:将一个或多个状态对象转换为其他状态

重启效应

?

Phases

帧的 3 个阶段

Compose 有 3 个主要阶段:

  • Composition 组合:要显示什么样的界面。Compose 运行可组合函数并创建界面说明。
  • Layout 布局:要放置界面的位置。该阶段包含两个步骤:测量和放置。对于布局树中的每个节点,布局元素都会根据 2D 坐标来测量并放置自己及其所有子元素。
  • Drawing 绘制:渲染的方式。界面元素会绘制到画布(通常是设备屏幕)中。

分阶段状态读取

第 3 阶段:绘制 绘制代码期间的状态读取会影响绘制阶段。常见示例包括 Canvas()、Modifier.drawBehind 和 Modifier.drawWithContent。当状态值发生更改时,Compose 界面只会运行绘制阶段。

重组循环(循环阶段依赖项)

Managing state

在 Compose 中,界面是不可变的,在绘制后无法进行更新。您可以控制的是界面的状态。每当界面的状态发生变化时,Compose 都会重新创建界面树中已更改的部分。可组合项可以接受状态并公开事件,例如 TextField 接受值并公开请求回调处理程序更改值的回调 onValueChange。

State and Jetpack Compose

可组合项的状态

对 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 是建议的新值

状态下降、事件上升的这种模式称为“单向数据流”。

在键发生变化时重新触发 remember 计算

remeber (key) {}

Where to hoist state

Save UI state in Compose

Architecting your Compose UI

单向数据流

Events in Compose

Architectural layering

  • Material
  • Foundation
  • UI
  • Runtime

例如,如果您想为自己的自定义组件添加手势支持,可以使用 Modifier.pointerInput 从头开始构建;但在此之上还有其他更高级别的组件,它们可以提供更好的起点,例如 Modifier.draggable、Modifier.scrollable 或 Modifier.swipeable。

CompositionLocal

Compose 提供了 CompositionLocal,可让您创建以树为作用域的具名对象,这可以用作让数据流经界面树的一种隐式方式。

CompositionLocal 元素通常在界面树的某个节点以值的形式提供。该值可供其可组合项的后代使用,而无需在可组合函数中将 CompositionLocal 声明为参数。

Navigation with Compose

App layout

custom Modifier and custom Layout

basics

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

Modifiers

Order of modifiers matters

requiredSize 不考虑 parent 的限制

offset 不影响 measurement 修饰符顺序很重要 没有外边距修饰符,而只有 padding 修饰符

  • size
  • requiredSize
  • width, height
  • sizeIn
  • wrapContentSize
  • fillMaxSize
  • clip
  • padding

Scope safety in Compose

只有直接 children 可以用 matchParentSize in Box 不影响 parent 的大小 fillMaxSize

weight in Row and Column

Extracting and reusing modifiers

尤其动画等需要多次调用的场景 unscoped modifiers 可以用于所有 scoped modifiers 只能作用于直接的 children 限定作用的方法不生效

Append infix fun then(other: Modifier): Modifier

Constraints

Modifiers in the UI tree

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.

Constraints in the layout phase

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

Modifiers that affect constraints

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

Create custom modifiers

两种方法:

  • A modifier factory
    • wrap existing APIs
    • concatenate with then
  1. CompositionLocal 工厂方法 call site 的 local 值, 而不是被拼接的时候的
  2. 工厂方法也是 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

Common situations using Modifier.Node

class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node()
data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>()
    implement draw() -> drwaCircle()

Zero parameters

class FixedPaddingNode : LayoutModifierNode, Modifier.Node()
data object FixedPaddingElement : ModifierNodeElement<FixedPaddingNode>()
    implement measure()

Referencing composition locals

Animating modifier

Sharing state between modifiers using delegation

Opting out of node auto-invalidation

TODO

List of modifiers

挨个试

Pager

Flow layouts

Custom layouts

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

methods:

  • Use the layout modifier
  • Create custom layouts

Adaptive layouts

TODO

Alignment lines

TODO

Instinsic measurements

ConstraintLayout

TODO

Components

App bars

TODO Navigation with Compose

Theming

Material Design 3 in 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

Custom design systems in Compose

Anatomy of a theme in Compose

Text and typography

Text

  • style text
  • style paragraph
  • maxLine and overflow

TextField

  • Value-based -> State-based

user interaction

  • selectable
  • clickable

fonts, emoji, autofill...

Images and graphics

Image

Painter

  • Bitmap BitmapPainter
  • Vector VectorPainter

painterResource

customize an image

  • Content scale
  • Clip
  • border
  • aspect ratio
  • Color filter
    • tint
    • color matrix
  • blur

customize painter

  • 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() }

Optimizing performance for images

  • 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

Graphics

Overview

  • 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 的变换是应用于整体内容.

Graphics modifiers

  • 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()

Brush: gradients and shaders 渐变和着色器 - 渐变和image

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

Shapes in Compose 多边形以及多边形见的转换动画

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

Animation

Quick guide to Animations in Compose

  • 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)

Animation modifiers and composables

  • 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
      • 淡入淡出;交叉渐变
  • Built-in animation modifiers

    • Animate composable size changes with animateContentSize
  • List item animations

Value-based 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

Animated vector images in Compose

  • AnimatedVectorDrawable

android/views https://developer.android.com/develop/ui/views/animations

Advanced animation example: Gestures

// TODO after learning gesture

Customize animations - AnimationSpec

  • 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)

Shared element transitions in Compose

Touch and input

Pointer input

Understand gestures

Definitions

pointer, pointerEvent, gestures

Different levels of abstraction

  • 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() {}

Event dispatching and hit-testing

hit-testing, sharePointerInputWithSiblings, event propagation logic(same chain of composables).

Event consumption

- 链条上的所有组件都可以手动事件,即使已被其它组件消费
- pointer changes are passed to each composable that it hits

Event propagation

  • In the Initial pass
  • In the Main pass
  • In the Final pass

Tap and press

  • Modifier.clcikable {}
  • Modifier.combinedClickable {}
  • Modifier.pointerInput { detectTapGestures { } }

Scoll

  • rememberScrollState
  • rememberScrollableState
  • Nested scrolling
    • Modifier.nestedScroll(nestedScrollConnection)
  • Nested scrolling interop

Drag, swipe, and fling

Multi-touch gesture

Keyboard input

Focus

User interactions

Stylus input

copy and paste

Input compatibility on large screens

Accessibility

Performance

Layout Inspector Composition tracing

Best practices

  • Use remember
  • Use lazy layout keys
  • Use derivedStateOf
  • Defer reads as long as possible
  • Avoid backwards writes

Style guidelines

  • 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

UI testing

Tools

System capabilities

  • Windown insets
  • Cutouts
  • Picture-in-picture
  • Predictive back

Create widgets