В последней версии IntelliJ IDEA (2021.1 на момент публикации) есть классный экран-заставка, основанный на сетке, содержащей различные цветные формы, как показано ниже:
Цель этого поста - реализовать составной компонент Jetpack Compose, имитирующий этот шаблон.
Как мы видим, представление представляет собой сетку из пяти различных типов ячеек: круга, верхнего левого квадранта, верхнего правого квадранта, нижнего левого квадранта и нижнего правого квадранта. Квадранты - это всего лишь четверть части большего круга.
Мы собираемся реализовать это с помощью Canvas. Обычно это звучит как хороший кандидат при рисовании геометрических фигур.
Разделение холста
Чтобы Canvas вел себя как сетка, мы должны параметризовать функцию обертывания для Canvas количеством строк и столбцов для рисования:
@Composable fun IntelliJSplashScreen(rows: Int, columns: Int, modifier: Modifier) = TODO()
На основе этих двух аргументов в первую очередь необходимо вычислить размер ячейки. В этом случае все ячейки будут квадратными, поэтому ширина и высота будут равны. Затем размер ячейки рассчитывается на основе размера холста и количества строк / столбцов. Это позволяет нам перебирать всю сетку, чтобы что-то сделать для каждой ячейки:
Canvas(modifier = modifier) { val cellSize = min(this.size.width / columns, this.size.height / rows) for (row in 0 until rows) { for (column in 0 until columns) { translate( left = column * cellSize, top = row * cellSize ) { // TODO Draw cell drawIntelliJCell(cellSize) } } } }
Обратите внимание, что приведенный выше код включает перевод холста перед рисованием ячейки. Для левого перевода значение основывается на количестве столбцов, а для верхнего мы должны основывать его на строках.
Типы клеток
Как мы уже говорили ранее, существует 5 различных типов клеток:
Радиус круга равен половине cellSize. Квадранты представляют собой дугу окружности под углом 90 градусов с радиусом, равным cellSize. Кажется разумным смоделировать все это в закрытом классе:
sealed class IntelliJCell { object Circle : IntelliJCell() sealed class Quadrant(val startAngle: Float, val topLeftOffset: Offset) : IntelliJCell() { object TopLeft : Quadrant(180f, Offset.Zero) object TopRight : Quadrant(270f, -Offset(1f, 0f)) object BottomLeft : Quadrant(90f, -Offset(0f, 1f)) object BottomRight : Quadrant(0f, -Offset(1f, 1f)) } }
Обратите внимание, что Quadrant также является закрытым классом для группировки всех 4 квадрантов. Класс параметризован двумя свойствами:
- startAngle: представляет начальный угол дуги. 0 соответствует 3 часам, поэтому для нижнего правого угла установлен 0. SweepAngle (размер дуги, измеренный по часовой стрелке) для каждого квадранта равен 90.
- topLeftOffset: дополнительное смещение множителя (относительно cellSize), которое нужно добавить для вычисления левого верхнего угла дуги. Для верхнего левого квадранта легко видеть, что это дополнительное смещение равно 0.
Рисование клеток
С учетом всего вышесказанного мы теперь можем определить функцию расширения drawIntelliJCell
через интерфейс DrawScope для обработки рисования ячейки заданного размера:
fun DrawScope.drawIntelliJCell(cellSize: Float) = TODO()
В этом примере мы просто собираемся рандомизировать тип и цвет ячейки при каждом вызове этой функции:
val cellTypes = listOf( IntelliJCell.Circle, IntelliJCell.Quadrant.TopLeft, IntelliJCell.Quadrant.TopRight, IntelliJCell.Quadrant.BottomLeft, IntelliJCell.Quadrant.BottomRight ) val cellColors = listOf( Color(0xFFFF7000), Color(0xFF007EFF), Color(0xFFFF0058) ) val cell = cellTypes.random() val color = cellColors.random() when (cell) { is IntelliJCell.Circle -> // TODO Draw circle is IntelliJCell.Quadrant -> // TODO Draw quadrant }
Нарисовать круговую ячейку просто, радиус составляет половину размера ячейки, а его центр расположен в середине ячейки:
drawCircle( color = color, radius = cellSize / 2, center = Offset(cellSize, cellSize).div(2f) )
Для квадрантов startAngle предоставляется конкретным квадрантом, а точка topLeft - это дополнительное смещение, обеспечиваемое конкретными квадрантами. Размер в этом случае в два раза больше размера ячейки:
drawArc( color = color, startAngle = cell.startAngle, sweepAngle = 90f, useCenter = true, topLeft = cell.topLeftOffset.times(cellSize), size = Size(cellSize, cellSize).times(2f) )
Все это вместе есть в этом Гисте. Глядя на предварительный просмотр примера компонента с 8 строками и 13 столбцами, мы получаем что-то вроде:
Следующие шаги
Поработав над этим экраном-заставкой, я увидел возможность поработать над дополнительными вещами вокруг этого холста, основанного на сетке. Итак, я напишу следующую статью, в которой расскажу о следующих функциях / изменениях:
- Поддержка выравнивания содержания.
- Добавьте несколько конфигураций размеров (вместо того, чтобы просто установить фиксированное количество строк и столбцов).
- Соотношение сторон ячеек (а не только квадратов).
- Сделайте холст сетки общим (отделите этот конкретный пример от компонента многократного использования).