如果第一个项目被移动,如何防止 LazyColumn 自动滚动

And*_*lov 10 android android-scroll android-jetpack-compose android-jetpack-compose-lazy-column

我使用 Jetpack Compose UI 构建一个简单的 TODO 应用程序。这个想法是有一个可以选中或取消选中的任务列表,并且选中的任务应该位于列表的末尾。

一切工作正常,除了当我检查屏幕上的第一个可见项目时,它会随着滚动位置向下移动。

我相信这与 有关LazyListState,有这样的功能:

/**
 * When the user provided custom keys for the items we can try to detect when there were
 * items added or removed before our current first visible item and keep this item
 * as the first visible one even given that its index has been changed.
 */
internal fun updateScrollPositionIfTheFirstItemWasMoved(itemsProvider: LazyListItemsProvider) {
    scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemsProvider)
}
Run Code Online (Sandbox Code Playgroud)

所以我想禁用这种行为,但我没有找到方法。


下面是重现问题的简单代码和截屏视频。当我尝试检查“项目 0”、“项目 1”和“项目 4”时,此截屏视频中出现问题,但在检查“项目 7”和“项目 8”时,它按我预期的方式工作。

如果您选中或取消选中当前第一个可见项目的任何项目,不仅该项目位于整个列表中的第一个项目,其行为也相同。

class MainActivity : ComponentActivity() {

    @OptIn(ExperimentalFoundationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) {

                    val checkItems by remember { mutableStateOf(generate(20)) }

                    LazyColumn() {
                        items(
                            items = checkItems.sortedBy { it.checked.value },
                            key = { item -> item.id }
                        ) { entry ->

                            Row(
                                modifier = Modifier.animateItemPlacement(),
                                verticalAlignment = Alignment.CenterVertically,
                            ) {
                                Checkbox(
                                    checked = entry.checked.value,
                                    onCheckedChange = {
                                        checkItems.find { it == entry }
                                            ?.apply { this.checked.value = !this.checked.value }
                                    }
                                )
                                Text(text = entry.text)
                            }
                        }
                    }
                }
            }
        }
    }
}

data class CheckItem(val id: String, var checked: MutableState<Boolean> = mutableStateOf(false), var text: String = "")

fun generate(count: Int): List<CheckItem> =
    (0..count).map { CheckItem(it.toString(), mutableStateOf(it % 2 == 0), "Item $it") }
Run Code Online (Sandbox Code Playgroud)

z.g*_*g.y 6

由于animateItemPlacement惰性项目需要唯一的 ID/密钥才能获得动画,因此可能会牺牲第一个项目,因此key使用其index位置(无动画)设置其将防止出现此问题

   itemsIndexed(
        items = checkItems.sortedBy { it.checked.value },
        key = { index, item -> if (index == 0) index else item.id }
   ) { index, entry ->
        ...
   }
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述