Android 像 Google 周日历一样撰写

问题描述 投票:0回答:1

我想在撰写中复制 Google 周日历视图,但卡在滚动上。我使用

detectTransformGestures()
来缩放和翻译内容,但是一旦我松开手指,翻译就会停止。您将如何添加滚动行为,一旦您放手,该行为就会继续滚动(就像普通的 Recyclerview 或 compose 中的
.scrolling()
一样)。

我找到了https://github.com/alamkanak/Android-Week-View,但似乎过于复杂并且在xml中使用。我想要更干净、更容易使用的东西。

这是我当前的代码。内容还不限于顶部和底部,您可以根据需要缩放。

@Composable
fun WeekCalendar() {

    var scaleY by remember {
        mutableStateOf(1f)
    }
    var offset by remember {
        mutableStateOf(0f)
    }

    var centroi by remember {
        mutableStateOf(0f)
    }



    Canvas(modifier = Modifier

        .pointerInput(Unit) {
            this.detectTransformGestures() { centroid, pan, zoom, rotation ->

                scaleY *= zoom
                offset += pan.y * zoom
                // center zooming around point location
                offset += (zoom - 1) * (offset - centroid.y)
                centroi = centroid.y
            }
        }
        .fillMaxWidth()
        .height(1200.dp)
    ) {
        val bottom = size.height * scaleY + offset
        val top = offset

        drawLine(
            color = Color.Red,
            start = Offset(0f, top),
            end = Offset(size.width, top),
        )

        drawLine(
            color = Color.Red,
            start = Offset(size.width, top),
            end = Offset(size.width, bottom),
        )

        drawLine(
            color = Color.Red,
            start = Offset(size.width, bottom),
            end = Offset(0f, bottom),
        )

        drawLine(
            color = Color.Red,
            start = Offset(0f, bottom),
            end = Offset(0f, top),
        )

        drawLine(
            color = Color.Red,
            start = Offset(0f, top),
            end = Offset(size.width, bottom),
        )

        drawPoints(listOf(Offset(size.width / 2, centroi)), PointMode.Points, color = Color.Blue, strokeWidth = 5f)
    }
}
android android-studio calendar android-jetpack-compose composable
1个回答
0
投票

我不知道这是否仍然相关,但我找到了一种使用 2023.08.00 Compose BOM

remember()
来实现的方法。为了避免处理滚动逻辑,我使用了带有内置垂直滚动扩展的包装
Column
。缩放逻辑与您的相同,但在重组之间保持不变。

@Composable
fun CalendarScreenDemo(modifier: Modifier = Modifier) {
    val baseHeight = 60.dp * 24
    var scaleY by remember { mutableFloatStateOf(1f) }
    val scrollState = rememberScrollState()
    val coroutineScope = rememberCoroutineScope()

    Column(
        modifier = modifier.verticalScroll(scrollState)
    ) {
        Canvas(
            modifier = modifier
                .height(baseHeight * scaleY)
                .fillMaxWidth()
                .pointerInput(Unit) {
                    detectZoomChanges zoom@{ centroid, zoom ->
                        val oldScaleY = scaleY
                        // Constrain min/max zoom
                        scaleY = (scaleY * zoom).coerceIn(0.75f..2f)

                        // Don't move scroll position if no effective zoom occured
                        val actualZoom = scaleY / oldScaleY
                        val scrollY = scrollState.value * actualZoom

                        // Center zooming around gesture origin
                        val scrollOffset = (zoom - 1) * (scrollY - centroid.y)

                        coroutineScope.launch {
                            scrollState.scrollTo(scrollY.roundToInt() - scrollOffset.roundToInt())
                        }
                    }
                },
        ) {
            val gradient = Brush.verticalGradient(
                listOf(Color.Red, Color.Blue, Color.Green),
                0.0f,
                (baseHeight * scaleY).toPx(),
                TileMode.Repeated
            )
            drawRect(gradient)
        }
    }
}

这与 Google 日历的缩放功能类似,例如当达到最大/最小缩放时,进一步的缩放手势将滚动屏幕。

detectZoomChanges()
函数基于内置函数
detectTransformgestures()
,稍作修改以仅消耗缩放事件。我刚刚删除了会消耗平移和旋转事件的部分。这样,平移事件仍然可以向上冒泡到周围的列,并作为滚动手势进行处理。 (缩放时仍然需要手动调整滚动位置,但至少它可以工作。)

suspend fun PointerInputScope.detectZoomChanges(
    onGesture: (centroid: Offset, zoom: Float) -> Unit
) {
    awaitEachGesture {
        var zoom = 1f
        var pastTouchSlop = false
        val touchSlop = viewConfiguration.touchSlop

        awaitFirstDown(requireUnconsumed = false)
        do {
            val event = awaitPointerEvent()
            val canceled = event.changes.fastAny { it.isConsumed }
            if (!canceled) {
                val zoomChange = event.calculateZoom()

                if (!pastTouchSlop) {
                    zoom *= zoomChange

                    val centroidSize = event.calculateCentroidSize(useCurrent = false)
                    val zoomMotion = abs(1 - zoom) * centroidSize

                    if (zoomMotion > touchSlop) {
                        pastTouchSlop = true
                    }
                }

                if (pastTouchSlop) {
                    val centroid = event.calculateCentroid(useCurrent = false)
                    if (zoomChange != 1f) {
                        onGesture(centroid, zoomChange){
                        event.changes.fastForEach {
                            if (it.positionChanged()) {
                                it.consume()
                            }
                        }
                    }
                }
            }
        } while (!canceled && event.changes.fastAny { it.pressed })
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.