Требуется разработать небольшое приложение, которое отображает прямоугольники с числами в несколько колонок. Для портретной ориентации - требуется отображать прямоугольники(или карточки) в 3 колонки, для горизонтальной ориентации требуется отображать прямоугольники в 4 колонки. Отображаемое число - индекс элемента (считать можно как от 0 так и от 1). Четные элементы должны иметь красный фон, нечетные - синий.
Под списком должна находиться кнопка (если используется ActionButton, то она может находиться в нижней части списка), тап по которой добавляет новый прямоугольник к списку. Кнопка (даже Action Button) не должна перекрывать прямоугольники при скролле списка до конца (необходимо добавить отступ в конце списка).
Переворот экрана не должен сбивать количество прямоугольников.
- Нельзя использовать персистентное хранилище (Никаких записей в файловую систему, никаких записей в SharedPreferences).
- Приложение не должно использовать доступ в интернет и манифест не должен содержать uses-permission INTERNET.
- Приложение не должно содержать хардкод.
- Приложение должно использовать ресурсы(resources) для работы.
- В коде можно оставлять комментарии, но в конечной версии нельзя оставлять Log.
Что будет плюсом
- Сделать квадратики, а не прямоугольники.
Основные сценарии проверки
- Поворот экрана до добавления элементов
- Поворот после добавления 10+ элементов
- Скролл до конца списка + проверка позиции кнопки
Условия приема Домашнего задания
- Прием ДЗ 1 будет на РК 1.
- Требуется загрузить код в git-репозиторий, чтобы было удобно его просматривать.
private const val COLUMNS_PORTRAIT = 3
private const val COLUMNS_LANDSCAPE = 4
Храним значения как константы для читаемости кода и так как "обычно числа не хранят в ресурсас, создайте лучше константы".
val columnsCount =
if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) {
COLUMNS_PORTRAIT
} else {
COLUMNS_LANDSCAPE
}
С помощью LocalConfiguration.current.orientation получаем текущую конфигурацию устройства. В зависимости от полученного значения берём из ресурсов нужное колличество колонок.
LazyVerticalGrid(
columns = GridCells.Fixed(columnsCount)...
Фиксируем известное число колонок на основе вычисленного значения в LazyVerticalGrid(компонент в Jetpack Compose, который отображает элементы в виде вертикально прокручиваемой сетки).
{
items(count.intValue, key = { it }) { item ->
val number = item + 1
NumberSquare(number = number)
}
Создаем элементы NumberSquare сетки с помощью LazyVerticalGrid, обеспечивая стабильность ключей для анимаций, передаём соответсвующий номер(нумерация с 1).
val isEven = number % 2 == 0
val color = if (isEven) {
colorResource(id = R.color.even_color)
} else {
colorResource(id = R.color.odd_color)
}
В зависимости от number определяем цвет, подгружая из ресурсов.
Box(
modifier = modifier
.aspectRatio(1f)
.clip(RoundedCornerShape(dimensionResource(id = R.dimen.square_corner_radius)))
.background(color)...
Устанавливаем цвет как background элемента NumberSquare(Box).
LazyVerticalGrid(
columns = GridCells.Fixed(columnsCount),
modifier = Modifier
.fillMaxSize()
.padding(
start = dimensionResource(id = R.dimen.grid_padding_horizontal),
end = dimensionResource(id = R.dimen.grid_padding_horizontal),
bottom = dimensionResource(id = R.dimen.grid_padding_bottom)...
Ключевой элемент - нижний отступ у списка, который создает "буферную зону" внизу списка.
FloatingActionButton(
onClick = { count.intValue++ },
modifier = Modifier
.align(Alignment.BottomEnd)...
FAB позиционируется абсолютно - всегда видна, но не мешает контенту
val count = rememberSaveable { mutableIntStateOf(0) }
rememberSaveable — это как remember, но с дополнительной возможностью сохранять значение при пересоздании Activity(при перевороте экрана). rememberSaveable отлично подходит для нас, так как mutableIntStateOf может быть положен в Bundle(Bundle умеет сохранять только простые типы (Int, String, Boolean, ArrayList и т.п.). mutableIntStateOf просто оборачивает Int.
Box(
modifier = modifier
.aspectRatio(1f)...
Modifier.aspectRatio() - Высота и ширина элемента должны быть связаны в заданной пропорции. 1f - это 1:1.
- Нельзя использовать персистентное хранилище (Никаких записей в файловую систему, никаких записей в SharedPreferences). ✅
- Приложение не должно использовать доступ в интернет и манифест не должен содержать uses-permission INTERNET. ✅
- Приложение не должно содержать хардкод. ✅
- Приложение должно использовать ресурсы(resources) для работы. ✅
- В коде можно оставлять комментарии, но в конечной версии нельзя оставлять Log. ✅
- Нумерация с 1.
- Скругление элементов(выглядит эстетичнее).
- Использование FAB(FloatingActionButton). Хотелось приблизится к примеру в задании.
- Использование windowInsetsPadding(WindowInsets.statusBars) - модификатор в Jetpack Compose, который добавляет отступы для статус-бара или выреза камеры. Зачем? Чтобы контент не залезал под системный статус-бар. Внизу это не необходимо, так как мы всё равно делаем "буферную зону" внизу списка для FAB.
При клике на последний квадратик, он должен удаляться.
Box(
modifier = modifier
.aspectRatio(1f)
.clip(RoundedCornerShape(dimensionResource(id = R.dimen.square_corner_radius)))
.background(color)
.clickable {
lastClick()
}
Элементам(боксам) добавила кликабельность.
fun NumberSquare(
number: Int,
lastClick: () -> Unit,
modifier: Modifier = Modifier
Передала внутрь квадратиков функцию, которая
{
items(count.intValue, key = { it }) { item ->
val number = item + 1
val isLast = number == count.intValue
NumberSquare(
number = number,
lastClick = {
if (isLast) {
count.intValue--
}
})
}
проверяет совпадение номера квадратика(нумерация с 1) с количеством элементов и если это последний квадратик убавляет count.intValue.