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

如何在Jetpack Compose中获取onTouchEvent?

金瑞
2023-03-14

在正常视图中,我们可以有onTouchEvent

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event?.action) {
            MotionEvent.ACTION_DOWN -> {}
            MotionEvent.ACTION_MOVE -> {}
            MotionEvent.ACTION_UP -> {}
            else -> return false
        }
        invalidate()
        return true
    }

在Jetpack Compose中,我只能找到修饰符中的tabGestureFilter,它只从ACTION_UP中获取操作。

Modifier
    .tapGestureFilter { Log.d("Track", "Tap ${it.x} | ${it.y}") }
    .doubleTapGestureFilter { Log.d("Track", "DoubleTap ${it.x} | ${it.y}") }

Jetpack Compose是否有等效的onTouchEvent

共有3个答案

苏翰学
2023-03-14

也许有点晚了,但由于compose一直在更新,我今天就是这样做的:

Modifier
    .pointerInput(Unit) {
        detectTapGestures {...}
     }
    .pointerInput(Unit) {
        detectDragGestures { change, dragAmount ->  ...}
    })

我们还有<code>探测权限</code>和<code>探测权限

ps: 1.0.0-贝塔03

宋景福
2023-03-14

如果您没有使用与现有视图代码互操作的touch api,则<code>pointerInteropFilter</code>未被描述为首选使用方式。

一个特殊的PointerInputModifier,提供对最初分派到Compose的底层MotionEvents的访问。首选pointerInput并仅将其用于与使用MotionEvents的现有代码的互操作。虽然此修改器的主要目的是允许任意代码访问分派到Compose的原始MotionEvent,但为了完整性,提供了类似项以允许任意代码与系统交互,就好像它是Android视图一样。

您可以使用指针输入 awaitTouchDown 进行MotionEvent.ACTION_DOWN,并使用 awaitPointerEvent 进行MotionEvent.ACTION_MOVEMotionEvent.ACTION_UP

val pointerModifier = Modifier
    .pointerInput(Unit) {
        forEachGesture {

            awaitPointerEventScope {
                
                awaitFirstDown()
               // ACTION_DOWN here
               
                do {
                    
                    //This PointerEvent contains details including
                    // event, id, position and more
                    val event: PointerEvent = awaitPointerEvent()
                    // ACTION_MOVE loop

                    // Consuming event prevents other gestures or scroll to intercept
                    event.changes.forEach { pointerInputChange: PointerInputChange ->
                        pointerInputChange.consumePositionChange()
                    }
                } while (event.changes.any { it.pressed })

                // ACTION_UP is here
            }
        }
}

关于手势的一些关键说明

  1. 指针输入传播是指当您有多个指针从底部传输到顶部时
  2. 如果子级和父级正在侦听输入更改,它将从内部子级传播到外部子级,然后传播到父级。与从家长到孩子的触摸事件不同
  3. 如果您不使用事件,其他事件(如滚动或拖动)可能会干扰或使用事件,大多数事件在传播到它们之前都会检查是否已使用

实例的detectDragGestures源代码

val down = awaitFirstDown(requireUnconsumed = false)
    var drag: PointerInputChange?
    var overSlop = Offset.Zero
    do {
        drag = awaitPointerSlopOrCancellation(
            down.id,
            down.type
        ) { change, over ->
            change.consumePositionChange()
            overSlop = over
        }
    } while (drag != null && !drag.positionChangeConsumed())

因此,当您需要阻止其他事件拦截时

在< code>awaitFirstDown之后调用< code > pointerinputchange . consume down(),在< code>awaitPointerEvent之后调用pointerinputchange . consumepositionchange()

并且waitFirstDown()在默认情况下为true的所需未消耗参数。如果您将其设置为false,即使指针输入在您的手势之前消耗掉,您仍然可以得到它。这也是像拖动这样的事件如何使用它来首先向下的。

您看到的每个可用事件< code > detectdraggestions 、< code > detecttapgestions 甚至< code>awaitFirstDown都使用< code>awaitPointerEvent来实现,因此使用< code>awaitFirstDown、< code>awaitPointerEvent并使用更改,您可以配置自己的手势。

例如,这是我从原始 detectTransformGestures 自定义的函数,只能在关闭特定数量的指针时调用

