AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 77251037
Accepted
Oleksandr Myronchuk
Oleksandr Myronchuk
Asked: 2023-10-08 02:34:23 +0800 CST2023-10-08 02:34:23 +0800 CST 2023-10-08 02:34:23 +0800 CST

Jetpack Compose - 将点击传播到较低的 UI 层

  • 772

我正在努力为我的应用程序创建“入门”体验,这本质上是有关如何使用该应用程序的快速指南。为了实现这一目标,我希望用户单击应用程序指示他们的位置。具体来说,我想创建一个用户界面,其中包含允许用户单击的圆形元素和不允许单击的灰色透明背景(为了清楚起见,请参阅附图)

在此输入图像描述

我当前的挑战在于弄清楚如何将此单击事件传播到较低的 UI 层。

这是相关的代码片段:

        Canvas(
        modifier = Modifier
            .fillMaxSize()
            .zIndex(3f)
            .pointerInput(Unit) {
                detectTapGestures(onTap = { offset ->
                    if (!Rect(position, volume).contains(offset)) {
                        Log.d("app-debugger9","Cancel the click!")
                    }
                    else
                    {
                        Log.d("app-debugger9","Propagate click to below UI layers!")
                    }
                })
            }
    ) {
        val circlePath = Path().apply {
            addOval(Rect(position, volume))
        }
        clipPath(circlePath, clipOp = ClipOp.Difference) {
            drawRect(SolidColor(Color.Black.copy(alpha = alphaValue)))
        }
    }

Log.d(“app-debugger9”,“将单击传播到较低的 UI 层!”) - 此 Log.d 语句指示我打算将单击事件传播到较低的 UI 层的时刻。我的问题是:我怎样才能实现这一目标?

目前,无论我单击圆内还是圆外的位置,我都无法将该单击传播到较低的 UI 层。

我期望的是,如果我在圆圈外单击,则该单击将被忽略。但是,如果我在圆圈内部单击,则单击会传播到较低的 UI 层,从而导致打开关卡(因为该圆圈位于关卡打开按钮上方)。

注意:1)不使用“pointerInput(Unit)”也可以完成此操作,但我不确定如何做到这一点,所以我现在选择使用“pointerInput(Unit)”。2) 当我引用“上方”或“下方”UI 层时,我使用的是 .zIndex()。如果指数较大,则表示“高于”,如果指数较小,则表示“较低”。

kotlin
  • 1 1 个回答
  • 60 Views

1 个回答

  • Voted
  1. Best Answer
    Thracian
    2023-10-08T15:33:46+08:002023-10-08T15:33:46+08:00

    使用 Modifier.clickable 或 PointerEventScope.detectTapGestures,您无法将手势传播到后代或父级,因为它们调用PointeEventChange.consume(),您可以查看有关Jetpack Compose 中的手势和手势传播的答案。但就您而言,您似乎正在尝试将事件传播到不可能的位置,在我看来,这是一个糟糕的设计。

    对于任何想要将手势传递给父母而不是兄弟姐妹的人,您可以使用 轻松编写自定义上下检测手势awaitEachGesture。

    @Preview
    @Composable
    private fun GesturePrpoagation() {
    
        val context = LocalContext.current
    
        Box(modifier = Modifier
            .fillMaxSize()
            .border(2.dp, Color.Red)
            .pointerInput(Unit) {
    //            detectTapGestures {
    //                Toast.makeText(context, "Parent tapped", Toast.LENGTH_SHORT).show()
    //            }
                awaitEachGesture {
                    val down: PointerInputChange = awaitFirstDown()
                    val up: PointerInputChange? = waitForUpOrCancellation()
                    Toast.makeText(context, "Parent tapped", Toast.LENGTH_SHORT).show()
                }
            }
        ) {
    
            Box(modifier = Modifier
                .background(Color.Red)
                .fillMaxSize()
                .zIndex(1f)
                .pointerInput(Unit) {
    //                detectTapGestures {
    //                    Toast.makeText(context, "Box1 tapped", Toast.LENGTH_SHORT).show()
    //                }
                    awaitEachGesture {
                        val down: PointerInputChange = awaitFirstDown()
                        val up: PointerInputChange? = waitForUpOrCancellation()
                        Toast.makeText(context, "Box1 tapped", Toast.LENGTH_SHORT).show()
                    }
                }
            )
    
            Box(modifier = Modifier
                .background(Color.Black.copy(alpha = .5f))
                .fillMaxSize()
                .zIndex(2f)
                .pointerInput(Unit) {
    //                detectTapGestures {
    //                    Toast.makeText(context, "Box2 tapped", Toast.LENGTH_SHORT).show()
    //                }
                    awaitEachGesture {
                        val down: PointerInputChange = awaitFirstDown()
                        val up: PointerInputChange? = waitForUpOrCancellation()
                        Toast.makeText(context, "Box2 tapped", Toast.LENGTH_SHORT).show()
                    }
                }
            )
        }
    }
    

    根据 Box1 和 Box2 的 zIndex,您将看到 zIndex 较大的一个将触发事件,如果默认情况下均为零,则将为第二个 Box 触发事件,然后传播到父级。您可以通过食用其中任何一个来选择性地改变这一点PointerInputChange。

    在 OP 情况下,不需要第二个 Box。可以使用 Modifier.drawWithContent 绘制带有圆形剪辑的黑色透明层,并根据触摸位置触发该层或任何组件中的单击事件。

    您可以看到,虽然点击手势被消耗,但除非您消耗手势,否则它们会被传播。

    @Preview
    @Composable
    private fun TouchLayerSample() {
    
        var isTouched by remember {
            mutableStateOf(false)
        }
    
        val context = LocalContext.current
    
        Column(
            modifier = Modifier.fillMaxSize()
                .pointerInput(Unit) {
                    val size = this.size
                    val center = size.center
                    detectTapGestures { offset: Offset ->
    
                        val circleCenter = Offset(
                            x = center.x.toFloat(),
                            y = size.height * .3f
                        )
    
                        // This is for measuring distance from center of circle to
                        // touch position to invoke only invoke when touch is inside circle
                        isTouched = isTouched(
                            center = circleCenter,
                            touchPosition = offset,
                            radius = 200f
                        )
    
                        if (isTouched) {
                            Toast.makeText(context, "Circle is touched", Toast.LENGTH_SHORT).show()
                        }
    
                    }
                }
                .drawWithCache {
    
                    val center = this.size.center
    
                    val circlePath = Path().apply {
                        addOval(
                            Rect(
                                center = Offset(
                                    x = center.x,
                                    y = size.height * .3f
                                ),
                                radius = 200f
                            )
                        )
                    }
                    onDrawWithContent {
                        drawContent()
                        clipPath(circlePath, clipOp = ClipOp.Difference) {
                            drawRect(SolidColor(Color.Black.copy(alpha = .8f)))
                        }
    
                    }
                }
        ) {
    
            // This can be any content that is behind transparent black layer.
    
            // If you have a clickable component here you can use isTouched with custom gesture in previous example to not consume it inside detectTapGesture, so it can be propagated to component. In that case you need to also change PointerEventPass to Initial to propagate from ancestor to descendant.
    
            Image(
                modifier = Modifier.fillMaxSize(),
                painter = painterResource(R.drawable.landscape1),
                contentDescription = null,
                contentScale = ContentScale.FillBounds
            )
        }
    }
    
    private fun isTouched(center: Offset, touchPosition: Offset, radius: Float): Boolean {
        return center.minus(touchPosition).getDistanceSquared() < radius * radius
    }
    

    在此输入图像描述

    • 1

