简体   繁体   中英

Jetpack Compose: When using Modifier.selectable how do you prevent a layout composable from being selectable while scrolling?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM