Android jetpack compose中的按钮长按监听器

Posted

技术标签:

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

我有一个带有 Buttonandroid 可组合 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 但我不确定。 【参考方案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()
        
    

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

【讨论】:

现在不工作

以上是关于Android jetpack compose中的按钮长按监听器的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack Compose 中的文本渐变

Android日志:Jetpack Compose中的布局

android jetpack compose中的“remember”和“mutableState”有啥区别?

Android jetpack compose中的按钮长按监听器

Jetpack Compose 和 Compose Navigation 如何处理 Android 活动?

Koin在KMM与Android Jetpack Compose中的应用