Потяните, чтобы обновить

Компонент «Потяните для обновления» позволяет пользователям перетаскивать вниз начало содержимого приложения, чтобы обновить данные.

API поверхность

Используйте компонуемый объект PullToRefreshBox для реализации функции обновления при перетягивании, которая служит контейнером для прокручиваемого контента. Следующие ключевые параметры управляют поведением и внешним видом обновления:

  • isRefreshing : логическое значение, указывающее, выполняется ли действие обновления.
  • onRefresh : лямбда-функция, которая выполняется, когда пользователь инициирует обновление.
  • indicator : настраивает индикатор, который система рисует при обновлении путем перетягивания.

Простой пример

В этом фрагменте показано базовое использование PullToRefreshBox :

@Composable
fun PullToRefreshBasicSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

Ключевые моменты кода

  • PullToRefreshBox является оболочкой LazyColumn , который отображает список строк.
  • PullToRefreshBox требуются параметры isRefreshing и onRefresh .
  • Содержимое блока PullToRefreshBox представляет собой прокручиваемый контент.

Результат

В этом видео демонстрируется базовая реализация функции обновления путем потягивания из предыдущего кода:

Рисунок 1. Базовая реализация функции обновления списка элементов путем перетягивания.

Расширенный пример: настройка цвета индикатора

@Composable
fun PullToRefreshCustomStyleSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            Indicator(
                modifier = Modifier.align(Alignment.TopCenter),
                isRefreshing = isRefreshing,
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                color = MaterialTheme.colorScheme.onPrimaryContainer,
                state = state
            )
        },
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

Ключевые моменты кода

  • Цвет индикатора настраивается с помощью свойств containerColor и color в параметре indicator .
  • Функция rememberPullToRefreshState() управляет состоянием действия обновления. Это состояние используется вместе с параметром indicator .

Результат

В этом видео показана реализация функции обновления путем потягивания с цветным индикатором:

Рисунок 2. Реализация функции обновления путем потягивания с пользовательским стилем.

Расширенный пример: создание полностью настраиваемого индикатора

Вы можете создавать сложные пользовательские индикаторы, используя существующие компонуемые элементы и анимацию. В этом фрагменте показано, как создать полностью настраиваемый индикатор в вашей реализации обновления по нажатию:

@Composable
fun PullToRefreshCustomIndicatorSample(
    items: List<String>,
    isRefreshing: Boolean,
    onRefresh: () -> Unit,
    modifier: Modifier = Modifier
) {
    val state = rememberPullToRefreshState()

    PullToRefreshBox(
        isRefreshing = isRefreshing,
        onRefresh = onRefresh,
        modifier = modifier,
        state = state,
        indicator = {
            MyCustomIndicator(
                state = state,
                isRefreshing = isRefreshing,
                modifier = Modifier.align(Alignment.TopCenter)
            )
        }
    ) {
        LazyColumn(Modifier.fillMaxSize()) {
            items(items) {
                ListItem({ Text(text = it) })
            }
        }
    }
}

// ...
@Composable
fun MyCustomIndicator(
    state: PullToRefreshState,
    isRefreshing: Boolean,
    modifier: Modifier = Modifier,
) {
    Box(
        modifier = modifier.pullToRefreshIndicator(
            state = state,
            isRefreshing = isRefreshing,
            containerColor = PullToRefreshDefaults.containerColor,
            threshold = PositionalThreshold
        ),
        contentAlignment = Alignment.Center
    ) {
        Crossfade(
            targetState = isRefreshing,
            animationSpec = tween(durationMillis = CROSSFADE_DURATION_MILLIS),
            modifier = Modifier.align(Alignment.Center)
        ) { refreshing ->
            if (refreshing) {
                CircularProgressIndicator(Modifier.size(SPINNER_SIZE))
            } else {
                val distanceFraction = { state.distanceFraction.coerceIn(0f, 1f) }
                Icon(
                    imageVector = Icons.Filled.CloudDownload,
                    contentDescription = "Refresh",
                    modifier = Modifier
                        .size(18.dp)
                        .graphicsLayer {
                            val progress = distanceFraction()
                            this.alpha = progress
                            this.scaleX = progress
                            this.scaleY = progress
                        }
                )
            }
        }
    }
}

Ключевые моменты кода

  • В предыдущем фрагменте кода использовался Indicator , предоставляемый библиотекой. Этот фрагмент кода создаёт пользовательский составной индикатор с именем MyCustomIndicator . В этом составе модификатор pullToRefreshIndicator управляет позиционированием и запуском обновления.
  • Как и в предыдущем фрагменте, пример извлекает экземпляр PullToRefreshState , поэтому вы можете передать один и тот же экземпляр как PullToRefreshBox , так и pullToRefreshModifier .
  • В этом примере используются цвет контейнера и пороговое значение положения из класса PullToRefreshDefaults . Таким образом, вы можете повторно использовать поведение и стили по умолчанию из библиотеки Material, настраивая только те элементы, которые вам нужны.
  • MyCustomIndicator использует Crossfade для перехода между значком облака и индикатором CircularProgressIndicator . Значок облака увеличивается по мере перемещения пользователя и превращается в индикатор CircularProgressIndicator при начале обновления.
    • targetState использует isRefreshing для определения, какое состояние отображать (значок облака или круговой индикатор прогресса).
    • animationSpec определяет анимацию tween с указанной длительностью CROSSFADE_DURATION_MILLIS .
    • state.distanceFraction показывает, насколько далеко пользователь потянул ползунок вниз, в диапазоне от 0f (нет нажатия) до 1f (полностью потянуто).
    • Модификатор graphicsLayer изменяет масштаб и прозрачность.

Результат

В этом видео показан пользовательский индикатор из предыдущего кода:

Рисунок 3. Реализация функции обновления путем потягивания с использованием пользовательского индикатора.

Дополнительные ресурсы