Similar presentations:
Устанавливайте приложение ЦифроАрбат
1.
2.
3.
4. Устанавливайте приложение а ЦифроАрбат
● можно нажатьи
получать напоминание о мероприятии
● обсуждать мероприятие
● делиться мероприятием с друзьями
5. Навигация в Android
от UX до реализации6.
Севастьян ЖуковAndroid Developer
@seva_zhukov
6
7.
Новые фичиУсложнение навигации
Удобный UX
7
8.
Навигация — процесс управлениянекоторым объектом
Навигация в Android — перемещение
между фичами
8
9.
Панель навигации+
Программная реализация
=
Навигация
9
10.
от UX10
11.
Drawer LayoutTab Navigation
Circle Menu
Classic Menu
Гибридные решения
11
12.
Drawer Layout— выдвигающаяся панель сбоку
Доступ к фиче за 2 действия
В приложении предполагается большое
количество важных фич (более 5) и
доступ к ним хотелось бы иметь за
минимальное количество шагов
12
13.
Drawer Layout+
+
-
Много фич на панели
Привычно для пользователя
Сложная настройка
Сложности доступа на большом экране (тяжело
тянуться пальцем в противоположную сторону экрана)
13
14.
Tab Navigation— панель внизу экрана
Доступ к фиче за 1 действие
Небольшое количество
главных фич (менее 5)
14
15.
Tab Navigation+
Доступ к фиче в один клик
Возможность разместить на панели малое
количество фич
15
16.
Circle Menu— круглая кнопка в углу для отображения
панели
Доступ к фиче за 2 тапа
Приложение развлекательного портала +
большое количество важных фич (до 9)
16
17.
Circle Menu+
+
Ярко, оригинально и динамично
Большое количество фич (до 9)
-
Нагрузка приложения анимацией
Кнопка занимает часть контента
Непривычно пользователю
17
18.
Classic Menu— панель занимает весь экран и
содержит в себе все фичи
приложения
Доступ к фиче за 1 тап
Небольшое приложение,
просто для новичков
18
19.
Classic Menu+
+
Простота реализации
Очевидность использования
Невозможность перейти к другой фиче, не
вернувшись на главную панель навигации
19
20.
Гибридные решенияНевозможно использовать
только один вид панели
ВК: много важных фич
Tab + Classic
Lingualeo: 2 логических
раздела в одной из фич
Drawer + Tab
20
21.
Выбираем решениеВид\Парамет
р
Drawer Layout
Tab
Navigation
Circle Menu
Classic Menu
Более 5
главных фич
Неопытность Очевидность
разработчика интерфейса
Оригинально
сть
интерфейса
Возможность
большой
вложенности
фич
Минимальны
й шаг доступа
к фиче
+2
0
+2
+1
+2
+1
0
+1
+2
+1
+2
+2
+1
+1
0
+2
+2
+1
+1
+2
+2
0
0
0
21
22.
до реализации22
23.
2324.
PushReplace
Clear stack + Add New Screen
Back / BackTo
Change container
транзакции
Set Root
24
25.
ConductorJetpack
MultiStack Navigation
Library
решения
Cicerone
25
26.
FragmentManagerSet Root
fragmentManager.beginTransaction()
.add(R.id.container, IntroFragment())
.commit()
Replace
fragmentManager.beginTransaction()
.replace(R.id.container, SignInFragment())
.commit()
26
27.
FragmentManagerPush = Replace + AddToBackStack
fragmentManager.beginTransaction()
.replace(R.id.container, SignUpFragment())
.addToBackStack(Screen.SIGN_UP)
.commit()
BackTo
supportFragmentManager.popBackStack(Screen.FIRST.name,
FragmentManager.POP_BACK_STACK_INCLUSIVE)
Back
supportFragmentManager.popBackStack()
27
28.
FragmentManagerClear stack + Set New Screen
for (i in 0 until fragmentManager.backStackEntryCount) {
fragmentManager.popBackStackImmediate()
}
fragmentManager
.beginTransaction()
.replace(R.id.container, fragment)
.commit()
28
29.
FragmentManagerПереход между контейнерами (активити)
startActivity(Intent(this, MainActivity::class.java))
Передача данных между фрагментами
val bundle = Bundle()
bundle.putString("id", "1")
val postFragment = PostFragment()
postFragment.arguments = bundle
29
30.
FragmentManager.ПроблемыМного кода
Нет сохранения состояния навигатора
Нужно учитывать жизненный цикл контейнера
java.lang.IllegalStateException: Can not perform
this action after onSaveInstanceState
30
31.
FragmentManager.Решение проблемExtensions
fun Fragment.setClearScreen(containerId: Int, fragment: Fragment, ) {
for (i in 0 until fragmentManager!!.backStackEntryCount) {
fragmentManager!!.popBackStackImmediate()
}
fragmentManager!!
.beginTransaction()
.replace(containerId, fragment)
.commit()
}
31
32.
CiceroneScreen
32
33.
Ciceronecicerone = Cicerone.create()
fun getNavigatorHolder(): NavigatorHolder {
return cicerone.navigatorHolder
}
fun getRouter(): Router {
return cicerone.router
}
33
34.
var navigator = object :SupportFragmentNavigator(supportFragmentManager,
R.id.main_container) {
override fun createFragment(screenKey: String?, data:
Any?): Fragment {
return when (screenKey) {
Screen.FIRST.name -> FirstScreen() ...
} ...
}
override fun onResume() {
super.onResume()
App.instance.getNavigatorHolder().setNavigator(navigator)
}
34
35.
CiceroneSet Root
getRouter().newRootScreen(Screen.FIRST.name)
Replace
getRouter().replaceScreen(Screen.SECOND.name)
Push
getRouter().navigateTo(Screen.SECOND.name)
35
36.
CiceroneClear stack + Set New Screen
getRouter().newRootScreen(Screen.FIRST.name)
Передача данных между экранами
getRouter().navigateTo(Screen.SECOND.name, data)
BackTo
App.instance.getRouter().backTo(Screen.FIRST.name)
36
37.
CiceroneBack
App.instance.getRouter().exit()
Переход между контейнерами (активити)
getGlobalRouter().navigateTo(Screen.MAIN.name)
37
38.
CiceroneСохранение состояния навигатора
Транзакции в одну строку
Экраны - не обязательно фрагменты
Дополнительный код реализации навигации
38
39.
ConductorController
ControllerTransaction
Router
do it
Controller
ChangeHandler
39
40.
ConductorRouter
val router = Conductor.attachRouter(this, auth_container,
savedInstanceState)
Set Root
router.setRoot(RouterTransaction.with(IntroController()))
Replace
router.replaceTopController(RouterTransaction.with(SignInCont
40
roller()))
41.
ConductorPush
router.pushController(RouterTransaction.with(SignUpController
()))
Clear stack + Set New Screen
router.setRoot(RouterTransaction.with(IntroController()))
Back
router.handleBack()
41
42.
ConductorПередача данных между экранами
router.pushController(RouterTransaction.with(SignUpController
(data)))
BackTo
router.popToTag(TAG)
Переход между контейнерами
startActivity(Intent(this, MainActivity::class.java))
42
43.
Conductor.Интеграция с архитектуройMosby (MVP)
Controller - MvpController
Presenter создается после onCreateView и до onAttach
override fun onAttach(view: View) {
super.onAttach(view)
presenter.doSomething()
}
43
44.
ConductorСохранение состояния навигатора
Транзакции в одну строку
Быстрые транзакции
Легко интегрируемая анимация
Не нужна “ручная” реализация навигации
Ограниченный выбор готовых архитектурных решений
44
45.
Navigation Architecture ComponentNavigation
Navigation Graph
Fragment3
Fragment1
NavController
Fragment2
45
46.
Navigation Architecture Component46
47.
<navigation...
android:id="@+id/mobile_navigation"
app:startDestination="@id/authFragment">
<fragment
android:id="@+id/authFragment"
android:name="com.memebattle.flexible_control.feature.auth.presentation.AuthFrag
ment"
android:label="fragment_auth"
tools:layout="@layout/fragment_auth" >
<action
android:id="@+id/action_authFragment_to_mainFragment"
app:clearTask="true"
app:destination="@id/mainFragment" />
</fragment>
...
</navigation>
47
48.
Navigation Architecture Componentclass MainActivity : AppCompatActivity() {
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
App.component.inject(this)
navController = Navigation.findNavController(this,
R.id.nav_host_global)
navController.navigate(R.id.action_authFragment_to_mainFragment)
}
}
48
49.
Navigation Architecture ComponentSet Root
in Navigation Graph: app:startDestination="@id/authFragment"
Clear stack + Set New Screen
in action: app:clearTask="true"
Передача данных между экранами
navController.navigate(R.id.mapFragment, args)
49
50.
Navigation Architecture ComponentPush
navController.navigate(R.id.action_authFragment_to_mainFragment)
Back
navController.popBackStack()
BackTo
navController.popBackStack(R.id.mapFragment, true)
50
51.
Deep LinkЗапуск приложения сразу на нужном экране
Установка Deep Link в destination
<deepLink
android:id="@+id/deepLink"
app:uri="app://myapp/frag3" />
51
52.
Deep LinkAndroidManifest.xml
<activity
android:name=".MainActivity">
<nav-graph android:value="@navigation/main_graph" />
</activity>
Авто-настройка Intent Filter
52
53.
Navigation Architecture ComponentsNavigation UI
Автоматическое переключение по стекам
NavigationUI.setupWithNavController(bottomNavigationView, navController)
Стеки экранов не сохраняются!
53
54.
Navigation Architecture ComponentНаглядный граф навигации
Deep Link
Nested Graph
На графе нельзя описать переходы назад
java.lang.IllegalStateException: Can not perform this action
after onSaveInstanceState
54
55.
Заряжаем колоды фрагментовс MultiStack Navigation Library
55
56.
Стэки1
2
3
56
57.
Порядок стековtap profile item [profile]
tap news item [profile, news]
tap messages item [profile, news, messages]
tap news item [profile, messages, news]
57
58.
Pop ScreenСпуск по стеку item а
Если стек item а заканчивается, то происходит спуск по
порядку стеков
[profile, news]
[profile]
58
59.
Глобальный Push ScreenЕсть возможность сделать Push Screen в глобальный
контейнер, оставаясь в локальном бэкстеке
global
global
push
local
screen
59
60.
Настройка MSNL.Activityclass MainActivity : AppCompatActivity() {
private lateinit var msFragmentManager: MSFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
msFragmentManager =
MSFragmentManager(supportFragmentManager)
msFragmentManager.globalContainerId = R.id.global_container
msFragmentManager.addGlobal(MainFragment())
}
}
60
61.
Настройка MSNL.Activityclass MainActivity : AppCompatActivity() {
private lateinit var msFragmentManager: MSFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
msFragmentManager =
MSFragmentManager(supportFragmentManager)
msFragmentManager.globalContainerId = R.id.global_container
msFragmentManager.addGlobal(MainFragment())
}
}
61
62.
Настройка MSNL.Activityclass MainActivity : AppCompatActivity() {
private lateinit var msFragmentManager: MSFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
msFragmentManager =
MSFragmentManager(supportFragmentManager)
msFragmentManager.globalContainerId = R.id.global_container
msFragmentManager.addGlobal(MainFragment())
}
}
62
63.
Настройка MSNL.Activityclass MainActivity : AppCompatActivity() {
private lateinit var msFragmentManager: MSFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
msFragmentManager =
MSFragmentManager(supportFragmentManager)
msFragmentManager.globalContainerId = R.id.global_container
msFragmentManager.addGlobal(MainFragment())
}
}
63
64.
Настройка MSNL.Activityclass MainActivity : AppCompatActivity() {
private lateinit var msFragmentManager: MSFragmentManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
msFragmentManager =
MSFragmentManager(supportFragmentManager)
msFragmentManager.globalContainerId = R.id.global_container
msFragmentManager.addGlobal(MainFragment())
}
}
64
65.
Настройка MSNL.Activityoverride fun onBackPressed() {
val fragment = this.supportFragmentManager
.findFragmentById(R.id.global_container)
(fragment as? IOnBackPressed)?.onBackPressed()
}
65
66.
Настройка MSNL.FlowFragmentclass MainFragment : Fragment(), IOnBackPressed {
override fun onCreateView(inflater: LayoutInflater, container:
ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_main, container, false)
msFragmentManager.localContainerId = R.id.local_container
val fragments = arrayListOf(NewsFragment(), MessagesFragment(),
FriendsFragment(), ProfileFragment())
MSNavigation.setupNavigation(msFragmentManager,
v.bottomNavigationView, fragments)
return v
}
}
66
67.
Настройка MSNL.FlowFragmentclass MainFragment : Fragment(), IOnBackPressed {
override fun onCreateView(inflater: LayoutInflater, container:
ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_main, container, false)
msFragmentManager.localContainerId = R.id.local_container
val fragments = arrayListOf(NewsFragment(), MessagesFragment(),
FriendsFragment(), ProfileFragment())
MSNavigation.setupNavigation(msFragmentManager,
v.bottomNavigationView, fragments)
return v
}
}
67
68.
Настройка MSNL.FlowFragmentclass MainFragment : Fragment(), IOnBackPressed {
override fun onCreateView(inflater: LayoutInflater, container:
ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_main, container, false)
msFragmentManager.localContainerId = R.id.local_container
val fragments = arrayListOf(NewsFragment(), MessagesFragment(),
FriendsFragment(), ProfileFragment())
MSNavigation.setupNavigation(msFragmentManager,
v.bottomNavigationView, fragments)
return v
}
}
68
69.
Настройка MSNL.FlowFragmentclass MainFragment : Fragment(), IOnBackPressed {
override fun onCreateView(inflater: LayoutInflater, container:
ViewGroup?, savedInstanceState: Bundle?): View? {
val v = inflater.inflate(R.layout.fragment_main, container, false)
msFragmentManager.localContainerId = R.id.local_container
val fragments = arrayListOf(NewsFragment(), MessagesFragment(),
FriendsFragment(), ProfileFragment())
MSNavigation.setupNavigation(msFragmentManager,
v.bottomNavigationView, fragments)
return v
}
}
69
70.
Настройка MSNL.FlowFragmentoverride fun onBackPressed(): Boolean {
MSNavigation.onBackPressed()
return true
}
70
71.
MSNL.Методыfun navigate(fragment: Fragment, args: Bundle?)
fun replace(fragment: Fragment, args: Bundle?)
fun navigateGlobal(fragment: Fragment, args: Bundle?)
fun replaceGlobal(fragment: Fragment, args: Bundle?)
fun add(containerId: Int, fragment: Fragment)
fun addGlobal(containerId: Int, fragment: Fragment)
fun back()
fun backTo(fragmentTag: String)
71
72.
Вопросы?Севастьян Жуков
@seva_zhukov
MSNL
72