Похожие презентации:
A4-practive-nav
1.
Android Practive #4Многоэкранное
приложение
и навигация
Clients
XML Layout · MVVM · Activity navigation · Side Menu
☰ Clients Categories
2.
Что должно получиться к концу занятияМини-приложение “Clients”: список, редактирование клиентов, редактирование категорий, боковое меню.
Старт
Одна Activity
RecyclerView + CRUD
ViewModel держит список
Финиш
MainActivity
список клиентов
Открывать экран редактирования по клику на клиента.
Передавать clientId через Intent extras.
Возвращаться назад и обновлять UI через
ViewModel / LiveData.
Добавить Side Menu для навигации
EditClientActivity
имя + категория
CategoriesActivity
cashback по категориям
Clients -> Edit Client
Clients -> Categories
Drawer menu -> screen route
02
3.
MVVM в нашем приложенииView рисует экран. ViewModel хранит состояние и отдаёт его через observable-данные.
View
Activity / XML
RecyclerView
кнопки
viewModel.uiState.observe(this) { state ->
adapter.submitList(state.clients)
emptyText.isVisible = state.clients.isEmpty()
}
ViewModel
clients:
LiveData<List<Client>>
add / update / delete
Model / Repository
Client
Category
cashback
После поворота Activity пересоздаётся.
ViewModel переживает configuration change.
UI должен заново подписаться и перерисоваться.
// Activity не хранит список напрямую
04
4.
MVVM в нашем приложенииКак выглядит LiveData во ViewModel
Обычно внутри ViewModel создают изменяемый объект MutableLiveData,
но наружу отдают только неизменяемый интерфейс LiveData.
два главных свойства:
private val _clients: MutableLiveData<List<Client>>
val clients: LiveData<List<Client>>
_clients доступен только внутри ViewModel. Его можно изменять.
clients доступен из Activity.
04
5.
Начинаем навигацию :)Редактирование клиента и категорий не стоит держать внутри MainActivity.
MainActivity сейчас
список + add + edit + delete
вся логика в одном месте
// Было условно так:
MainActivity
├─ show list
├─ add client
├─ edit dialog
├─ delete client
└─ edit categories
Проблемы
• растёт XML
• растёт Activity
• сложнее тестировать
• хуже UX
Решение
отдельные экраны
для отдельных задач
// Станет так:
MainActivity -> EditClientActivity
MainActivity -> CategoriesActivity
// уже лишнее
05
6.
Навигация через Activity и IntentДля первого занятия берём понятную схему: одна задача — одна Activity.
Activity
Intent
val intent = Intent(this, EditClientActivity::class.java)
intent.putExtra("CLIENT_ID", client.id)
startActivity(intent)
New Activity
Back stack
val clientId = intent.getStringExtra("CLIENT_ID")
// загрузить клиента по id
// заполнить EditText / Spinner
Intent — объект-описание действия и данных для запуска Activity.
Back stack позволяет вернуться кнопкой Back без ручного “закрытия”.
Для результата есть Activity Result API; для первого прохода можно начать с onResume.
06
7.
Обновляем модель данныхТеперь клиент имеет категорию, а категория — cashback.
enum class ClientCategoryType {
REGULAR, PREMIUM, VIP
}
data class ClientCategory(
val type: ClientCategoryType,
val title: String,
val cashback: Double
)
Почему enum?
Cashback
отдельно
фиксированный набор
без опечаток в строках
удобно для Spinner
категории можно менять
клиент хранит только type
справочник один для всех
data class Client(
val id: Int,
val name: String,
val category: ClientCategoryType
)
07
8.
Где хранить данные между Activity?Activity-scoped ViewModel не шарится автоматически между разными Activity.
MainActivity
ClientsViewModel
EditClientActivity
EditClientViewModel
CategoriesActivity
CategoriesViewModel
ClientRepository object
единый источник данных для практики
object ClientRepository {
private val clients = mutableListOf<Client>()
private val categories = mutableMapOf<ClientCategoryType, ClientCategory>()
fun getClient(id: Int): Client? = clients.find { it.id == id }
fun updateClient(client: Client) { /* replace */ }
}
08
9.
Практика 1: открываем экран редактированияКлик по клиенту → Intent → EditClientActivity.
Проверка
// В адаптер передаём callback
class ClientsAdapter(
private val onEditClick: (Client) -> Unit
) : RecyclerView.Adapter<ClientsViewHolder>()
// В MainActivity
val adapter = ClientsAdapter { client ->
val intent = Intent(this, EditClientActivity::class.java)
intent.putExtra("CLIENT_ID", client.id)
startActivity(intent)
}
Нажатие на строку клиента открывает новый
экран.
clientId можно увидеть в Logcat.
Кнопка Back возвращает на список.
Мини-правило
Не передаём весь объект клиента,
передаём id. Экран сам загружает данные.
09
10.
Практика 2: форма EditClientActivityНа экране редактирования меняем имя и категорию клиента.
Edit client
// layout idea
EditText: clientNameEditText
Spinner: categorySpinner
Button: saveButton
Name
// Save
val updated = client.copy(
name = nameEditText.text.toString(),
category = selectedCategory
)
viewModel.updateClient(updated)
finish()
Category
SAVE
При открытии: заполнить поля из ViewModel.
При Save: обновить repository/ViewModel.
После finish() пользователь возвращается назад.
10
11.
Практика 3: экран категорийОтдельный экран редактирует cashback для REGULAR, PREMIUM, VIP.
REGULAR
Cashback: 1%
PREMIUM
Cashback: 5%
// CategoriesActivity
val categories = viewModel.categories.value
// Можно сделать три EditText:
regularCashbackEditText
premiumCashbackEditText
vipCashbackEditText
VIP
Cashback: 10%
SAVE ALL
Это справочник, а не список клиентов.
Изменение cashback влияет на отображение всех
клиентов этой категории.
Для 90 минут достаточно трёх полей без
RecyclerView.
// Затем сохранить в repository
11
12.
Как обновить список после возврата?Есть быстрый учебный вариант и более правильный современный вариант.
Вариант A
onResume()
override fun onResume() {
super.onResume()
viewModel.reloadClients()
}
Простой, понятный, но может
перезагружать чаще, чем нужно.
Вариант B
Activity Result API
На занятии
val editLauncher =
registerForActivityResult(
ActivityResultContracts.StartActivityFo
rResult()
) { result ->
if (result.resultCode == RESULT_OK)
{
viewModel.reloadClients()
}
}
Базовый путь: onResume.
Улучшенный путь: Activity
Result API.
Объяснить, почему
startActivityForResult устарел.
Чище: обновляем UI только когда
экран реально сообщил об
изменениях.
12
13.
Side Menu: зачем и из чего состоитБоковое меню даёт постоянные точки входа: Clients, Categories, About.
<androidx.drawerlayout.widget.DrawerLayout>
<LinearLayout>
<!-- Toolbar + RecyclerView -->
</LinearLayout>
NavigationView
☰ Clients
☰ Categories
☰ About
content
MainActivity layout
<com.google.android.material.navigation.NavigationView
android:layout_gravity="start"
app:menu="@menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
DrawerLayout — контейнер, который умеет выдвигать меню.
NavigationView — список пунктов меню.
Toolbar/Hamburger — кнопка открытия меню.
13
14.
Порядок кодинга: файл за файломЧёткий маршрут снижает хаос в Android Studio.
1. Client.kt
добавить category
2. ClientRepository.kt
общий источник данных
3. EditClientActivity.kt/xml
форма редактирования
4. CategoriesActivity.kt/xml
cashback
5. MainActivity.kt
Intent navigation
6. activity_main.xml + menu
Side Menu
14
15.
Типичные ошибки и быстрая диагностикаБольшая часть проблем будет не в “логике”, а в связке XML ↔ Activity ↔ Manifest.
Activity не открывается
проверить Manifest, имя класса, Intent target
clientId == null
проверить key в putExtra/getExtra
список не обновился
reload в onResume или result callback
Spinner пустой
adapter для Spinner и enum values
меню не открывается
DrawerLayout root + gravity=start + toolbar click
15
16.
ВажноеНавигация — это не просто “открыть экран”, а связать экран, данные и жизненный цикл.
1
Экран = отдельная
пользовательская задача
2
Intent передаёт
минимальные данные
4
UI подписывается на
состояние
3
ViewModel не заменяет
навигацию
5
Side Menu — вход в
основные разделы
Следующий логичный шаг: Navigation Component, Fragment-based navigation или Compose
Navigation.
17
17.
Источники и опорные материалыДокументация Android, на которую можно ссылаться студентам после занятия.
Android Developers:
● Navigation component — общая модель navigation graph / destinations.
● Intents and intent filters — запуск Activity через Intent.
● ViewModel — состояние UI и переживание configuration changes.
● LiveData — lifecycle-aware observable holder.
● Activity Result APIs — современный способ получать результат от Activity.
18
Программирование