In Jetpack Compose there is a Modifier
extension called selectable
.
Configure component to be selectable, usually as a part of a mutually exclusive group, where only one item can be selected at any point in time.
I'm using this for a mutually exclusive radio group inside a scrollable list. In my case a LazyColumn
. This works fine, clicking on the selectable areas lights them up and results in detected clicks. However I noticed that the area also lights up while "touching" these areas while scrolling.
I made a simple example composable if you want to see what I mean, simply scroll through the list and you will see how scrolling triggers a short selected state:
@Composable
fun Example() {
LazyColumn {
item {
repeat(100){
Column(
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.selectable(
selected = false,
onClick = { }
)
) {
Text("Example")
}
}
}
}
}
Has anyone figure out how to fix kind of behaviour? I tried looking for any related documentation at https://developer.android.com/jetpack/compose/gestures but nothing really explains how to "block" touch events while scrolling.
You can selectively enable Modifier.selectable(enabled) based on scroll state but even with derivedStateOf
i see that there is huge performance loss.
val scrollState = rememberLazyListState()
val enableSelectable = derivedStateOf {
!scrollState.isScrollInProgress
}
Modifier
.fillMaxWidth()
.height(40.dp)
.selectable(
enabled = enableSelectable.value,
selected = false,
onClick = { }
)
I created a simple but longer example than you did, and included a video showing how it behaves with this code.
I believe what you are seeing is the ACTION_DOWN
causing a ripple. It's not actually "selecting" the item because it does not change the selected state. I am not seeing the ripple when I scroll, but only when I keep my finger pressed on a specific row - the ripple disappears when my finger moves down.
I got the info about MotionEvent
s from this answer: https://stackoverflow.com/a/64594717/1703677 (Change the false
s to true
to see more info in the logs)
@Composable
fun Content() {
val selectedValue = remember { mutableStateOf("") }
LazyColumn {
item {
repeat(100) {
val label = "Item $it"
val selected = selectedValue.value == label
SingleRadioButtonWithLabel(label, selected) {
selectedValue.value = label
}
}
}
}
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun SingleRadioButtonWithLabel(
label: String,
selected: Boolean,
onClick: () -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.selectable(
selected = selected,
onClick = {
onClick()
Log.e("TestApp", "Row onClick")
}
)
.pointerInteropFilter {
when (it.action) {
MotionEvent.ACTION_DOWN -> {
Log.e("TestApp", "MotionEvent.ACTION_DOWN")
}
MotionEvent.ACTION_MOVE -> {
Log.e("TestApp", "MotionEvent.ACTION_MOVE")
}
MotionEvent.ACTION_UP -> {
Log.e("TestApp", "MotionEvent.ACTION_UP")
}
else -> false
}
false
}
) {
RadioButton(
selected = selected,
onClick = {
onClick()
Log.e("TestApp", "Radio Button onClick")
},
)
Text(
text = label,
modifier = Modifier.fillMaxWidth()
)
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.