В этой статье я поделюсь своим опытом использования Canvas в Jetpack Compose, новым набором инструментов для создания пользовательских интерфейсов, созданным Google. Android Dev Challenge #2 дал мне возможность узнать массу вещей о Canvas и о том, как использовать его для рисования и анимации фигур или текстов очень красивым образом.
Большинство примеров кода основаны на этом проекте.
Первые шаги с Canvas
Если вы знакомы с холстами в Android View, вы не потеряетесь с Canvas и в Jetpack Compose. Все имена функций одинаковы, а некоторые из них даже более явны при работе с Path API, например: relativeQuadraticBezierTo() вместо rQuadto() для кривой сегмента пути.
Если вы не знакомы с родным Android Canvas, я настоятельно рекомендую взглянуть на эту статью Ребекки Фрэнкс, чтобы погрузиться в Android Canvas.
В Jetpack Compose есть компонент Canvas, который является Composable в библиотеке компонентов пользовательского интерфейса, и который позволяет раскрыть все возможности рисования в вашем приложении. Мы собираемся нарисовать смайлик с простыми формами (круг, дуга и прямоугольники), чтобы показать его возможности.
Лямбда onDraw на холсте дает нам доступ к DrawScope. Эта область позволяет нам рисовать все, что мы хотим, на холсте. Помните, что начало координат (x = 0, y = 0) холста находится в верхнем левом углу.
Чтобы нарисовать смайлик, мы собираемся нарисовать круг с обводкой. Если мы оставим стиль пустым, он будет заполнен по умолчанию. Все методы рисования принимают Color или Brush (используются для добавления градиентов со списком цветов). Чтобы определить радиус, у нас есть доступ к размеру текущей среды рисования size, предоставленным DrawScope, он позволяет вычислить масштабируемый радиус в зависимости от размера компонента. Атрибут center принимает Offset, чтобы установить положение фигуры на холсте.
После этого мы рисуем дугу для рта одним цветом, и то же самое делаем с прямоугольниками для глаз. Теперь наш смайлик готов к отображению на экране:
В DrawScope доступно множество методов для рисования на холсте, вот пример текущих функций:
- drawCircle() // рисует круг в заданных координатах
- drawArc() // рисует дугу, масштабируемую в размер заданного прямоугольника
- drawImage() // рисует ImageBitmap на холсте
- drawPoints() // рисует последовательность точек
- drawPath() // рисует путь заданным цветом
и многое другое … Теперь давайте посмотрим, как мы можем анимировать рисунки, которые мы добавили на холст.
Анимации с Canvas
Теперь, когда у нас есть основы, давайте посмотрим, как мы можем пойти дальше и реализовать и анимировать более сложные рисунки. Я решил разработать анимацию волны, которая медленно смещается вниз и уменьшается с течением времени.
DrawScope предлагает действительно хороший API для непосредственной анимации рисунков на холсте. Вы можете применять переводы, вращения или масштабные преобразования. Для анимации волны мы используем преобразование перевода.
Сначала мы определяем два типа AnimationState для реализации целевой анимации. Во-первых, мы устанавливаем бесконечную анимацию во float от 0 до 1, чтобы получить эффект бесконечной волны благодаря RememberInfiniteTransition(). Затем мы выставляем значение анимации с помощью animateFloat() и соответствующие спецификации.
Для всех конечных анимаций мы будем работать напрямую с функциями AnimationState, такими как animateFloatAsState(), animateColorAsState ()… где можно установить целевое значение и определить спецификации анимации.
Теперь, когда мы определили состояния, мы можем реализовать волновую анимацию. Чтобы нарисовать саму волну, мы собираемся использовать Path, который позволит нам добавлять сегменты кривой Безье к нашему пути, как функцию синусоиды. Затем, когда рисунок будет готов, нам нужно обернуть его лямбда-выражением translate(), предоставляемым DrawScope, и передать значение AnimateState для анимации верхних пикселей.
Вот полная анимация в действии!
Используем нативный Canvas для рисования текста
На данный момент вы не можете рисовать текст прямо на холсте Jetpack Compose. Для этого вам нужно получить доступ к нативному холсту из фреймворка Android, чтобы нарисовать на нем текст. В лямбде onDraw компонента Canvas вы можете вызвать функцию drawIntoCanvas для доступа к базовому Canvas с помощью nativeCanvas (весьма полезно, если вы можете повторно использовать некоторую логику рисования, реализованную в предыдущих приложениях Android). Затем вы можете вызвать все методы, связанные с собственным Canvas, например, drawText() или drawVertices().
Чтобы применить стиль к тексту, необходимо использовать объект Paint. Поскольку мы используем нативный холст, мы не можем использовать Paint из Jetpack Compose напрямую с функцией drawText(). Чтобы получить собственный экземпляр Paint, мы можем вызвать метод asFrameworkPaint() для работы с android.graphics.Paint.
Вот фрагмент кода, который показывает, как нарисовать простой текст на нативном холсте:
А вот как это выглядит в приложении:
Вы можете использовать все преобразования Jetpack Compose Canvas (перевод, поворот, масштабирование…) и обернуть все в drawIntoCanvas, чтобы добавить анимацию к тому, что вы нарисовали.
Рисование на холсте открывает множество возможностей для лучшего дизайна! Вы можете очень легко добавить простые рисунки, но в конечном итоге можете и реализовать сложные математические алгоритмы, если хотите показать пользователям сложные узоры и анимации. У Jetpack Compose Canvas есть что предложить, поэтому используйте его с умом.