使用 Jetpack Compose 制作动画形状 Android

Animate shape using Jetpack compose for Android

我想使用 compose like stock Android 12 像这样构建一个很棒的锁屏:

我设法做了类似的事情,但是我有 2 个问题,一个是当我使用 pointerInteropFilter 获取动作事件时我使用 remember 值来改变形状,return 在这个函数中我得到了一个类似的动画,但是点击监听器没有被调用,如果我 return false 形状保持在“正方形”,我错过了什么?有什么方法可以使“形状”动画化?我找到了 DP、颜色和尺寸,但没有找到形状。 这是代码

@Composable
fun RoundedPinBackground(
    modifier: Modifier = Modifier,
    backgroundColor: Color,
    onClicked: () -> Unit,
    content: @Composable () -> Unit,
) {
    var selected by remember { mutableStateOf(false) }

    val shape = if (selected) {
        RoundedCornerShape(10.dp)
    } else {
        CircleShape
    }
    Surface(
        tonalElevation = 10.dp,
        modifier = Modifier
            .clip(shape)
    ) {
        Box(modifier = modifier
            .size(80.dp)
            .clip(shape)
            .background(color = backgroundColor)
            .clickable { onClicked.invoke() }
            .pointerInteropFilter {
                when (it.action) {
                    MotionEvent.ACTION_DOWN -> {
                        selected = true
                    }

                    MotionEvent.ACTION_UP -> {
                        selected = false
                    }
                }
                true
            },
            contentAlignment = Alignment.Center
        ) {
            content()
        }
    }
}

这是我的结果

Syntax Description
Motion event true Motion event false

为了设置半径动画,您需要使用 animate*AsState() API 之一。

这里的关键是通过使用 animateDpAsState() 逐渐改变形状的角半径,因为圆是 RoundedCornerShape,角半径是整体尺寸的一半 Circle.

为了获得涟漪效果,您可以使用带有rememberRipple()指示的可点击修饰符。

下面是两者的工作示例:

@Composable
fun RoundedPinBackground(
    modifier: Modifier = Modifier,
    size: Dp,
    backgroundColor: Color,
    onClicked: () -> Unit,
    content: @Composable () -> Unit,
) {
    val interactionSource = remember { MutableInteractionSource() }
    val isPressed = interactionSource.collectIsPressedAsState()
    val radius = if (isPressed.value) {
        10.dp
    } else {
        size / 2f
    }
    val cornerRadius = animateDpAsState(targetValue = radius)

    Surface(
        tonalElevation = 10.dp,
        modifier = modifier
            .clip(RoundedCornerShape(cornerRadius.value))
    ) {
        Box(
            modifier = Modifier
                .background(color = backgroundColor)
                .size(size)
                .clip(RoundedCornerShape(cornerRadius.value))
                .clickable(
                    interactionSource = interactionSource,
                    indication = rememberRipple()
                ) { onClicked.invoke() },
            contentAlignment = Alignment.Center
        ) {
            content()
        }
    }
}