Как создать приложение для заметок о чистой архитектуре (MVVM / CRUD / Jetpack Compose) - Руководство по Android Studio

YOUTUBE · 27.11.2025 08:19

Ключевые темы и таймкоды

Введение

0:00
  • Приветствие и анонс нового видео о чистой архитектуре.
  • Упоминание о предыдущем видео о криптовалюте и обещание новых видео при успехе текущего.

Особенности видео

0:44
  • Использование локальной базы данных Room вместо общедоступного API.
  • Получение отзывов по последнему проекту и планы по улучшениям.
  • Объяснение чистой архитектуры с нуля.

Интерфейс приложения

1:43
  • Описание пользовательского интерфейса приложения Node с Jetpack Compose.
  • Примеры заметок с цветами и возможностью сортировки.

Создание и управление заметками

2:38
  • Процесс создания заметки: выбор цвета, заголовка и контента.
  • Анимация затухания цвета при сохранении заметки.
  • Возможность редактирования и удаления заметок.

Значение чистой архитектуры

4:33
  • Преимущества чистой архитектуры для портфолио и навыков.
  • Различие между MVVM и чистой архитектурой.
  • Принципы SOLID и новые подходы в чистой архитектуре.

Масштабируемость и тестирование

6:24
  • Важность масштабируемости и простоты расширения архитектуры.
  • Лёгкость тестирования функций и замены компонентов.
  • Пример перехода от Retrofit к Ktor.

Структура модулей

7:22
  • Эмуляция структуры модулей в Android с помощью пакетов.
  • Фокус на структуре пакетов для понимания чистой архитектуры.

Начало работы

8:18
  • Регистрация на электронную рассылку для получения рекомендаций.
  • Описание начального проекта с настроенными стилями и зависимостями.
  • Ссылка на репозиторий на GitHub для загрузки проекта.

Настройка структуры пакета

9:08
  • Настройка структуры пакета для понимания работы чистой архитектуры.
  • Закрытие вкладок для продолжения работы.

Введение в структуру пакета

9:59
  • Корневой пакет содержит пользовательский интерфейс и основные действия.
  • Три основных уровня в чистой архитектуре: представление, данные и домен.
  • Уровень представления содержит пользовательский интерфейс.
  • Уровень данных отвечает за доступ к API, базе данных и общим настройкам.
  • Уровень домена содержит бизнес-правила и логику, например, фильтрацию списка.

Преимущества многомодульной архитектуры

10:57
  • Многомодульная архитектура позволяет создавать небольшие и заменяемые модули.
  • Маленькие модули ускоряют сборку приложения.
  • Замена модулей становится проще при использовании нескольких функций.

Разделение приложения по функциям

11:54
  • Функция приложения — это набор экранов, например, отображение узлов, добавление заметок, удаление узлов.
  • Каждая функция имеет свои уровни представления, домена и данных.
  • Такое разделение облегчает понимание структуры приложения и его замену.

Практическое применение структуры

13:49
  • Создание пакета для функции node с именем feature_node.
  • Разделение пакета на слои: представление, данные и домен.
  • Важность абстракции источника данных, чтобы избежать зависимости от конкретной базы данных.

Создание пакетов данных и домена

14:47
  • Создание пакета данных с пакетом для источника данных.
  • Создание пакета домена с пакетами модели, use case, репозитория и утилит.

Создание презентационного уровня

15:43
  • Создание пакета для каждого экрана: заметки и добавить узел редактирования.
  • Добавление утилитарного пакета и компонентов для каждого экрана.

Дополнительные пакеты

17:41
  • Создание пакета di для внедрения зависимостей с помощью Dagger Hilt.
  • Возможность создания пакета core для общих компонентов.

Реализация класса узла

18:33
  • Создание класса node в пакете model с полями: заголовок, тип содержимого, временная метка, цвет, идентификатор.
  • Добавление сопутствующего объекта для управления цветами узлов.

Создание объекта доступа к данным

21:16
  • Создание интерфейса NodeDao для доступа к данным в базе Room.
  • Определение функций для вставки, удаления, получения одного и всех узлов.

Определение функций DAO

21:45
  • Функция для получения всех узлов возвращает поток.
  • Для работы функции требуется SQL-запрос «select * from nodes».
  • Функция приостановки для получения узла по идентификатору.

Особенности функции приостановки

22:46
  • Идентификатор узла имеет тип integer.
  • Функция возвращает узел с возможностью обнуления, при отсутствии узла возвращается null.
  • Запрос для получения узла по идентификатору.

Функции вставки и удаления узлов

23:08
  • Функция вставки узла с стратегией предотвращения конфликтов «на замену».
  • Функция удаления узла.

Создание класса базы данных

24:38
  • Абстрактный класс NodeDatabase с аннотацией базы данных.
  • Определение таблицы узлов и версии базы данных.
  • Наследование от RoomDatabase.

Роль репозитория

25:57
  • Репозиторий обращается к источникам данных API или база данных.
  • Логика принятия решений о загрузке данных из кэша или API.
  • Проверка ошибок и пересылка данных в варианты использования.

Интерфейс репозитория

26:45
  • Интерфейс NodeRepository для тестирования.
  • Функции: getNodes, getNodeById, insertNode, deleteNode.
  • Возможность создания поддельных версий репозитория для тестирования.

Реализация репозитория

28:38
  • Создание реализации репозитория узлов класса Kotlin.
  • Реализация интерфейса NodeRepository.

Реализация репозитория

28:56
  • Репозиторий принимает параметр — объект DAO.
  • Функции репозитория: getNotes, получение узла по идентификатору, вставка узла, удаление узла.
  • В простом приложении репозиторий вызывает функции DAO, без сложной логики.

Введение в варианты использования

29:57
  • Варианты использования содержат бизнес-логику и делают код удобочитаемым.
  • Каждый вариант использования отвечает за одно действие пользователя.
  • Преимущества: быстрое понимание логики по названию класса, повторное использование кода.

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

31:49
  • Get nodes — самый сложный вариант использования.
  • Возможность сортировки узлов по названию, цвету и дате.
  • Создание класса GetNodes с интерфейсом репозитория.

Структура варианта использования

32:47
  • Один общедоступный метод в варианте использования.
  • Метод возвращает поток списка узлов.

Служебные классы для сортировки

34:20
  • Создание классов TypeOrder и NodeOrder для определения типа заказа и способа сортировки.
  • TypeOrder: восходящий или нисходящий.
  • NodeOrder: по названию, дате или цвету.

Реализация сортировки узлов

37:05
  • Возврат потока со списком узлов из репозитория.
  • Сопоставление списка узлов с отсортированным списком в зависимости от типа заказа.
  • Использование функций сортировки по возрастанию и убыванию.

Детали сортировки

38:10
  • Сортировка по названию с переводом в нижний регистр.
  • Сортировка по дате и цвету.

Повторное использование бизнес-логики

39:16
  • Бизнес-логика описывает доступ к хранилищу заметок.
  • Класс вариантов использования можно повторно использовать во всём приложении.
  • Это упрощает разработку и снижает количество логики в моделях представления.

Создание варианта использования для удаления узла

40:11
  • Создаём класс Kotlin под названием `deleteNodeUseCase`.
  • В конструкторе вызываем репозиторий узлов и функцию оператора приостановки suspend.
  • Функция удаляет узел, вызывая репозиторий с командой `deleteNode`.

Объединение вариантов использования

41:11
  • Объединяем варианты использования в один класс для упрощения конструктора ViewModel.
  • Создаём класс `NodesUseCases` с описанием каждого варианта использования.
  • Планируем расширить `deleteNodeUseCase` в будущем.

Настройка Dagger Hilt

42:05
  • Настраиваем библиотеку внедрения зависимостей Dagger Hilt.
  • Регистрируем класс приложения в манифесте.
  • Создаём модуль для предоставления зависимостей с заданным сроком службы.

Предоставление базы данных и репозитория

44:09
  • Предоставляем экземпляр базы данных комнат через функцию singleton.
  • Используем контекст приложения для создания базы данных.
  • Создаём репозиторий узлов, используя экземпляр базы данных и DAO.

Тестирование и поддельные зависимости

46:10
  • Для тестирования создаём отдельный модуль с поддельным репозиторием узлов.
  • Внедрение зависимостей автоматически заменяет реальные зависимости на поддельные.
  • Это позволяет тестировать классы без изменений в производственном приложении.

Завершение настройки

47:06
  • Предоставляем варианты использования узлов в модуле.
  • Dagger Hilt автоматически внедряет зависимости, упрощая разработку.

Создание класса-оболочки и модели представления

47:20
  • Класс-оболочка предоставляет варианты использования узлов, используя экземпляр репозитория.
  • Для get nodes и delete node возвращаются соответствующие варианты использования.
  • Модель представления напрямую связана с пользовательским интерфейсом и выполняет роль презентатора в шаблоне MVP.

Задачи модели представления

48:16
  • Модель представления использует варианты использования для обработки бизнес-логики.
  • Упорядоченный список узлов, полученный из базы данных, преобразуется в состояние, видимое пользовательскому интерфейсу.
  • Создаётся класс NotesViewModel для управления состоянием пользовательского интерфейса.

