Android jetpack compose 中的按钮长按监听器
Button Long Press Listener in Android jetpack compose
我有一个 Android 可组合 UI 和 Button
.
如何跟踪按钮长按事件?我让它在 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")
},
),
)
处理此问题的最佳方法是自己滚动 Button
。 Material Button
基本上只是一个 Surface
和一个 Row
。添加您自己的 Modifier.clickable
不起作用的原因是已经设置了一个。
因此,如果您想添加 onLongPress
等,您可以 copy/paste 默认实现并将这些 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")
}
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..
}
)
}
5 个月后,接受的答案因 API 更改而无效。 detectTapGestures()
在 Button
上对我也不起作用(我猜 .clickable()
偷走了事件?)。
Surface 现在有两个 public 构造函数。第一个不可点击并明确覆盖 .pointerInput(Unit)
为空
Surface(
...
clickAndSemanticsModifier = Modifier
.semantics(mergeDescendants = false) {}
.pointerInput(Unit) { detectTapGestures { } }
)
第二个(由 Button
使用)可点击并明确设置 Modifier.clickable()
。如果 Button
和 detectTapGestures()
对你不起作用,这个也不会起作用。
还有第三个私有构造函数不会覆盖您的点击事件。所以我最终只是偷了它并将它放在自定义 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()
}
}
}
如果有更好的方法,请分享。因为目前的解决方案很难看。
您可以像下面这样使用combinedClickable
:
Modifier
.combinedClickable(
onClick = { },
onLongClick = { },
)
警告:使用 Compose 1.0.1
此方法被标记为 @ExperimentalFoundationApi
因此此答案可能会在未来的版本中过时。
Modifier.pointerInput(Unit) {
detectTapGestures(
onPress = { /* Called when the gesture starts */ },
onDoubleTap = { /* Called on Double Tap */ },
onLongPress = { /* Called on Long Press */ },
onTap = { /* Called on Tap */ }
)
}
我尝试了@adneal 的回答,但出于某种原因它无法接收“onLongClick”。
经过一些研究,我更新如下以使其工作:
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
@Composable
fun ButtonWithLongPress(
onClick: () -> Unit,
onDoubleClick:()->Unit = {},
onLongClick: () -> Unit,
modifier: Modifier = Modifier,
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(
onClick = { },
modifier = modifier
.combinedClickable(
interactionSource,
rememberRipple(),
true,
null,
Role.Button,
null,
onClick = { onClick() },
onLongClick = { onLongClick() },
onDoubleClick = {onDoubleClick()}),
enabled = enabled,
shape = shape,
color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
interactionSource = interactionSource,
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
Row(
Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight
)
.padding(contentPadding)
.combinedClickable(interactionSource,
null,
true,
null,
Role.Button,
null,
onClick = { onClick() },
onLongClick = { onLongClick() },
onDoubleClick = { onDoubleClick() }),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}}
现在它可以正常工作了,如果需要,设置 double-click 是可选的
这是我的解决方案
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun MyButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
onLongClick: () -> 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)
var tapped by remember { mutableStateOf(false) }
Surface(
modifier = modifier
.clip(shape)
.indication(interactionSource, LocalIndication.current)
.pointerInput(Unit) {
detectTapGestures(
onPress = { offset ->
tapped = true
val press = PressInteraction.Press(offset)
interactionSource.emit(press)
tryAwaitRelease()
interactionSource.emit(PressInteraction.Release(press))
tapped = false
},
onTap = { onClick() },
onLongPress = { onLongClick() }
)
}
,
shape = shape,
color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
) {
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
)
}
}
}
}
简单地覆盖默认按钮并在需要捕获点击或长按事件时使用它
我有一个 Android 可组合 UI 和 Button
.
如何跟踪按钮长按事件?我让它在 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")
},
),
)
处理此问题的最佳方法是自己滚动 Button
。 Material Button
基本上只是一个 Surface
和一个 Row
。添加您自己的 Modifier.clickable
不起作用的原因是已经设置了一个。
因此,如果您想添加 onLongPress
等,您可以 copy/paste 默认实现并将这些 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")
}
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..
}
)
}
5 个月后,接受的答案因 API 更改而无效。 detectTapGestures()
在 Button
上对我也不起作用(我猜 .clickable()
偷走了事件?)。
Surface 现在有两个 public 构造函数。第一个不可点击并明确覆盖 .pointerInput(Unit)
为空
Surface(
...
clickAndSemanticsModifier = Modifier
.semantics(mergeDescendants = false) {}
.pointerInput(Unit) { detectTapGestures { } }
)
第二个(由 Button
使用)可点击并明确设置 Modifier.clickable()
。如果 Button
和 detectTapGestures()
对你不起作用,这个也不会起作用。
还有第三个私有构造函数不会覆盖您的点击事件。所以我最终只是偷了它并将它放在自定义 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()
}
}
}
如果有更好的方法,请分享。因为目前的解决方案很难看。
您可以像下面这样使用combinedClickable
:
Modifier
.combinedClickable(
onClick = { },
onLongClick = { },
)
警告:使用 Compose 1.0.1
此方法被标记为 @ExperimentalFoundationApi
因此此答案可能会在未来的版本中过时。
Modifier.pointerInput(Unit) {
detectTapGestures(
onPress = { /* Called when the gesture starts */ },
onDoubleTap = { /* Called on Double Tap */ },
onLongPress = { /* Called on Long Press */ },
onTap = { /* Called on Tap */ }
)
}
我尝试了@adneal 的回答,但出于某种原因它无法接收“onLongClick”。
经过一些研究,我更新如下以使其工作:
@OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
@Composable
fun ButtonWithLongPress(
onClick: () -> Unit,
onDoubleClick:()->Unit = {},
onLongClick: () -> Unit,
modifier: Modifier = Modifier,
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(
onClick = { },
modifier = modifier
.combinedClickable(
interactionSource,
rememberRipple(),
true,
null,
Role.Button,
null,
onClick = { onClick() },
onLongClick = { onLongClick() },
onDoubleClick = {onDoubleClick()}),
enabled = enabled,
shape = shape,
color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
interactionSource = interactionSource,
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
Row(
Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight
)
.padding(contentPadding)
.combinedClickable(interactionSource,
null,
true,
null,
Role.Button,
null,
onClick = { onClick() },
onLongClick = { onLongClick() },
onDoubleClick = { onDoubleClick() }),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}}
现在它可以正常工作了,如果需要,设置 double-click 是可选的
这是我的解决方案
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun MyButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
onLongClick: () -> 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)
var tapped by remember { mutableStateOf(false) }
Surface(
modifier = modifier
.clip(shape)
.indication(interactionSource, LocalIndication.current)
.pointerInput(Unit) {
detectTapGestures(
onPress = { offset ->
tapped = true
val press = PressInteraction.Press(offset)
interactionSource.emit(press)
tryAwaitRelease()
interactionSource.emit(PressInteraction.Release(press))
tapped = false
},
onTap = { onClick() },
onLongPress = { onLongClick() }
)
}
,
shape = shape,
color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
) {
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
)
}
}
}
}
简单地覆盖默认按钮并在需要捕获点击或长按事件时使用它