Отслеживание видимости в Compose

Отслеживание того, когда элемент пользовательского интерфейса виден на экране, полезно для различных вариантов использования, таких как ведение журнала аналитики, управление состоянием пользовательского интерфейса и оптимизация ресурсов путем автоматического воспроизведения или приостановки видеоконтента. В Compose есть несколько модификаторов для отслеживания видимости элементов интерфейса, например:

  • onVisibilityChanged – этот модификатор уведомляет вас, когда видимость composable-функции меняется. Он идеально подходит для запуска действия или побочного эффекта каждый раз, когда composable-функция становится видимой.
  • onLayoutRectChanged – этот модификатор предоставляет информацию о границах composable-функции относительно корня, окна и экрана. Он обеспечивает низкоуровневый контроль и является базовым API для onVisibilityChanged. Этот модификатор похож на onGloballyPositioned, но обеспечивает более высокую производительность и гибкость.

Вы можете использовать эти API с любой composable-функцией в цепочке модификаторов.

Отслеживайте изменения видимости с помощью onVisibilityChanged

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

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

Text(
    text = "Some text",
    modifier = Modifier
        .onVisibilityChanged { visible ->
            if (visible) {
                // Do something if visible.
            } else {
                // Do something if not visible.
            }
        }
        .padding(vertical = 8.dp)
)

Модификатор onVisibilityChanged предоставляет логическое значение, которое отражает текущее состояние видимости composable-функции. Кроме того, в нем есть параметры minFractionVisible и minDurationMs, которые позволяют точнее контролировать, когда должен срабатывать обратный вызов видимости.

Как и в случае с другими модификаторами, при использовании модификатора onVisibilityChanged важна последовательность. В примере выше показана composable-функция, которая отрисовывает текст с отступом. Чтобы модификатор влиял на всю composable-функцию вместе с отступом, добавьте модификатор onVisibilityChanged перед модификатором padding.

Как задать ограничение по времени для composable-функции перед активацией обратного вызова видимости

В некоторых случаях вам может потребоваться запускать действие только после того, как элемент был виден пользователю в течение определенного времени. Например, вы можете настроить автовоспроизведение видео, если оно было видно пользователю в течение определенного времени.

Чтобы запустить действие после того, как элемент будет виден в течение определенного периода, используйте параметр minDurationMs в модификаторе onVisibilityChanged. Этот параметр указывает минимальное время, в течение которого composable-функция должна быть непрерывно видна, чтобы сработал обратный вызов. Если composable-функция перестает быть видимой до истечения этого времени, таймер сбрасывается. Значение по умолчанию – 0 миллисекунд.

Следующий фрагмент кода меняет цвет фона на фиолетовый после того, как composable-функция была видна пользователю в течение трех секунд:

var background by remember { mutableStateOf(PalePink) }
Card(
    modifier = modifier
        // ...
        .onVisibilityChanged(minDurationMs = 3000) {
            if (it) {
                background = MutedPlum
            }
        }
) {

    Box(
        modifier = Modifier
            // ...
            .background(background),
        contentAlignment = Alignment.Center,
    ) {
        // ...
    }
}

Рисунок 1. Фон меняется с розового на сливовый после того, как composable-функция непрерывно находится на экране в течение трех секунд.

Укажите минимальную видимую долю

Задавать минимальную видимую долю для обратного вызова видимости composable-функции полезно при работе с прокручиваемым контентом (например, LazyColumn), чтобы оптимизировать получение данных для элементов, которые превышают размер экрана.

В таких случаях используйте параметр minFractionVisible в модификаторе onVisibilityChanged, чтобы определить долю, которая должна быть на экране, чтобы composable-функция была помечена как видимая. Поддерживаются значения с плавающей запятой в диапазоне от 0.0f до 1.0f. По умолчанию используется значение 1.0f. 1.0f означает, что composable-функция должна быть полностью видна на экране или заполнять всю область просмотра, если она больше области просмотра.

Обратный вызов получает true, когда видимая часть больше minFractionVisible, а также когда composable-функция полностью видна или заполняет область просмотра. В противном случае оно получает false.

LazyColumn(
    modifier = modifier.fillMaxSize()
) {
    item {
        Box(
            modifier = Modifier
                // ...
                // Here `visible` is `true` when more than 20% of the composable is visible.
                .onVisibilityChanged(
                    minFractionVisible = 0.2f,
                ) { visible ->
                    if (visible) {
                        // Call specific logic here.
                        // viewModel.fetchDataFromNetwork()
                    }
                }
                .padding(vertical = 16.dp)
        ) {
            Text(
                text = "Sample Text",
                modifier = Modifier.padding(horizontal = 16.dp)
            )
        }
    }
}

Рисунок 2. Без указания minFractionVisible. Рисунок 3. Для minFractionVisible задано значение 0.2f.

В примере, который мы использовали ранее, роботы Androidify предварительно загружаются из сети до того, как composable-функция станет полностью видимой. На рисунке 2 третий робот не загружается, так как composable-функция видна не полностью. На рисунке 3 задано значение minFractionVisible, и третий робот загружается до того, как он полностью виден на экране.

Как скрыть компонент, сохранив его место

Чтобы скрыть composable-функцию, не меняя макет вокруг нее, используйте модификатор visible. Если передать false, composable-функция будет измерена, но не размещена и не отрисована. В отличие от значения 0f для свойства alpha, visible(false) также удаляет элемент из дерева специальных возможностей и отключает сенсорное взаимодействие, сохраняя при этом пространство макета.

В примере ниже показан значок галочки, который сохраняет свое место, когда он скрыт, поэтому текст рядом с ним не перемещается:

Row(modifier, verticalAlignment = Alignment.CenterVertically) {
    Icon(
        painter = painterResource(R.drawable.check),
        contentDescription = "Saved",
        modifier = Modifier
            // The icon keeps its space while it's hidden, so the text doesn't move.
            .visible(isSaved)
    )
    Text("Draft", Modifier.padding(start = 8.dp))
}

Используйте visible, когда макет вокруг composable-функции не должен перемещаться при ее появлении и исчезновении. Чтобы удалить composable-функцию и ее пространство, не вызывайте ее или используйте AnimatedVisibility для анимации появления и исчезновения.