当前位置: 首页 > 知识库问答 >
问题:

在jetpack compose中是否有等效的“触摸委托”?

吉玉石
2023-03-14

android视图可以有一个触摸委托来增加元素的可点击区域,而不增加它的填充。jetpack compose中是否有类似的东西?我找不到可以做到这一点的修饰符。

共有3个答案

谢典
2023-03-14

我正在寻找一个更简洁的解决方案,无意中发现了ViewConfiguration类。所以基本上clickable modifier依靠< code > localview configuration . current 来提供默认实现。如果您已经知道您想要增加触摸区域的大小,您可以只提供这个类的自定义实现,并覆盖< code > minimumtuchtargetsize 来提供更新的大小。以下是视图配置的子类别示例

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

}

如果您事先不知道增加的大小,您可以使用< code>BoxWithConstraints或任何其他< code>SubComposeLayout来度量组件并在约束中提供它们供您使用。

甄成弘
2023-03-14

这是实现我建立,我没有反驳任何错误的计算,但如果你做评论或希望它可以帮助你建立更好的基于这个答案。

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之前设置它,它会增加可点击区域和Composable缩放。顶部的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
                )
            ) {

            }
    )
}

后果

欧阳安阳
2023-03-14

将状态移至父级

@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)
    )
}
  • 状态-吊装
  • 手势
 类似资料:
  • 请注意,我使用以下代码得到了相同的结果: 所以问题是,fooObservable直到订阅了PublishSubject之后才订阅PublishSubject, 是否有一种方法可以在第一次订阅FooObservable之后立即运行代码? 如果请求与已经订阅的请求匹配,那么observable应该在订阅时立即提供最新的匹配值。 当没有订阅者时,我需要取消我包装的服务的订阅。

  • 问题内容: 使用以下代码: 我得到以下html: 我怎样才能没有任何标签?是否有InnerText等效项? 问题答案: 所有你需要的是:

  • 问题内容: 尝试在Java / Swing中构建GUI应用程序。我主要习惯于使用VB之类的工具在Windows方面“绘画” GUI(或更准确地说,是Gupta SQLWindows …想知道有多少人知道那是什么;-)。 我在Swing中找不到等效的Group Box … 使用组框,您将在几个相关小部件周围有一个方形框(通常带有标题)。一个示例是围绕几个单选按钮的分组框(标题说明了单选按钮的含义,例

  • 问题内容: 我们需要查看对象在Javascript中具有哪些方法/字段。 问题答案: 正如其他人所说,您可以使用Firebug,这将使您在Firefox上无后顾之忧。Chrome和Safari都具有内置的开发人员控制台,该控制台具有与Firebug控制台几乎相同的界面,因此您的代码应可跨这些浏览器移植。对于其他浏览器,有Firebug Lite 。 如果您不适合使用Firebug,请尝试以下简单脚

  • 问题内容: 一些数据库支持以下命令: 本质上,我需要提取前10条记录,然后提取10条记录,然后提取10条记录,等等。也许有另一种方式可以做到这一点,但是在过去,对于支持“ START AT”的数据库,我已经像上面那样完成了。 问题答案: 哪个版本的SQL Server? 在SQL Server 2000中,这确实是一个痛苦(尽管可以使用诸如stingyjack发布的丑陋技巧)。 在2005年及之后

  • ChromeAPI提供了一个获取当前窗口(返回包含当前正在执行的代码的窗口)的方法。Safari有等效的方法吗?