Объект состояния

49:14
  • Объект состояния представляет текущее состояние пользовательского интерфейса на экране узла.
  • Включает текущий порядок узлов, список узлов и состояние видимости раздела заказа.
  • Создаётся класс NotesState для хранения этих данных.

Обработка действий пользователя

51:08
  • Модель представления обрабатывает все действия пользователя, такие как изменение порядка узлов, удаление заметки и восстановление узла.
  • Для обработки событий создаётся класс-оболочка NodesEvent.
  • События Order, Delete, Restore и ToggleOrder обрабатываются моделью представления.

Реализация событий

53:05
  • Событие Order передаётся из пользовательского интерфейса в модель представления при изменении порядка узлов.
  • Событие Delete узла передаётся с указанием удалённого узла.
  • Событие Restore узла используется для восстановления последнего удалённого узла.

Обновление состояния

54:38
  • Модель представления обновляет состояние узла при переключении раздела «Порядок».
  • Переменная состояния инициализируется состоянием пустого узла.
  • Логическое значение состояния обновляется для отображения раздела «Порядок».

Удаление и восстановление узла

56:13
  • При удалении узла вызывается вариант использования DeleteNode.
  • Для восстановления узла сохраняется ссылка на последний удалённый узел в приватной переменной.
  • Планируется использование варианта использования AddNode для добавления узла обратно в базу данных.

Создание варианта использования addnode

57:42
  • Создание варианта использования addnode, который принимает ссылку на репозиторий и узел для вставки.
  • Проверка значений узла: название и содержимое.
  • Вставка узла только при успешной проверке значений.

Обработка ошибок при вставке узла

58:37
  • Проверка пустого заголовка узла и выдача исключения.
  • Создание класса исключений для обработки ошибок.
  • Унаследование исключения от исходного исключения.

Проверка полей узла

59:39
  • Проверка пустого заголовка и содержимого узла.
  • Возможность дополнительной проверки минимальной и максимальной длины полей.

Вставка узла и проверка исключений

1:00:17
  • Вставка узла при ложных утверждениях if.
  • Проверка исключений в блоке try-catch.
  • Использование вариантов использования узлов.

Событие передачи заметок

1:01:12
  • Добавление недавно удалённого узла.
  • Присвоение недавно удалённому узлу значения null.
  • Проверка изменения порядка заметок.

Сравнение классов для проверки порядка

1:01:59
  • Сравнение классов node order для проверки изменения порядка.
  • Возврат текущего порядка при совпадении классов.

Функция для возврата узлов в заданном порядке

1:04:30
  • Возврат потока узлов из базы данных.
  • Сопоставление потока с состоянием viewmodel.
  • Запуск потока в viewmodel scope coutine.

Управление сопрограммами

1:05:26
  • Отмена старой сопрограммы при каждом вызове функции.
  • Назначение нового задания для получения узлов.
  • Загрузка узлов в порядке по умолчанию.

Создание экрана заметок

1:07:16
  • Создание переключателя и раздела «Порядок» для упорядочивания списка.
  • Компоновка элементов экрана заметок.

Настройка параметров переключателя

1:08:15
  • Настройка текста, логического значения, функции проверки и модификатора для переключателя.
  • Установка вертикального выравнивания и цветов переключателя.

Создание раздела заказа

1:09:57
  • Создание компонуемого раздела заказа с пятью переключателями.
  • Передача модификатора для раздела заказа.

Порядок узлов и переключатели

1:11:01
  • Передача порядка узлов для определения проверяемых переключателей.
  • По умолчанию порядок узлов определяется по убыванию.
  • При изменении порядка узлов новый порядок передаётся в функцию обратного вызова родительскому компонуемому объекту.

Создание столбца переключателей

1:11:19
  • Создание столбца из двух строк: первая строка содержит три переключателя, вторая — два.
  • Использование модификатора fillMaxWidth для первой строки.
  • Жёсткое кодирование строк для простоты урока.

Настройка переключателей

1:12:19
  • Переключатели выбираются в зависимости от порядка узлов.
  • При нажатии на переключатель «Название» запускается функция обратного вызова с новым порядком узлов.
  • Копирование и вставка переключателей для других типов порядка.

Второй ряд переключателей

1:13:26
  • Установка вертикального пространства и модификатора fillMaxWidth для второго ряда.
  • Копирование первых двух переключающих кнопок для сортировки по возрастанию.
  • Сохранение текущего порядка узлов при изменении типа заказа.

Функция копирования порядка узлов

1:14:26
  • Создание функции copy для изменения типа заказа без использования классов данных.
  • Возврат нового порядка узлов с изменённым типом заказа.
  • Дублирование функции для переключателей «Дата» и «Цвет».

Создание элемента «узел»

1:16:10
  • Создание компонуемого элемента «узел» с параметрами: узел, модификатор, радиус угла.
  • Настройка радиуса срезанного угла 30dp.
  • Использование холста для рисования закруглённых углов и клипсы для обрезки края.

Работа с холстом

1:18:56
  • Создание коробки для рисования поверх холста.
  • Применение модификатора canvas с родительским размером.
  • Объяснение разницы между родительским размером и максимальным размером заливки.

Определение размера холста

1:19:53
  • Холсты требуют фиксированного размера при инициализации.
  • Необходимость относительной единицы измерения для поддержки нескольких размеров экрана.
  • Проверка размера коробки после размещения текстов для определения размера холста.

Введение в холст и курс

1:21:08
  • Обсуждение области для рисования на холсте.
  • Рекомендация ознакомиться с курсом для более глубокого понимания холста.

Создание пути обрезки

1:22:08
  • Определение пути, огибающего прямоугольник с обрезанными углами.
  • Использование контура обрезки для создания пути.
  • Настройка координат x и y для начала пути.

Рисование круглых прямоугольников

1:22:56
  • Рисование большого круглого прямоугольника с цветом узла.
  • Преобразование цвета в цвет композиции.
  • Копирование и рисование маленького круглого прямоугольника с изменённым цветом.

Смешивание цветов

1:24:27
  • Применение операции смешивания цветов для затемнения.
  • Определение соотношения смешивания и верхнего левого угла.
  • Избегание округлений путём смещения угла.

Размещение текста и кнопки

1:26:34
  • Настройка отступов и стилей текста.
  • Создание кнопки со значком для удаления узла.
  • Применение модификатора для выравнивания кнопки.

Создание экрана заметок

1:28:36
  • Создание класса Kotlin для экрана заметок.
  • Настройка параметров: navcontroller и viewmodel.
  • Создание каркаса с плавающей кнопкой действия.

Реализация раздела «Порядок»

1:31:09
  • Настройка модификатора столбца и отступов.
  • Добавление кнопки со значком для переключения порядка.
  • Реализация анимации видимости раздела «Порядок».

Анимация раздела «Порядок»

1:32:42
  • Настройка анимации ввода и выхода раздела.
  • Отправка события в viewmodel при изменении порядка узлов.
  • Создание разделителя и отложенного столбца со списком заметок.

Работа с узлами и модификаторами

1:34:55
  • Использование перегрузки со списком элементов для передачи состояния.
  • Добавление модификатора fillMaxWidth для интерактивности узлов.
  • Реализация удаления узла при нажатии на кнопку «Удалить».

Отображение панели быстрого доступа

1:36:15
  • Использование сопрограммы для асинхронного отображения панели быстрого доступа.
  • Проверка результата действия при нажатии на кнопку «Отменить».
  • Добавление пространства между узлами.

Создание модели для получения узла

1:37:39
  • Создание варианта использования для получения узла по идентификатору.
  • Добавление экземпляра в репозиторий и вызов функции оператора приостановки.
  • Реализация модели представления для добавления узла.

Управление состояниями в модели представления

1:38:49
  • Определение необходимых состояний: текущий выбранный цвет, заголовок, содержимое.
  • Создание отдельных состояний для каждого элемента пользовательского интерфейса.
  • Разработка класса-оболочки для управления состоянием текстового поля.

Использование потока событий

1:42:51
  • Объяснение необходимости общего потока событий пользовательского интерфейса.
  • Отправка разовых событий из viewmodel и их обработка в пользовательском интерфейсе.
  • Примеры событий: показ закусочной, сохранение узла.

Настройка событий для редактирования узлов

1:45:02
  • Создание класса событий для редактирования узлов.
  • Добавление событий для каждого действия пользовательского интерфейса.
  • Настройка подсказок для ввода названия и содержимого узла.

События и состояния

1:46:18
  • Добавление события «добавленный узел».
  • Обработка событий при фокусировке текстовых полей заголовка и содержимого.
  • Использование состояния фокуса для скрытия подсказки.

События изменения цвета и сохранения узла

1:47:17
  • Добавление события изменения цвета узла.
  • Сохранение узла при нажатии на плавающую кнопку действия.
  • Различие событий в модели представления.

Обработка событий фокусировки

1:48:15
  • Обновление значения заголовка при фокусировке текстового поля.
  • Отображение подсказки при отсутствии фокуса и пустом тексте.

События изменения содержимого и цвета

