Каждый Android-разработчик сталкивается с задачей обучения пользователей новым функциям или помощи в навигации по интерфейсу. Традиционные всплывающие окна или сообщения могут быть навязчивыми. Конечно же есть способ элегантно подсвечивать элементы UI и предоставлять контекстную помощь. Встречайте TAO Bubbles — легковесную библиотеку для Jetpack Compose, созданную для отображения настраиваемых «пузырей», «подсказок» или «тултипов», которые могут указывать на конкретные UI‑компоненты.TAO Bubbles прекрасно подходит для создания пошаговых руководств, демонстрации новых возможностей или предоставления контекстно‑зависимой справки прямо в вашем приложении.

P.S. Проект создавался во время изучения Jetpack Compose, так что конструктивная критика и pull requests с улучшениями приветствуется ).

Ключевые особенности TAO Bubbles для Jetpack Compose

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

  • Гибкое позиционирование: Подсказки могут указывать на любую сторону целевого composable-компонента (LEFT, RIGHT, TOP, BOTTOM).

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

  • Последовательный показ: Используйте BubbleShowController для легкого отображения серии подсказок одна за другой, проводя пользователя через несколько шагов.

  • Анимации появления/исчезновения: Плавные и настраиваемые анимации для появления и исчезновения подсказок делают взаимодействие более живым.

  • Затемнение фона (Scrim): Опциональный слой затемнения фона помогает сфокусировать внимание пользователя на подсказке.

  • Декларативный API: Библиотека разработана с учетом современных практик Jetpack Compose, предлагая простой и интуитивно понятный API.

  • Легкое использование: Никаких сложных конструкций. Добавление к существующему проекту очень простое.

Как это работает?

Пример использования (одиночный Bubble):

Определяете общие стили для всех ваших «пузырей» или для конкретной группы.

    val settings = BubblesSettings(
            scrimColor = Color(0x22002EFF), // Цвет затемнения фона
            backgroundColor = OrangeVeryLight, // Цвет фона подсказки
            bubbleBorderColor = Color.Black, // Цвет рамки
            bubbleBorderWidth = 2.dp // Толщина рамки
            ... и так далее
        )

Подготавливаете данные для каждой подсказки.

    val bubbleData = BubbleData(
        id = "Intro_Feature", // Уникальный ID для сохранения состояния (показана/скрыта)
        arrowPosition = ArrowPosition.BOTTOM, // С какой стороны целевого объекта прилегает стрелка и подсказка
        content = { onDismissClick, onStopShowRequest ->
            // Здесь размещается любой ваш Composable-контент для подсказки
            // onDismissClick - функция вызываемая по закрытию подсказки
            // onStopShowRequest - функция для остановки всей последовательности (если используется BubbleShowController)
            Text("Это важная новая кнопка!")
        }
    )

И используете эти данные в своем коде:

    @Composable
    fun SingleBubbleExample() {
        var showBubble by remember { mutableStateOf(true) }
        var targetRect by remember { mutableStateOf<Rect?>(null) } // Rect целевого компонента

        Box(modifier = Modifier.fillMaxSize()) {
            Button(
                onClick = { showBubble = !showBubble },
                modifier = Modifier
                    .align(Alignment.Center)
                    .onGloballyPositioned { coordinates ->
                        // Получаем геометрию компонента, к которому будет привязана подсказка
                        targetRect = coordinates.boundsInWindow()
                    }
            ) {
                Text("Показать подсказку")
            }

            Bubble(
                targetComponentRect = targetRect, // Передаем Rect цели
                bubbleData = bubbleData,          // Наши данные для подсказки
                settings = settings,              // Наши настройки стиля
                isVisible = showBubble,           // Управляем видимостью
                onDismissRequest = { showBubble = false } // Действие при закрытии
            )
          
        }
    }
    

Настройка показа последовательных подсказок.

Показ большого количества подсказок не сильно сложнее. Для настройки используем BubbleShowController.

Подготавливаем список (или по одному) BubbleData для каждой подсказки:

    val testBubbles = listOf(
        BubbleData(
            id = "Step_1",
            arrowPosition = ArrowPosition.BOTTOM,
            content = { onDismissClick, onStopShowRequest ->
                MyContent("Подсказка 1: Нажмите здесь", onDismissClick, onStopShowRequest)
            }
        ),
        BubbleData(
            id = "Step_2",
            arrowPosition = ArrowPosition.LEFT,
            content = { onDismissClick, onStopShowRequest ->
                MyContent("Подсказка 2: Затем проверьте это", onDismissClick, onStopShowRequest)
            }
        ),
        BubbleData(
            id = "Step_3",
            arrowPosition = ArrowPosition.RIGHT,
            content = { onDismissClick, onStopShowRequest ->
                MyContent("Подсказка 3: И наконец, сюда!", onDismissClick, onStopShowRequest)
            }
        )
    )

    // MyContent - ваш кастомный Composable для содержимого подсказки
    @Composable
    fun MyContent(text: String, onDismiss: () -> Unit, onStopShow: () -> Unit) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text(text)
            Button(onClick = onDismiss) { Text("Далее") }
            // Button(onClick = onStopShow) { Text("Завершить тур") } // Если нужно
        }
    }
    

И используем подготовленные данные в основном коде связывая каждый BubbleData с целевым объектом:

    @Composable
    fun BubbleSequenceExample() {
        // Подготовка показа
        val bubblesSettings = remember { testSettings } // Используем ранее созданные настройки
        val bubblesData = remember { testBubbles } // Используем ранее созданные данные для каждой подсказки. Но можно и по одному. 
        val bubbleShowController = rememberBubbleShowController( // Контроллер показа 
            settings = bubblesSettings,
            bubbles = bubblesData,
            onFinished = {
                // Действия после завершения всех подсказок
                Log.d("BubbleShow", "Все подсказки показаны!")
            }
        )

        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(Color(0xFFE0F7FA))
        ) {
            // Ваши UI компоненты, к которым будут привязаны подсказки
            Box(
                modifier = Modifier
                    .align(Alignment.TopStart)
                    .padding(16.dp)
                    .size(100.dp)
                    // Связываем компонент с данными подсказки через контроллер
                    .assignBubble(controller = bubbleShowController, bubbleData = bubblesData[0]),
                contentAlignment = Alignment.Center
            ) {
                Text("Элемент 1", color = Color.Black)
            }

            Box(
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(16.dp)
                    .size(100.dp)
                    .assignBubble(controller = bubbleShowController, bubbleData = bubblesData[1]),
                contentAlignment = Alignment.Center
            ) {
                Text("Элемент 2", color = Color.Black)
            }

            Box(
                modifier = Modifier
                    .align(Alignment.BottomStart)
                    .padding(16.dp)
                    .size(100.dp)
                    .assignBubble(controller = bubbleShowController, bubbleData = bubblesData[2]),
                contentAlignment = Alignment.Center
            ) {
                Text("Элемент 3", color = Color.Black)
            }

            // Кнопка для перезапуска показа
            Button(
                modifier = Modifier
                    .align(Alignment.Center)
                    .padding(top = 224.dp),
                onClick = {
                    bubbleShowController.restartShow()
                }) {
                Text("Перезапустить показ")
            }

            // Отображаем текущую подсказку из контроллера
            // Composable Bubble с контроллером автоматически управляет видимостью и настройками.
            bubbleShowController.ShowBubbles()
        }
    }
    

Модификатор .assignBubble() связывает ваши UI-компоненты с соответствующими данными подсказок в контроллере. Контроллер затем сам позаботится об определении targetComponentRect для каждой подсказки.

Нужно больше настроек?

Если вы предпочитаете иметь полный контроль и кастомизацию каждого пузыря в списке для показа, то используйте BubbleShowControllerExtended для контроля и BubbleDataExtended для настройки каждого пузыря.

Заключение

TAO Bubbles для Jetpack Compose — это прост ой, но мощный инструмент для создания интерактивных подсказок и туров по приложению. Благодаря гибкой настройке и удобному API, вы сможете значительно улучшить пользовательский опыт, помогая пользователям осваивать функционал вашего приложения легко и непринужденно.
Попробуйте TAO Bubbles в своем следующем проекте!

Скачать последний релиз и код демо-приложения

Лицензия MIT

Комментарии (0)