到目前为止,我们了解了推迟列表和推迟网格的作业原理,可是,假如你需求进行自定义,该怎么办呢?

LazyLayout

在RecyclerView中,你可以完成自己的布局管理器。为了在Compose 1.2中到达相同的方针。一个叫做LazyLayout的API.

推迟列表和推迟网格根据LazyLayout API构建.

假如你需求针对自定义用例完成自己的推迟布局,可以考虑为某个现有完成创立分支。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

例如,Compose for Wear OS有一个自定义的Wear专用推迟列表,可根据与组件中心的距离缩放项。它是一个很好的LazyLayout用例。

我们会在内部运用LazyLayout API来构建交错网格。在交错网格中,项在垂直方向可以有不同的高度。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

Item animations

呼声最高的功用之一是为列表项变化增加动画作用。所以在Compose1.1中,针对推迟列表推出了项从头排序动画功用

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

Compose1.2中,将这项功用移植到推迟网格。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

这个API非常简略,你只需为项内容设置animateItemPalcement修饰符,你乃至可以根据需求设定自定义动画规格。

LazyColumn {
    items(books, key = { it.id }) {
        Row(Modifier.animateItemPlacement(
            tween(durationMillis = 250)
        )) {
            ...
        }
    }
}

一定要为项供给键值以便找到被移动元素的新方位

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

除了支撑动画之外,经过供给键,还可以正确处理从头排序。例如,当项的方位发生变化时,将项和组合项中的回忆状态随相应项一同移动

LazyColumn {
    items(books, key = { it.id }) {
        val remembereddValue = remember {
            Random.nextInt()
        }
    }
}

不过对于可用作项键的类型有一条约束,键的类型应受Bundle支撑,这是Android的机制。其作用是在创立activity时保留相应状态。Bundle支撑基元、枚举或Parcelable等类型。

LazyColumn {
    items(books, key= {
        // primitives, enums, Parcelable, etc
    }) {
        ...
    }
}

键必须受Bundle支撑以便在创立activity时,乃至在你翻滚脱离项,然后翻滚回来时,这个项可组合项中rememberSaveable仍可以康复。

LazyColumn {
    items(books, key = { it.id }) {
        val rememberedValue = rememberSaveable {
            Random.nextInt()
        }
    }
}

留意项

看看怎么充分利用推迟布局

第一条,不要运用巨细为0像素的项

在某些状况下你可能会这样做。

例如,当你期望在后期阶段异步检索图片之类的数据来填充列表项时。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

为了说明为什么要避免这样做。我简略介绍一下推迟布局对子项的处理方式,相同的原则适用于一切推迟列表和推迟网格。

但我们举一个详细例子:

LazyColumn在初次丈量项时会认为项的高度没有约束。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

“` val childConstraints = Constraints( maxWidth = if (isVertical) constraints.maxWidth else Constraints.Infinity, maxHeight = if (!isVertical) constraints.maxHeight else Constraints.Infinity ) “`

也便是说,它不会约束项摆放。而会一直组合项,直到运用核算的丈量高度填满可用的视图之后,列表会中止组合子项。这个形式支撑按需增加和移出内容。推迟布局也正是根据这一概念。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

假如列表项的初始高度为0像素,则意味着在初次丈量时LazyColumn会组合它的一切项,因为项的高度为0像素。这些项能轻松地包容到窗口中。

@Composable
fun Item() {
    Image(
        painter = rememberImagePainter(
            data = imageUrl
        ),
    ...
    )
}

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

几毫秒后,图片加载,项重组并开始显现图片。这时推迟列表意识到实践只要一些项能包容到窗口中。所以会放弃初次丈量时不用要地组合起来的其余项。

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

为避免这种状况,你应该为项设置默许巨细,以便推迟布局正确核算实践上有多少项可以包容到窗口中。

@Composable
fun Item() {
    Image(
        painter = rememberImagePainter(
            data = imageUrl
        ),
        modifier = Modifier.size(30.dp),
        ...
    )
}

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

假如你知道自己的项在数据异步加载后的大致巨细,最好的做法是保证加载前后项的巨细保持不变。例如经过增加一些占位符到达这个目的的

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

第二条,避免嵌套可向同一方向翻滚的组件

准确来说,这个提示仅适用于一种状况,也便是将没有预定义尺度的可翻滚子级嵌套在可向同一方向翻滚的另一个父级中。例如,测验在可垂直翻滚的父级Column中嵌套高度不固定的子级LazyColumn.

// Throws IllegalStateException
Column(
    modifier = Modifier.verticalScroll(state)
) {
    LazyColumn {
        ...
    }
}

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

实践上即便你测验这样做,也无法做到,Compose不支撑这种嵌套.这可能听起来凌然不解,因为运用View,你可以在ScrollView内封装RecyclerView,但这是有价值的,功能会遭到严重影响原因和我们在前面所说的,巨细为0像素的项那种状况类似。ScrollView在丈量子项时,会认为子项的高度可以为恣意值,这样,你的RecyclerView就可以不受约束。继而可以当即创立一切项导致无法循环运用。所以Compose会测验引导你避免选用这种形式以避免出现这个问题。

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    ...
    <androidx.recyclerView.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        ... />
    ...
</ScrollView>

而只需将一切可组合项封装在一个父级LazyColumn内,并运用强大的DSL传入不同类型的内容就能完成相同的成果。这样系统就可以在一个方位,既宣布标题等单个列表项,又宣布多个列表项

LazyColumn {
    item {
        Header()
    }
    items(data) { item ->
        Item(item)
    }
    item {
        Footer()
    }
}

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

但请留意,嵌套不同方向布局的景象是答应的。例如在可翻滚的父级Row中嵌套子级LazyColumn

val scrollState = rememberScrollState()
Row(
    modifier = Modifier.horizontalScroll(scrollState)
) {
    LazyColumn {
        ...
    }
}

Jetpack Compose(第四趴)——Compose中的延迟布局(中)

还有一种景象也是答应的,你还是运用相同方向的布局,但一起为嵌套的子级设置固定尺度

val scrollState = rememberScrollState()
Column(
    modifier = Modifier.verticalScroll(scrollState)
) {
    LazyColumn(
        modifier = Modifier.height(200.dp)
    ) {
        ...
    }
}

Jetpack Compose(第四趴)——Compose中的延迟布局(中)