1:49:15
  • Обработка событий изменения содержимого узла.
  • Выбор нового цвета узла при нажатии на цветной круг.

Сохранение узла

1:50:13
  • Запуск варианта использования добавления узла при сохранении.
  • Проверка допустимости данных и генерация исключений при пустых заголовке или содержимом.
  • Передача идентификатора узла для обновления существующего узла.

Получение текущего узла

1:53:17
  • Использование навигационных аргументов для получения идентификатора узла.
  • Проверка идентификатора узла на равенство минус единице.
  • Получение узла по идентификатору и обновление его данных в viewmodel.

Обновление данных узла

1:55:44
  • Обновление значения заголовка, содержимого и цвета узла.
  • Извлечение данных узла из базы данных и отображение их в текстовом поле.

Создание пользовательского интерфейса

1:57:08
  • Создание прозрачного текстового поля с подсказкой.
  • Настройка параметров текстового поля: текст, подсказка, модификатор, стиль текста.
  • Установка функции изменения значения on и логического значения для однострочного текста.

Настройка текстового поля

1:58:20
  • Использование функции изменения фокуса для управления состоянием фокуса.
  • Применение модификатора для стилизации текстового поля.
  • Настройка отображения изменения значения при вводе.

Модификаторы и подсказки

1:59:12
  • Применение модификаторов single line и texttile.
  • Использование модификатора fillmaxwidth для получения информации о состоянии фокуса.
  • Отображение подсказки поверх текстового поля с тёмно-серым цветом.

Реализация экрана добавления заметок

2:00:33
  • Добавление экрана добавления заметок в пакет add added node.
  • Настройка навигационного контроллера для перемещения назад при нажатии «Сохранить».
  • Передача цвета узла в качестве аргумента навигации.

Анимация фона

2:01:51
  • Анимация фона с эффектом затухания цвета.
  • Использование анимируемого свойства для изменения цвета фона.
  • Проверка цвета узла для корректного отображения.

Создание плавающей кнопки действия

2:03:46
  • Создание плавающей кнопки действия с иконкой «сохранить заметку».
  • Присвоение состояния scaffold для отображения элементов.

Настройка цветов узлов

2:05:41
  • Создание ряда для цветов узлов с модификатором fillmaxwidth.
  • Настройка отступов и горизонтального расположения цветов.
  • Создание кругов для каждого цвета с границей и тенью.

Кликабельность цветов и анимация

2:08:11
  • Присвоение кликабельности цветам узлов.
  • Настройка анимации цвета при нажатии на цвет.
  • Установка продолжительности анимации в 500 миллисекунд.

Текстовые поля и наблюдение за событиями

2:09:39
  • Настройка разделителя и прозрачного текстового поля с подсказками.
  • Передача нового заголовка при изменении значения.
  • Наблюдение за событиями пользовательского интерфейса через viewmodel eventflow.

Прокрутка страницы и события

2:12:07
  • Использование запущенного блока эффектов для прокрутки страницы вверх.
  • Сбор последних данных через viewmodel eventflow.
  • Обработка событий пользовательского интерфейса для отображения и скрытия элементов.

Настройка навигации и Snackbar

2:12:37
  • Сохраняем узел из события пользовательского интерфейса.
  • Указываем состояние scaffold и хоста snackbar для отображения Snackbar.
  • Вызываем navcontroller для перехода к экрану заметок.

Определение классов экранов и маршрутов

2:13:45
  • Определяем класс экрана для заметок и экрана добавления/редактирования заметок.
  • Маршрут для экрана заметок — «на экран заметок».
  • Аналогично для экрана добавления и редактирования заметок — «добавить добавленный экран заметок».

Настройка навигационного контроллера

2:14:07
  • Создаём навигационный контроллер 1-го уровня и navhost для определения экранов.
  • Начинаем с пункта назначения — экрана импорта.
  • Используем компонуемую функцию для определения различных экранов.

Передача аргументов в маршруты

2:15:00
  • Добавляем аргументы для маршрута: идентификатор узла и цвет узла.
  • Аргументы необязательные, по умолчанию равны минус единице.
  • Определяем навигационные аргументы с типами данных и значениями по умолчанию.

Тестирование приложения

2:18:17
  • Проверяем работу приложения в эмуляторе.
  • Добавляем примечание и проверяем навигацию.
  • Обновляем и сохраняем заметки, проверяем порядок узлов.

Завершение и призыв к взаимодействию

2:22:08
  • Приложение полностью работает.
  • Автор благодарит зрителей и призывает оставлять комментарии и лайки.
  • Предлагает подписаться на рассылку новостей и ознакомиться с платными курсами.
  • Прощается с зрителями и желает хорошего дня.