相关问题

  • 使用 ?.get 访问参数时 Kotlin 引发错误 Unresolved reference

  • Kotlin - 如何正确并行处理数据?

  • 如何在 Compose 中结合注释和样式?

  • Kotlin 收集 SharedFlow 未收集

  • 如何在 Spring Data Elasticsearch 中使用多术语过滤器?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    使用 <font color="#xxx"> 突出显示 html 中的代码

    • 2 个回答
  • Marko Smith

    为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类?

    • 1 个回答
  • Marko Smith

    您可以使用花括号初始化列表作为(默认)模板参数吗?

    • 2 个回答
  • Marko Smith

    为什么列表推导式在内部创建一个函数?

    • 1 个回答
  • Marko Smith

    我正在尝试仅使用海龟随机和数学模块来制作吃豆人游戏

    • 1 个回答
  • Marko Smith

    java.lang.NoSuchMethodError: 'void org.openqa.selenium.remote.http.ClientConfig.<init>(java.net.URI, java.time.Duration, java.time.Duratio

    • 3 个回答
  • Marko Smith

    为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)?

    • 4 个回答
  • Marko Smith

    为什么库中不调用全局变量的构造函数?

    • 1 个回答
  • Marko Smith

    std::common_reference_with 在元组上的行为不一致。哪个是对的?

    • 1 个回答
  • Marko Smith

    C++17 中 std::byte 只能按位运算?

    • 1 个回答
  • Martin Hope
    fbrereto 为什么在传递 {} 时重载解析更喜欢 std::nullptr_t 而不是类? 2023-12-21 00:31:04 +0800 CST
  • Martin Hope
    比尔盖子 您可以使用花括号初始化列表作为(默认)模板参数吗? 2023-12-17 10:02:06 +0800 CST
  • Martin Hope
    Amir reza Riahi 为什么列表推导式在内部创建一个函数? 2023-11-16 20:53:19 +0800 CST
  • Martin Hope
    Michael A fmt 格式 %H:%M:%S 不带小数 2023-11-11 01:13:05 +0800 CST
  • Martin Hope
    God I Hate Python C++20 的 std::views::filter 未正确过滤视图 2023-08-27 18:40:35 +0800 CST
  • Martin Hope
    LiDa Cute 为什么 'char -> int' 是提升,而 'char -> Short' 是转换(但不是提升)? 2023-08-24 20:46:59 +0800 CST
  • Martin Hope
    jabaa 为什么库中不调用全局变量的构造函数? 2023-08-18 07:15:20 +0800 CST
  • Martin Hope
    Panagiotis Syskakis std::common_reference_with 在元组上的行为不一致。哪个是对的? 2023-08-17 21:24:06 +0800 CST
  • Martin Hope
    Alex Guteniev 为什么编译器在这里错过矢量化? 2023-08-17 18:58:07 +0800 CST
  • Martin Hope
    wimalopaan C++17 中 std::byte 只能按位运算? 2023-08-17 17:13:58 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve