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

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

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

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

  • 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. Реализация функции «потянуть для обновления» с пользовательским индикатором.

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