Автоматически прокручивать ленивый столбец вверх, чтобы отображать полную высоту карты при раскрытии

Этот код не работает должным образом. Я хочу, чтобы ленивый столбец автоматически прокручивался вверх, чтобы отображать всю высоту карты при расширении.

val listState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()

LazyColumn(
    state = listState
) {
    itemsIndexed(items) { index, item ->
        ExpandableCard(
            item = item,
            isExpanded = expandedCardsStates[item.id] ?: false,
            onCardClicked = {
                viewModel.expandedState(item.id)
                coroutineScope.launch {
                    listState.animateScrollToItem(index = index)
                }
            }
        )
    }
}

Вот как это выглядит с этим кодом, когда я раскрываю последнюю карточку:

Вот ожидаемое поведение:

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
83
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Использование animateScrollToItem не даст вам удовлетворительных результатов. Он попытается прокрутить LazyColumn так, чтобы заданный item оказался вверху LazyColumn. Если item будет последним на экране, анимация будет заикаться. Другая проблема заключается в том, что прокрутка может уже выполняться во время анимации, в результате чего элемент будет виден не полностью.

Вероятно, вам нужно прокручивать LazyColumn только в том случае, если развернутый элемент не полностью виден на экране. Чтобы обнаружить такие случаи, добавьте модификатор onGoballyPositioned к AnimatedVisibility:

@Composable
fun ExpandableCard(/** ... **/ ) {

    var isFullyVisible by remember {
        mutableStateOf(true)  // stores whether the expanded content is fully visible on screen
    }
    var contentHeightPx by remember {
        mutableStateOf(0f)  // stores the height of the expanded content
    }

    Card(
        //...
    ) {
        Column(
            modifier = Modifier.padding(16.dp)
        ) {
            // ...

            AnimatedVisibility(
                modifier = Modifier.onGloballyPositioned { coordinates ->
                    val (width, height) = coordinates.size
                    val (x1, y1) = coordinates.positionInRoot()
                    val x2 = x1 + width
                    val y2 = y1 + height
                    val screenWidth: Float = configuration.screenWidthDp.toPx
                    val screenHeight: Float = configuration.screenHeightDp.toPx
                    isFullyVisible = (x1 >= 0 && y1 >= 0) && (x2 <= screenWidth && y2 <= screenHeight)
                    contentHeightPx = coordinates.size.height.toFloat()
                },
                visible = isExpanded,
                enter = expandVertically() + fadeIn(),
                exit = shrinkVertically() + fadeOut()
            ) {
                //...
            }
        }
    }
}

Вам необходимо добавить следующую функцию расширения:

val Number.toPx
    get() = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        this.toFloat(),
        Resources.getSystem().displayMetrics
    )

Далее нам нужно убедиться, что прокрутка выполняется только после завершения анимации AnimatedVisibility. Нам нужно добавить отдельную функцию обратного вызова в ExpandableCard Composable. Кроме того, мы меняем тип возвращаемого значения функции onCardClicked с Job на Unit:

@Composable
fun ExpandableCard(/** ... **/ onCardClicked: () -> Unit, onCardExpanded: (Float) -> Job) {
    //...
}

Затем мы вызываем onCardExpanded после завершения анимации расширения:

AnimatedVisibility(
    //...
) {

    val animatedVisibilityScope = this

    LaunchedEffect(animatedVisibilityScope.transition.isRunning) {
        if (!animatedVisibilityScope.transition.isRunning &&
            animatedVisibilityScope.transition.currentState == EnterExitState.Visible
        ) {
            if (!isFullyVisible) {
                // If the content is not fully visible, scroll LazyColumn
                onCardExpanded(contentHeightPx)
            }
        }
    }

    // expanded content
}

Наконец, примените прокрутку в LazyColumn:

LazyColumn(
    modifier = Modifier
        .fillMaxHeight()
        .padding(start = 20.dp, end = 20.dp, top = 20.dp),
    state = listState
) {
    itemsIndexed(items) { index, item ->
        ExpandableCard(
            engagementPointItem = item,
            isExpanded = expandedCardsStates[item.id] ?: false,
            onCardClicked = {
                viewModel.toggleExpandedState(item.id)
            },
            onCardExpanded = { scrollAmount ->
                coroutineScope.launch {
                    listState.animateScrollBy(scrollAmount + 50)
                }
            }
        )
    }
}

Выход:

спасибо, но карточки над экраном при открытии раскрываются неправильно. Обратите внимание, что содержимое карточки длинное (приблизительно 10 строк)

Ibtihaj 24.06.2024 13:44

Что вы подразумеваете под «картой над экраном»? Можете ли вы поделиться видео проблемы? Кроме того, длина содержимого карты не должна иметь значения, поскольку требуемая высота рассчитывается динамически. Он будет прокручиваться настолько, насколько высока высота содержимого.

BenjyTec 24.06.2024 14:31

в отправленном вами видео при открытии первой отображенной карты (например, карты номер 31) она открывается вверх, а не вниз. в некоторых случаях, подобных этому, это делается неправильно

Ibtihaj 24.06.2024 14:36

Можете ли вы поделиться своим кодом на GitHub, пожалуйста?

Ibtihaj 24.06.2024 16:24

Я только что увидел, что в моем ответе отсутствует одна функция расширения, я отредактировал свое сообщение. Также вот ссылка на фрагмент: gitlab.com/-/snippets/3721931 Для простоты я использовал Column вместо LazyColumn. Таким образом, мне не нужно использовать ViewModel для хранения развернутого/свернутого состояния.

BenjyTec 24.06.2024 16:39

это работает! большое спасибо

Ibtihaj 24.06.2024 18:13

Другие вопросы по теме