【问题标题】:Button Long Press Listener in Android jetpack composeAndroid jetpack compose中的按钮长按监听器
【发布时间】:2021-04-26 08:36:22
【问题描述】:

我有一个带有 Button 的 Android 可组合 UI。

如何跟踪按钮长按事件?我让它适用于Text 长按,但对于Button,它不起作用。如果我对按钮应用修饰符,则与下面的方法相同,它不起作用。

Text(
    text = view.text,
    fontSize = view.textFontSize.toInt().sp,
    fontWeight = FontWeight(view.textFontWeight.toInt()),
    color = Color(android.graphics.Color.parseColor(view.textColor)),
    modifier = Modifier.clickable(
        onClick = {
            println("Single Click")
        }, 
        onLongClick = {
            println("Long Click")
        }, 
        onDoubleClick = {
            println("Double Tap")
        },
    ),
)

【问题讨论】:

  • 我认为你可以使用 LongPressFiredEvent 但我不确定。

标签: android kotlin android-jetpack-compose


【解决方案1】:

https://developer.android.com/jetpack/compose/gestures也可以使用。

例如:

 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.foundation.gestures.detectTapGestures

 modifier = Modifier
           .weight(2f)
           .pointerInput(Unit){
               detectTapGestures(
                     onLongPress = {
                             // perform some action here..
                     }
               )
            }
                        

