簡體   English   中英

jetpack compose 中是否有等效的“觸摸代表”?

[英]Is there a 'touch delegate' equivalent in jetpack compose?

android 視圖可以有一個觸摸委托來增加元素的可點擊區域,而不增加它的填充。 jetpack compose 中有類似的東西嗎? 我找不到可以做到這一點的修飾符。

將狀態移動到父級

@Composable
fun ViewScreen() {
    var scale by remember { mutableStateOf(1f) }
    var offset by remember { mutableStateOf(Offset.Zero) }
    var rotation by remember { mutableStateOf(0f) }

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            // touch event
            .pointerInput(Unit) {
                detectTransformGestures(
                    onGesture = { _, pan, gestureZoom, gestureRotate ->
                        scale *= gestureZoom
                        offset = offset.plus(pan)
                        rotation += gestureRotate
                    }
                )
            }
            .fillMaxSize()
    ) {
        Cube(scale, offset, rotation)
    }
}

@Composable
fun Cube(scale: Float, offset: Offset, rotation: Float) {
    Box(
        Modifier
            .graphicsLayer(
                scaleX = scale,
                scaleY = scale,
                rotationZ = rotation,
                translationX = offset.x,
                translationY = offset.y
            )
            .background(Color.Blue)
            .size(256.dp)
    )
}

這是我構建的實現,我沒有反駁計算中的任何錯誤,但是如果您確實發表評論或希望它可以幫助您根據此答案構建更好的實現。

1-) 創建數據類以增加或減少觸摸區域的 dp 大小

@Immutable
data class DelegateRect(
    val left: Dp = 0.dp,
    val top: Dp = 0.dp,
    val right: Dp = 0.dp,
    val bottom: Dp = 0.dp
) {
    companion object {
        val Zero = DelegateRect()
    }
}

@Immutable
data class RectF(
    val left: Float = 0f,
    val top: Float = 0f,
    val right: Float = 0f,
    val bottom: Float = 0f
)

2-) 創建一個組合修飾符來恢復或記住它的狀態

fun Modifier.touchDelegate(
    dpRect: DelegateRect = DelegateRect.Zero,
    onClick: () -> Unit
) =
    composed(
    inspectorInfo = {
        name = "touchDelegate"
        properties["dpRect"] = dpRect
        properties["onClick"] = onClick
    },
        factory = {

            val density = LocalDensity.current

            var initialSize by remember {
                mutableStateOf(IntSize.Zero)
            }

            val updatedRect = remember(dpRect) {
                with(density) {
                    RectF(
                        left = dpRect.left.toPx(),
                        top = dpRect.top.toPx(),
                        right = dpRect.right.toPx(),
                        bottom = dpRect.bottom.toPx(),
                    )
                }
            }


            val scale = remember(initialSize, updatedRect) {
                getScale(initialSize, updatedRect)
            }


            Modifier
                .graphicsLayer {
                    scaleX = scale.x
                    scaleY = scale.y
                    this.translationX = -updatedRect.left
                    this.translationY = -updatedRect.top

                    transformOrigin = TransformOrigin(0f, 0f)
                }
                .clickable {
                    onClick()
                }
                .graphicsLayer {
                    val scaleX = if (scale.x == 0f) 1f else 1 / scale.x
                    val scaleY = if (scale.y == 0f) 1f else 1 / scale.y
                    this.scaleX = scaleX
                    this.scaleY = scaleY
                    this.translationX = (updatedRect.left) * scaleX
                    this.translationY = (updatedRect.top) * scaleY
                    transformOrigin = TransformOrigin(0f, 0f)
                }
                .onSizeChanged {
                    initialSize = it
                }
        }
    )

讓我一步一步解釋

3-) Modifier.graphicsLayer{}可以縮放、平移或旋轉我們的 Composable 層。 它的順序很重要,如果我們在 Modifier.clickable 之前設置它,它會增加可點擊區域和可組合比例。 上面的Modifier.graphicsLayer{}的職責是放大觸摸區域和 Composable,縮放並轉換回原始位置第二個Modifier.graphicsLayer{}是必需的。

4-) 基本上我們縮放為新添加的 dp 大小並左移和上移,因為我們的變換原點通常是中心但更難計算平移。

5-) 向后平移時,我們需要考慮比例反轉。

6-) 為了獲得准確的縮放,我們需要從Modifier.onSizeChanged{}獲得的 Composable 的大小

7-) 使用初始非零大小和我們通過矩形添加觸摸偏移后的大小創建比例的縮放功能

private fun getScale(initialSize: IntSize, updatedRect: RectF): Offset =
    if (initialSize.width == 0 ||
        initialSize.height == 0
    ) {
        Offset(1f, 1f)
    } else {
        val initialWidth = initialSize.width
        val initialHeight = initialSize.height
        val scaleX =
            ((updatedRect.left + updatedRect.right + initialWidth) / initialWidth)
                .coerceAtLeast(0f)
        val scaleY =
            ((updatedRect.top + updatedRect.bottom + initialHeight) / initialHeight)
                .coerceAtLeast(0f)
        Offset(scaleX, scaleY)
    }

用法

Column(
    modifier = Modifier.fillMaxSize(),
    horizontalAlignment = Alignment.CenterHorizontally
) {

    Image(
        painter = painterResource(id = R.drawable.landscape1),
        contentDescription = null,
        contentScale = ContentScale.FillBounds,
        modifier = Modifier
            .size(200.dp)
            .clickable { }
    )

    Spacer(modifier = Modifier.height(40.dp))

    Image(
        painter = painterResource(id = R.drawable.landscape1),
        contentDescription = null,
        contentScale = ContentScale.FillBounds,
        modifier = Modifier
            .size(200.dp)
            .touchDelegate(
                DelegateRect(
                    left = 50.dp,
                    top = 40.dp,
                    right = 70.dp,
                    bottom = 90.dp
                )
            ) {

            }
    )
}

結果

在此處輸入圖像描述

我一直在為此尋找更清潔的解決方案,並偶然發現了ViewConfiguration class。 所以基本上可點擊修飾符依賴LocalViewConfiguration.current來提供默認實現。 如果您已經知道要將觸摸區域增加到的大小,則只需提供此 class 的自定義實現並覆蓋minimumTouchTargetSize以提供更新的大小。 這是視圖配置的示例子類

class CustomViewConfiguration(
    private val viewConfiguration: android.view.ViewConfiguration,
    private val size: Dp
) : ViewConfiguration {
    override val longPressTimeoutMillis: Long
        get() = 
    android.view.ViewConfiguration.getLongPressTimeout().toLong()

    override val doubleTapTimeoutMillis: Long
        get() = 
    android.view.ViewConfiguration.getDoubleTapTimeout().toLong()

    override val doubleTapMinTimeMillis: Long
        get() = 40

    override val touchSlop: Float
        get() = viewConfiguration.scaledTouchSlop.toFloat()

    override val minimumTouchTargetSize: DpSize
        get() = DpSize(size, size)
}

一旦定義好,您可以使用CompositionLocalProider將其提供給您的可組合

CompositionLocalProvider(
    LocalViewConfiguration provides CustomViewConfiguration(
        android.view.ViewConfiguration.get(
            LocalContext.current
        ),
        size.dp
    )
) {
    // Your composable here that needs an increased size

}

如果您事先不知道增加的大小,您可以使用BoxWithConstraints或任何其他SubComposeLayout來測量可組合項並在約束中提供它們以供您使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM