Я новичок в MVVM и пытаюсь создать приложение.
Приложение состоит из 4 разных частей: Профиль (вход - редактирование профиля и т.д.) Категории (показывает кошек и подкатегории) Товары (показать все товары, страницу с одним товаром, поиск и т. д.) Избранное (списки избранного, добавление в избранное, удаление избранного)
Мои вопросы: как мне перемещаться по пользовательскому интерфейсу с помощью Jetpack Compose? Нужно ли мне иметь одно основное действие и навигатор, который обрабатывает все страницы приложения? или Нужно ли мне создавать 4 действия с 4 различными моделями представления для переключения между ними?
Идеально ли иметь 4 разных репозитория и собрать их все в одну большую модель представления?
Кроме того, как мне просмотреть жизненный цикл модели, должны ли они жить внутри активности?
Для навигации вы можете использовать Jetpack Compose Navigation, который определяет ваши экраны и график навигации в основной деятельности.
Вы можете иметь ProfileViewModel
, CategoriesViewModel
, ItemsViewModel
и FavoritesViewModel
. Все они должны выполнять только задачу, указанную на этом конкретном экране.
ViewModels
в идеале должны иметь жизненный цикл, привязанный к соответствующим компонентам пользовательского интерфейса. Для действий вы можете создавать и использовать модели представлений, ограниченные жизненным циклом действия.
код:
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "profile") {
composable("profile") {
val viewModel: ProfileViewModel = viewModel()
ProfileScreen(navController, viewModel)
}
composable("categories") {
val viewModel: CategoriesViewModel = viewModel()
CategoriesScreen(navController, viewModel)
}
// Define other screens here
}
}
class ProfileViewModel : ViewModel() {
// Profile-related data and logic
}
class CategoriesViewModel : ViewModel() {
// Categories-related data and logic
}
@Composable
fun ProfileScreen(navController: NavController, viewModel: ProfileViewModel) {
// Profile screen UI
}
@Composable
fun CategoriesScreen(navController: NavController, viewModel: CategoriesViewModel) {
// Categories screen UI
}
ссылка для получения дополнительной информации - https://developer.android.com/codelabs/basic-android-kotlin-compose-navigation#0
В приложениях Compose обычно есть только одно действие, производное от ComponentActivity
. Основной целью мероприятия будет настройка дерева создания с помощью setContent
. Здесь будет основан весь ваш пользовательский интерфейс с вложенными вызовами составных функций. Различные части вашего приложения будут независимыми составными функциями, которые необходимо вызывать здесь. Они называются «Экранами», хотя это всего лишь соглашение об именах. Это просто создаваемые вами простые компонуемые функции.
Чтобы обеспечить средство навигации между этими частями, вы обычно используете NavHost
. Это встроенный компонент, который не будет иметь собственного контента, вместо этого он будет содержать ваши экраны. Вы можете использовать контроллер для переключения экрана, который должен отображаться в данный момент. Предполагается, что экраны должны отображаться в полноэкранном размере за вычетом того, что необходимо для панелей навигации.
Компонуемые экраны будут отображать каждую из ваших частей. Они могут даже содержать еще один NavHost
, если вам нужна вложенная навигация, когда экран состоит из нескольких подэкранов. Обычно с каждым экраном связана своя собственная модель представления, но она не обязательно должна быть только одна. Модель представления должна предоставлять все данные, которые должна отображать экран. Будете ли вы использовать только одну модель представления или несколько, или даже используете ли вы модель представления для разных экранов, решать вам, но каждая модель представления должна иметь значимый объем данных, которые она предоставляет. Вероятно, вам следует начать с одной модели представления на экран и двигаться дальше.
Ваши экраны должны запрашивать экземпляры необходимых им моделей представления. Вам не следует создавать их самостоятельно, для этого есть функции компоновки (а именно viewModel()
). Самый простой способ работы с моделями представлений — использовать среду внедрения зависимостей, например Hilt, особенно если вам нужно предоставить параметры модели представления (тогда вы будете использовать hiltViewModel()
). Хотя модели представлений создаются впервые, когда это необходимо экрану, они имеют собственный жизненный цикл. Модели представления не уничтожаются автоматически, когда ваш составной объект покидает композицию, например, когда пользователь переходит на другой экран. Это полезно, поскольку при возврате к экрану модель представления предоставит предыдущее состояние. Вы должны позволить Android SDK управлять жизненным циклом ваших моделей представления.
Ответственность модели представления заключается только в преобразовании данных, чтобы их можно было легко отобразить на ваших экранах (бизнес-логика, которую вы можете даже делегировать отдельному уровню предметной области, если она слишком сложна). Сами данные извлекаются из репозиториев. Обычно модель представления использует несколько репозиториев (опять же, самый простой способ получить экземпляр репозитория — использовать Hilt). Каждый репозиторий должен обеспечивать легкий доступ для извлечения и манипулирования хранящимися в нем данными. Если модели представлений в большей степени ориентированы на пользовательский интерфейс, то репозитории ориентированы на данные. Их основная цель — предоставить независимый доступ к источникам данных: в идеальном случае вы можете заменить источник данных другой реализацией, и вам останется только адаптировать репозиторий; модель представления и ваш пользовательский интерфейс создания не увидят никакой разницы. Вы захотите, чтобы ваши репозитории предоставляли данные в виде Потока (который будет трансформироваться вашей моделью представления и собираться только в пользовательском интерфейсе). В лучшем случае ваши источники данных уже предоставляют потоки, поэтому репозиторию особо нечего делать, но, возможно, источник данных основан на обратном вызове, тогда вашему репозиторию придется инкапсулировать это с помощью потока. Репозитории также могут реализовывать собственное поведение кэширования. Репозитории обычно должны быть одиночными, то есть должен быть только один экземпляр каждого класса репозитория, все модели представлений, которым нужен этот репозиторий, должны использовать один и тот же экземпляр. Это также можно легко реализовать с помощью Hilt.
Google предоставляет различные примеры проектов, которые вы можете использовать, чтобы увидеть, как они решили все эти проблемы. Хорошим началом будет проект Подсолнух.
Правильно! Просто замените
collectAsState
наcollectAsStateWithLifecycle
из зависимости Gradleandroidx.lifecycle:lifecycle-runtime-compose
. Таким образом, поток вашей модели представления будет правильно приостановлен/возобновлен в зависимости от того, когда это действительно необходимо.