【讨论】:

    【解决方案2】:

    你可以像下面这样使用combinedClickable

    Modifier
        .combinedClickable(
            onClick = { },
            onLongClick = { },
        )
    

    警告:使用 Compose 1.0.1,此方法被标记为 @ExperimentalFoundationApi,因此此答案在未来的版本中可能会过时。

    【讨论】:

      【解决方案3】:

      处理此问题的最佳方法是推出您自己的Button。材料Button 基本上只是一个Surface 和一个Row。添加您自己的Modifier.clickable 不起作用的原因是因为已经设置了一个。

      因此,如果您想添加 onLongPress 等,您可以复制/粘贴默认实现并传入这些 lambda。

      @Composable
      @OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
      fun Button(
          onClick: () -> Unit,
          modifier: Modifier = Modifier,
          onLongClick: (() -> Unit)? = null,
          onDoubleClick: (() -> Unit)? = null,
          enabled: Boolean = true,
          interactionState: InteractionState = remember { InteractionState() },
          elevation: ButtonElevation? = ButtonDefaults.elevation(),
          shape: Shape = MaterialTheme.shapes.small,
          border: BorderStroke? = null,
          colors: ButtonColors = ButtonDefaults.buttonColors(),
          contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
          content: @Composable RowScope.() -> Unit
      ) {
          val contentColor by colors.contentColor(enabled)
          Surface(
              shape = shape,
              color = colors.backgroundColor(enabled).value,
              contentColor = contentColor.copy(alpha = 1f),
              border = border,
              elevation = elevation?.elevation(enabled, interactionState)?.value ?: 0.dp,
              modifier = modifier.combinedClickable(
                  onClick = onClick,
                  onDoubleClick = onDoubleClick,
                  onLongClick = onLongClick,
                  enabled = enabled,
                  role = Role.Button,
                  interactionState = interactionState,
                  indication = null
              )
          ) {
              Providers(LocalContentAlpha provides contentColor.alpha) {
                  ProvideTextStyle(
                      value = MaterialTheme.typography.button
                  ) {
                      Row(
                          Modifier
                              .defaultMinSizeConstraints(
                                  minWidth = ButtonDefaults.MinWidth,
                                  minHeight = ButtonDefaults.MinHeight
                              )
                              .indication(interactionState, rememberRipple())
                              .padding(contentPadding),
                          horizontalArrangement = Arrangement.Center,
                          verticalAlignment = Alignment.CenterVertically,
                          content = content
                      )
                  }
              }
          }
      }
      

      用法:

      Button(
          onClick = {},
          onLongClick = {},
          onDoubleClick = {}
      ) {
          Text(text = "I'm a button")
      }
      

      【讨论】:

        【解决方案4】:

        根据documentation

        Modifier.pointerInput(Unit) {
                detectTapGestures(
                    onPress = { /* Called when the gesture starts */ },
                    onDoubleTap = { /* Called on Double Tap */ },
                    onLongPress = { /* Called on Long Press */ },
                    onTap = { /* Called on Tap */ }
                )
            }
        

        【讨论】:

          【解决方案5】:

          5 个月后,由于 API 更改,已接受的答案不起作用。 Button 上的 detectTapGestures() 对我也不起作用(我猜是 .clickable() 偷了事件?)。

          Surface 现在有两个公共构造函数。第一个不可点击并明确覆盖.pointerInput(Unit) 为空

          Surface(
          ...
              clickAndSemanticsModifier = Modifier
                  .semantics(mergeDescendants = false) {}
                  .pointerInput(Unit) { detectTapGestures { } }
          )
          

          第二个(由Button 使用)是可点击的,并明确设置Modifier.clickable()。如果 ButtondetectTapGestures() 对你不起作用,那么这个也不起作用。

          还有第三个私有构造函数不会覆盖您的点击事件。所以我最终只是偷了它并将它放在自定义 LongPressButton 旁边。

          @OptIn(ExperimentalFoundationApi::class)
          @Composable
          fun LongPressButton(
              modifier: Modifier = Modifier,
              onClick: () -> Unit = {},
              onLongPress: () -> Unit = {},
              onDoubleClick: () -> Unit = {},
              enabled: Boolean = true,
              interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
              elevation: ButtonElevation? = ButtonDefaults.elevation(),
              shape: Shape = MaterialTheme.shapes.small,
              border: BorderStroke? = null,
              colors: ButtonColors = ButtonDefaults.buttonColors(),
              contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
              content: @Composable RowScope.() -> Unit
          ) {
              val contentColor by colors.contentColor(enabled)
              Surface(
                  modifier = modifier,
                  shape = shape,
                  color = colors.backgroundColor(enabled).value,
                  contentColor = contentColor.copy(alpha = 1f),
                  border = border,
                  elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
                  clickAndSemanticsModifier = Modifier.combinedClickable(
                      interactionSource = interactionSource,
                      indication = rememberRipple(),
                      enabled = enabled,
                      role = Role.Button,
                      onClick = onClick,
                      onDoubleClick = onDoubleClick,
                      onLongClick = onLongPress,
                  )
              ) {
                  CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
                      ProvideTextStyle(
                          value = MaterialTheme.typography.button
                      ) {
                          Row(
                              Modifier
                                  .defaultMinSize(
                                      minWidth = ButtonDefaults.MinWidth,
                                      minHeight = ButtonDefaults.MinHeight
                                  )
                                  .padding(contentPadding),
                              horizontalArrangement = Arrangement.Center,
                              verticalAlignment = Alignment.CenterVertically,
                              content = content
                          )
                      }
                  }
              }
          }
          
          
          @Composable
          private fun Surface(
              modifier: Modifier,
              shape: Shape,
              color: Color,
              contentColor: Color,
              border: BorderStroke?,
              elevation: Dp,
              clickAndSemanticsModifier: Modifier,
              content: @Composable () -> Unit
          ) {
              val elevationOverlay = LocalElevationOverlay.current
              val absoluteElevation = LocalAbsoluteElevation.current + elevation
              val backgroundColor = if (color == MaterialTheme.colors.surface && elevationOverlay != null) {
                  elevationOverlay.apply(color, absoluteElevation)
              } else {
                  color
              }
              CompositionLocalProvider(
                  LocalContentColor provides contentColor,
                  LocalAbsoluteElevation provides absoluteElevation
              ) {
                  Box(
                      modifier
                          .shadow(elevation, shape, clip = false)
                          .then(if (border != null) Modifier.border(border, shape) else Modifier)
                          .background(
                              color = backgroundColor,
                              shape = shape
                          )
                          .clip(shape)
                          .then(clickAndSemanticsModifier),
                      propagateMinConstraints = true
                  ) {
                      content()
                  }
              }
          }
          

          如果有更好的方法,请分享。因为目前的解决方案很丑。

          【讨论】:

          • 现在不工作
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-25
          • 2021-08-13
          • 1970-01-01
          • 2013-07-01
          • 1970-01-01
          相关资源
          最近更新 更多