KMM Android 项目使用 Compose 构建UI

iOS 的UI先不管,Compose 都还费劲呢。

1. 预览需要传入参数

比如

  1. 列表 LazeColumn 需要接收数据
  2. navigation导航需要提供navController

“注意:我们强烈建议您不要向生产函数(即使其不带参数)添加 @Preview 注释,而是编写一个封装函数并在其中添加 @Preview 注释。“
—— 安卓开发者官网

前面那种情况,新创建一个方法就行

导航这种,官方推荐的是函数传递,也就是把方法作为参数传递进来

@compose
fun HomeScreen(navigateToDetailScreen: (userId:String)->Unit){
    
}

2. 预览失败

提示The following classes could not be found androidx.compose.ui.tooling.ComposeViewAdapter

发现依赖的ui-tool是preview版本,改成下面就可以了

implementation("androidx.compose.ui:ui-tooling:1.1.1")

3. 系统 insets 适配

  1. 通过ViewCompat.setOnApplyWindowInsetsListener可以拿到 WindowInsets
  2. 导入谷歌的新库

implementation("com.google.accompanist:accompanist-insets:0.23.1")

然后使用ProvideWindowInsets配合LocalWindowInsets.current.systemBars获取系统Insets

ProvideWindowInsets(false) {
    val inset = LocalWindowInsets.current.systemBars
    Box{
    }
}

4. Compose 的Padding 如何接受 pixel 而不是 DP

ContentPadding 为内边距
Padding 为外边距(相当于XML那一套的margin)

如果没有特殊需求
导入accompanist-insets后可以直接使用rememberInsetsPaddingValues来给view设置padding,完成适配

否则,只能通过LocalDensity自己转换

val density = LocalDensity.current
appBarHeight = with(density){size.height.toDp()}

5. 高斯模糊效果

调用Modifier.blur(30.dp)发现其只作用于当前View及其子View,对兄弟View是不会有类似iOS那种透视效果的

高斯模糊预览

2022-03-02结论,后续待更新
这里是Box内嵌套LazeColumn和TopAppBar,与预期效果有两点不同

    1. 对 TopAppBar设置的blur同时作用了TopAppBar内部的Text
    1. TopAppBar背景色设置为alpha = 0.5f,但是下层元素并没有影响,也就是没有真的像透过一层毛玻璃去看东西的效果。

6. 如何获取Compose View/控件/元素的宽高

使用 Modifier.onSizeChanged

ProvideWindowInsets(false) {
    val density = LocalDensity.current
    var appBarHeight by remember { mutableStateOf(0.dp) }
    Box {
        TopAppBar(
            modifier = Modifier.blur(4.dp).onSizeChanged { size ->
                appBarHeight = with(density){size.height.toDp()}
            },
            ...
        ),
      ...
}

7. List多列竖排使间距相同

单列有LazyColumn,多列有还在实验阶段的LazyVerticalGrid

设置列的数量

  • 固定=> GridCells.Fixed(spanCount)
  • 不固定=>GridCells.Adaptive(minSize.dp)

设置间距

  • 子元素间的间距使用horizontalArrangementverticalArrangement
  • 子元素与自身的间距使用contentPadding
LazyVerticalGrid(
// 固定列数为2
    cells = GridCells.Fixed(2),
    contentPadding = rememberInsetsPaddingValues(
        inset,
// 不应用顶部Insets
        applyTop = false,
// 顶部手动添加额外 Padding
        additionalTop = appBarHeight,
// 左边 Padding
        additionalStart = 12.dp,
// 右边 Padding
        additionalEnd = 12.dp,
    ),
// 元素水平间距
    horizontalArrangement = Arrangement.spacedBy(12.dp),
// 元素垂直间距
    verticalArrangement = Arrangement.spacedBy(12.dp),
) {
    items(roomInfoList) { roomInfo ->
        RoomItem(roomInfo, nav2Rooms)
    }
}

都设置为12.dp后,效果如下:

LazyVerticalGrid 效果

8. Compose 实现点击旋转360度的刷新效果

为什么动画结束需要手动重置状态,因为默认的插值器是0增大到360,再从360降到0,动画会顺时针、逆时针交替。
同时确保动画连贯,不会有边界情况。

效果:

// 动画标识,是否已经开始
var animateStart by remember { mutableStateOf(false) }
// 当前 Rotate 角度
val animateRotateDegree by animateFloatAsState(
// 目标角度
    if (animateStart) 360f else 0f,
// 动画模式,时长设置
    TweenSpec(if (animateStart) 500 else 0),
    finishedListener = {
// 结束时重置标志
        if (animateStart) animateStart = false
    }
)
IconButton(onClick = {
// 点击后动作
    animateStart = true
}) {
    Icon(
        Icons.Default.Refresh,
        ...
        modifier = Modifier.rotate(animateRotateDegree)
    )
}

流程分析

  • 点击按钮,animateStart = true
  • Rotate 角度改变为 360,开始 500ms 的动画,Icon 开始旋转
  • 500ms后,动画结束,animateStart = false
  • Rotate 角度改变为 0,开始 0ms 的动画,Icon 无需改变
  • 边界情况,开始后200ms再次点击,此时animateStart并未改变,无任何UI影响

你可能感兴趣的:(KMM Android 项目使用 Compose 构建UI)