Site icon AppTractor

Создание красивой кнопки с прогрессом в Jetpack Compose

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

Что мы будем создавать?

Мы собираемся создать многоразовый компонент ProgressButton. Эта кнопка будет иметь анимацию прогресса, которая заполняет кнопку слева направо. Ключевой особенностью этого компонента является его способность обрезать анимацию прогресса по форме кнопки, обеспечивая гладкий и плавный вид, независимо от того, используете ли вы закругленные углы или круглую форму.

Вот небольшой взгляд на то, как будет выглядеть наш конечный компонент в действии:

Вдохновение

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

Сценарии использования

Кнопка прогресса — это универсальный компонент, который может улучшить пользовательский опыт в различных сценариях:

Пошаговое руководство

Разберем создание нашей кнопки прогресса на понятные шаги/

1. Настройка Composable функции

@Composable
fun SpeseProgressButton(
    text: String,
    shape: Shape = CircleShape,
    progressDurationMillis: Int = 5000,
    onClick: () -> Unit,
    onProgressComplete: () -> Unit = {}
) {
    // ... implementation to follow
}

2. Инициализация анимации

Внутри нашего композитного элемента нам нужно настроить Animatable, который будет контролировать анимацию прогресса. Мы инициализируем его со значением 0f (означающим 0% прогресса).

val animatedWithFraction = remember { Animatable(0f) }

Далее мы будем использовать LaunchedEffect для запуска анимации при первом составлении ProgressButton. Анимация будет выполняться от 0f до 1f в течение указанного в progressDurationMillisвремени. По завершении анимации мы вызовем колбек onProgressComplete.

LaunchedEffect(Unit) {
    animatedWithFraction.animateTo(
        targetValue = 1f, // Animate to 100%
        animationSpec = tween(durationMillis = progressDurationMillis, easing = LinearEasing)
    )
    onProgressComplete()
}

3. Кастомное рисование с помощью drawBehind

Здесь и происходит волшебство. Мы будем использовать Modifier.drawBehind для рисования нашего индикатора прогресса. Внутри блока drawBehind у нас есть доступ к DrawScope, который предоставляет контекст рисования.

Сначала мы получаем контур формы кнопки. Затем этот контур преобразуется в Path.

val defaultButtonClipShape = shape
val outline = defaultButtonClipShape.createOutline(
    this.size,
    layoutDirection,
    density
)
val buttonShapePath = Path().apply { addOutline(outline) }

4. Обрезка анимации прогресса

Чтобы анимация прогресса соответствовала форме кнопки (включая закругленные углы), мы используем clipPath. Эта функция принимает Path и обрезает операции рисования в пределах своей лямбды до этого пути.

Внутри блока clipPath мы рисуем прямоугольник. Ширина этого прямоугольника определяется нашим показателем анимации, в результате чего он растет слева направо.

clipPath(buttonShapePath) {
    drawRect(
        color = color,
        size = Size(
            width = this.size.width * animatedWithFraction.value,
            height = this.size.height
        ),
    )
}

5. Сборка кнопки

Наконец, мы собираем все вместе в композиции Button. Мы применяем наш кастомный модификатор drawBehind и устанавливаем цвета и форму кнопки. Цвет контейнера имеет уменьшенный альфа-коэффициент, чтобы сделать видимой анимацию прогресса под ним.

Button(
    onClick = onClick,
    modifier = Modifier
        .height(IntrinsicSize.Max)
        .drawBehind { /* ... our drawing code ... */ },
    colors = ButtonDefaults.buttonColors(
        containerColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f),
        contentColor = MaterialTheme.colorScheme.onPrimary
    ),
    shape = shape
) {
    Text(
        text = text,
        style = MaterialTheme.typography.bodyMedium
    )
}

Пример использования

Интеграция ProgressButton в ваше приложение не представляет сложности. Вот как вы можете использовать его в своем пользовательском интерфейсе:

@Preview
@Composable
private fun ProgressBtnPreview() {
    SpeseTheme {
        Surface(
            modifier = Modifier.fillMaxSize(),
        ) {
            Column(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                SpeseProgressButton(
                    text = "Let's Get Started",
                    onClick = {
                        println("On Click is triggered")
                    },
                    shape = RoundedCornerShape(corner = CornerSize(12.dp)),
                    onProgressComplete = {
                        println("Progress is completed")
                    }
                )
            }
        }
    }
}

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

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

Заключение

Объединив мощь API анимации Jetpack Compose и кастомных модификаторов рисования, мы создали динамичную и привлекательную кнопку прогресса. Этот компонент не только улучшает визуальную привлекательность вашего приложения, но и предоставляет ценную обратную связь вашим пользователям. Не стесняйтесь использовать этот код, экспериментировать с различными формами и анимациями и интегрировать его в свои собственные проекты, чтобы создать более интерактивный и приятный пользовательский опыт.

Источник

Exit mobile version