![](/img/trans.png)
[英]How to detect button release after button press in Android Jetpack Compose?
[英]Button Long Press Listener in Android jetpack compose
我有一個帶有Button
的 Android Composable 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")
},
),
)
處理這個問題的最好方法是滾動你自己的Button
。 Material Button
基本上只是一個Surface
和一個Row
。 添加自己的Modifier.clickable
不起作用的原因是因為已經設置了一個。
因此,如果您想添加onLongPress
等,您可以復制/粘貼默認實現並將這些 lambdas 傳入。
@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..
}
)
根據 文檔
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
)
}
}
}}
現在它按應有的方式工作,如有必要,設置雙擊是可選的
這是我的解決方案
@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
)
}
}
}
}
只需覆蓋默認按鈕並在需要捕獲點擊或長按事件時使用它
5 個月后,由於 API 更改,接受的答案不起作用。 Button
上的detectTapGestures()
也對我不起作用(我猜.clickable()
竊取了事件?)。
Surface 現在有兩個公共構造函數。 第一個不可點擊並明確覆蓋.pointerInput(Unit)
為空
Surface(
...
clickAndSemanticsModifier = Modifier
.semantics(mergeDescendants = false) {}
.pointerInput(Unit) { detectTapGestures { } }
)
第二個(由Button
使用)是可點擊的,並明確設置Modifier.clickable()
。 如果帶有detectTapGestures()
的Button
對您不起作用,那么這個也不起作用。
還有第三個私有構造函數不會覆蓋您的點擊事件。 所以我最終只是偷了它並將它放在自定義 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
,所以這個答案在未來的版本中可能會過時。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.