suspend fun PointerInputScope.detectMultiplePointerTransformGestures(
    panZoomLock: Boolean = false,
    numberOfPointersRequired: Int = 2,
    onGesture: (centroid: Offset, pan: Offset, zoom: Float, rotation: Float) -> Unit,

    ) {
    forEachGesture {
        awaitPointerEventScope {
            var rotation = 0f
            var zoom = 1f
            var pan = Offset.Zero
            var pastTouchSlop = false
            val touchSlop = viewConfiguration.touchSlop
            var lockedToPanZoom = false

            awaitFirstDown(requireUnconsumed = false)

            do {
                val event = awaitPointerEvent()

                val downPointerCount = event.changes.size

                // If any position change is consumed from another pointer or pointer
                // count that is pressed is not equal to pointerCount cancel this gesture
                val canceled = event.changes.any { it.positionChangeConsumed() } || (
                        downPointerCount != numberOfPointersRequired)

                if (!canceled) {
                    val zoomChange = event.calculateZoom()
                    val rotationChange = event.calculateRotation()
                    val panChange = event.calculatePan()

                    if (!pastTouchSlop) {
                        zoom *= zoomChange
                        rotation += rotationChange
                        pan += panChange

                        val centroidSize = event.calculateCentroidSize(useCurrent = false)
                        val zoomMotion = abs(1 - zoom) * centroidSize
                        val rotationMotion =
                            abs(rotation * PI.toFloat() * centroidSize / 180f)
                        val panMotion = pan.getDistance()

                        if (zoomMotion > touchSlop ||
                            rotationMotion > touchSlop ||
                            panMotion > touchSlop
                        ) {
                            pastTouchSlop = true
                            lockedToPanZoom = panZoomLock && rotationMotion < touchSlop
                        }
                    }

                    if (pastTouchSlop) {
                        val centroid = event.calculateCentroid(useCurrent = false)
                        val effectiveRotation = if (lockedToPanZoom) 0f else rotationChange
                        if (effectiveRotation != 0f ||
                            zoomChange != 1f ||
                            panChange != Offset.Zero
                        ) {
                            onGesture(centroid, panChange, zoomChange, effectiveRotation)
                        }
                        event.changes.forEach {
                            if (it.positionChanged()) {
                                it.consumeAllChanges()
                            }
                        }
                    }
                }
            } while (!canceled && event.changes.any { it.pressed })
        }
    }
}

从 1.2.0-beta01 开始,部分使用,如 PointerInputChange.consemePositionChange()PointerInputChange.consumeDownChange() 和一个用于消耗所有更改的 PointerInputChange.consumeAllChanges() 已被弃用

PointerInputChange.consume()

是唯一用于防止其他手势/事件的。

另外,我这里有一个教程,详细介绍了手势

邓兴为
2023-03-14

我们有一个单独的包,非常有用。有两个主要的扩展函数适合您:

    < li> 指针输入 -文档 < li> 指针过滤器 -文档

如果您想处理和处理事件,我建议您使用pointerInteropFilter,它类似于View.onTouchEvent。它与修饰符一起使用:

Column(modifier = Modifier.pointerInteropFilter {
    when (it.action) {
        MotionEvent.ACTION_DOWN -> {}
        MotionEvent.ACTION_MOVE -> {}
        MotionEvent.ACTION_UP -> {}
        else ->  false
    }
     true
})

这将是将调整后的代码组合到您指定的View.onTouchEvent示例。

P、 别忘了@ExperimentalPointerInput注释。

 类似资料:
  • 问题内容: 我建立了一个HTTP服务器。我正在使用下面的代码来获取请求URL,但未获取完整URL。 我只得到和。 我想获得完整的客户端请求的URL作为或。 谢谢。 问题答案: 从net / http包的文档中: 您的代码的修改后的版本: 输出示例:

  • 问题内容: 不使用任何外部库,将网站的HTML内容提取为String的最简单方法是什么? 问题答案: 我目前正在使用此: 但不确定是否有更好的方法。

  • 问题内容: 我对如何使用axios上传进度事件感到有些困惑。实际上,我正在将大量文件存储到AWS s3中。为此,如何获得上传进度?我需要这个功能 目前,我的发帖请求是这样的: 问题答案: Axios存储库中有一个明确的示例,说明了如何执行此操作:https : //github.com/mzabriskie/axios/blob/master/examples/upload/index.html

  • 我知道,要获取地图,我可以使用fetchMap,但它不支持LinkedHashMap,我想保持由orderBy创建的插入顺序。 我试过了 但是字段1和字段2部分给了我以下错误。 无法解析“T”中的方法“field1” 希望有人能帮我~ 谢谢 解决方案: 基于@Lukas的回答,我尝试了几次,发现必须做到以下几点: > 在方法中,所有字段都必须有一个类型,除了那些计算的,其中添加类型会抛出在字段列表

  • 我从服务器得到这样的响应: 但打印fetch API捕获中的err只会返回错误对象。 它打印了这个(来自谷歌浏览器): 我想得到正确的错误处理状态代码,就像服务器没有响应一样。 在这种情况下,有没有办法获取响应数据?否则,我可以尝试什么替代方案? 任何建议都将不胜感激!

  • 问题内容: 我使用Crawljax抓取了动态网页。我可以获取当前的ID,状态和DOM。但我无法获得网站内容。任何人都可以帮助我吗? 如何获取动态/ Java脚本网页内容。 问题答案: 我们可以获取网站源代码cc.getBrowser()。getStrippedDom()); 或cc.getCurrentState()。getDocument();或 此编码是返回源代码(css / java脚本文件