Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Navigationの採用を検討してみて、 色々考えた話を共有するよ
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
kobashin
July 25, 2019
Programming
310
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Navigationの採用を検討してみて、 色々考えた話を共有するよ
kobashin
July 25, 2019
More Decks by kobashin
See All by kobashin
Mix Leap Study #69 Yahoo!ショッピング+PayPayフリマのSRE事例 ショッピングのSREチームとして試したこと、 失敗したこと
shinjikobayashi
0
310
気になったセッションから今すぐ開発に導入したいあれこれ(主観
shinjikobayashi
2
760
Systemアプリ開発入門
shinjikobayashi
6
13k
Other Decks in Programming
See All in Programming
Semantic Version 単位で戦略を柔軟に変えて、パッケージアップデートを自動化する
daitasu
1
330
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
340
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.6k
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
230
AI時代のUIはどこへ行く?その2!
yusukebe
22
7.7k
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
970
コンテキストの使い捨てをやめる — ビジネスルール駆動開発と miko —
ioki
0
260
Webフレームワークの ベンチマークについて
yusukebe
0
190
Observability in Practice:Grafana 與 Edge Device SRE 的那些事
blueswen
0
180
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
210
正しくソフトウェアを作る、前提を疑うための認知の視点 / doubt-premise
minodriven
21
7.2k
SREの積み重ねがAI駆動開発のガードレールになった ― 7つの実践/SRE Guardrails The 7
tomoyakitaura
5
710
Featured
See All Featured
Chasing Engaging Ingredients in Design
codingconduct
0
230
Deep Space Network (abreviated)
tonyrice
0
220
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.5k
HDC tutorial
michielstock
2
730
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
55k
Amusing Abliteration
ianozsvald
1
220
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
630
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
So, you think you're a good person
axbom
PRO
2
2.1k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.2k
Into the Great Unknown - MozCon
thekraken
41
2.6k
We Have a Design System, Now What?
morganepeng
55
8.2k
Transcript
Mix Leap Study #47 Android x Flutterษڧձ Navigationͷ࠾༻Λݕ౼ͯ͠Έͯɺ ৭ʑߟ͑ͨΛڞ༗͢ΔΑ @kobashinG
ʢ͜͠Μʣ 2019.7.25
ࣗݾհ ͜͠Μ (@kobashinG) Ϡϑʔגࣜձࣾ ϑϩϯτΤϯυશൠ, etc… Android, Linux, k8s, ˏϠϑʔγϣοϐϯά
ΏΔΏΔͱ͓͠·͢ ࠓͷAgenda • Android Jetpack Navigationͱʁ • SingleActivity or Multi-Activityʁ
• BottomNavigationͱҰॹʹ͍͍ͨ • ͦͷଞ (※࣌ؒΓͳ͍߹ඈ͔͢)
Android Jetpack Navigationͱʁ (ຊɺNavigation 2.1.0-beta02࣌ͷΛ͠·͢)
Android Jetpack Navigationͱʁ
Android Jetpack Navigationͱʁ • Navigation GraphΛͬͨUIભҠͷ࣮ • UIͷભҠΛActivity/Fragment͔ΒҠৡՄೳ • ࡶͳFragmentTransactionΛॻ͔ͳͯ͘ࡁΉ
• Fragmentؒͷσʔλड͚͠Λܕ҆શʹʢSafeArgsʣ • DeepLinkΛ؆қ࣮Մೳʹ
3͍ํߨ࠲ Navigation Editor (·ͨxml) Ͱը໘ભҠΛ࡞͢Δ
3͍ํߨ࠲ ActivityʹNavHostFragmentΛஔ͢Δ <androidx.constraintlayout.widget.ConstraintLayout > <fragment android:layout_width="0dp" android:layout_height="0dp" android:id="@+id/root_nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/navigation_graph_with_bottom_nav"
app:defaultNavHost="true" app:layout_goneMarginBottom="0dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintBottom_toTopOf="@id/bottom_navigation" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"/> LayoutʹNavHostFragment Λஔ͢Δ ઌఔ࡞ͨ͠ɺnavigation.xml Λࢦఆ͢Δ layout/activity_main.xml
3͍ํߨ࠲ ͜Ε͚ͩ
࠷ۙͷ New Features(ݸਓతʹྑ͍ͱࢥͬͨͭ) 2.1.0-alpha02 • NaviGraphͷείʔϓʹ߹ΘͤͨViewModel͕ੜՄೳʹ private val viewModel: SearchViewModel
by navGraphViewModels<SearchViewModel>(R.id.nav_search) ͜ͷείʔϓͰར༻͍ͨ͠ ViewModeΛએݴͰ͖Δ src/SearchFragment.kt
࠷ۙͷ New Features(ݸਓతʹྑ͍ͱࢥͬͨͭ) 2.1.0-alpha03ɹ<dialog />͕ར༻Մೳʹ <navigation> <fragment android:id="@+id/nav_fav" android:name="kobashin.com.navigation_sample.FavoriteFragment" android:label="FavoriteFragment"
tools:layout="@layout/fragment_favorite" > <action android:id="@+id/action_nav_fav_to_nav_modal" app:destination="@id/nav_modal" /> </fragment> <dialog android:id="@+id/nav_modal" android:name="kobashin.com.navigation_sample.BottomSheetFragment" android:label="BottomSheet" tools:layout="@layout/fragment_bottom_sheet"/> </navigation> BottomSheetDialogFragment DialogFragmentͳͷͰ ར༻Ͱ͖Δ navigation/navigation_graph.xml
Single-Activity or Multi-Activities? NavigationొޙɺͲ͏ͯ͠·͢ʁ
୲͍ͯ͠ΔΞϓϦʹΈࠐΉͱͨ͠Βʁ ͍· • Multi Activity • ࣌ʹFragment • Navigationܥ Libraryແ͍
• Single ActivityԽʁ • Navigationʁ Ͳ͏͢Δʁ
Ϣʔβʔͷಋઢ͔Βݕ౼͢Δ Top SearchTop Search ItemDetail
Ϣʔβʔͷಋઢ͔Βݕ౼͢Δ Top SearchTop Search ItemDetail Top TOP SearchTop TOP SearchTop
Search TOP SearchTop Search ItemDetail ͜͜ͷભҠΛؾ࣋ͪΑ͘ ͍ͨ͠ʂ
ActivityͱFragmentͷભҠͬͯ͘܁Γฦ͢ͱʁ • ActivityભҠɺFragmentભҠΛ܁Γฦ͢ • ڥ • Pixcel3 • OS ver
9 • UIదɻϘλϯ͚̍ͭͩɻ ݕূͯ͠ΈΔɻ
దͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ Activityฤ @Test fun mainActivityTest() { for (x in 0..1000)
{ val appCompatButton = onView( allOf( withId(R.id.button), withText("button"), childAtPosition( childAtPosition( withId(android.R.id.content), 0 ), 1 ), isDisplayed() ) ) appCompatButton.perform(click()) } } class SearchActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_search) button.setOnClickListener { startActivity(Intent(this, ItemDetailActivity::class.java)) } } } src/SearchActivity.kt androidTest/MainActivityTest.kt
దͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ Activityฤ ։࢝ޙɺ͍͍ͩͨ 300msఔͰը໘ભҠͯ͠ ͍Δ ऴྃલɺ͍͍ͩͨ1200ms ఔͰը໘ભҠ͍ͯ͠Δ ϝϞϦ༻ྔ͕1Gఔ·Ͱ ૿ՃˠGCΛ܁Γฦ͢ ։࢝ޙ
ऴྃલ
దͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ Fragmentฤ class SearchFragment : Fragment() { override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_search, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) button.setOnClickListener { findNavController() .navigate(R.id.action_searchFragment_to_itemDetailFragment) } } } ※ςετίʔυ΄΅ಉ͡ src/SearchFragment.kt
దͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ ը໘ભҠͷಈ͖͕͏·͘औΕ͍ͯͳ͍͕ɺςετ࣮ ߦ࣌ؒ5minऑʢActivity൛15minڧʣ ϝϞϦ༻ྔGCΛ܁Γฦ͠ͳ͕Βඍ૿ɻ ࠷ऴతʹ100Mఔ·Ͱ૿Ճ Fragmentฤ
ભҠ͚ͩʹண͢ΕFragmentͷํ͕Αͦ͞͏ʁ • ҙɿը૾ஔ͍ͯ͠ͳ͍దͳςετέʔεͰ͋Δ • ActivityΛੜ͢Δίετ܁Γฦ͢ʹ૿͍ͯ͘͠ • FragmentΛ༻͍ͨભҠͷύλʔϯίετখͦ͞͏ͩ • FragmentBackStackʹੵΈଓ͚ΔҝɺϦιʔεΛ อ࣋͢ΔύλʔϯͩͱϝϞϦޮѱԽ͍ͯ͘͠ʁ
• FragmentΛͬͨύλʔϯϝϞϦཧ͕͍͠ɻɻ
݁ہΔͳΒͲ͏͢Δஅͳͷʁ • Ϣʔβʔͷಈ͘ଠ͍ಋઢ෦FragmentͰભҠ ͦͷଞActivityͰભҠ͢ΔΞʔΩςΫνϟʹͨ͠ • ࣌ؒ܁Γฦ͠ར༻ͯ͠ཉ͍͠ը໘ؒͰͷભҠΛ FragmentͰͷભҠͱ͢Δ͜ͱͰUXΛྑ͔ͨͬͨ͘͠ ݕ౼՝ɿ ɹϦϦʔε࣌ʹεϖοΫͰͷଞOS VerͰͷݕূ
BottomNavigationͱҰॹʹ͍͍ͨ ಋೖ͚ͩͳΒɺ؆୯ɻͻͶΔͱͲ͏ͳΔʁ
BottomNavigationͱҰॹʹ͏ʹʁ <menu xmlns:android="http://schemas.android.com/apk/res/ android"> <item android:id="@+id/nav_top" android:icon="@drawable/ic_baseline_home_24px" android:title="top" /> <item
android:id="@+id/nav_search" android:icon="@drawable/ic_baseline_search_24px" android:title="search" /> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/navigation_graph" app:startDestination="@id/nav_top"> <fragment android:id="@+id/nav_top" android:name="kobashin.com.navigation_sample.TopFragment" android:label="TopFragment" tools:layout="@layout/fragment_top" > <action android:id="@+id/action_topFragment_to_searchFragment" app:destination="@id/nav_search" /> <action android:id="@+id/action_topFragment_to_mypageFragment" app:destination="@id/mypageFragment" /> </fragment> ͜͜Λἧ͑Δ menu/item_id navigation/fragment_id Λἧ͑Δ menu/menu.xml navigation/navigation_graph.xml
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
// লུ val navController = findNavController(R.id.root_nav_host_fragment) setupActionBarWithNavController( navController, AppBarConfiguration( setOf( R.id.nav_top, R.id.nav_search, R.id.nav_fav, R.id.nav_my ) ) ) bottom_navigation.setupWithNavController(navController) navigation/fragment_id Λ͢ BottomNavigationͷ ॳظԽΛΕͳ͍ ActionBarͷॳظԽ࣌ʹ ભҠઌͷidsΛ͢ src/MainActivity.kt BottomNavigationͱҰॹʹ͏ʹʁ
BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠ • Top -> SearchͰλϒ͕ҠΔ • λϒҠಈͰλϒͷભҠ͕ඈͿ ՝ Γ͍ͨ͜ͱ •
֤λϒຖʹStackΛอ࣋ • λϒͷߦ͖དྷͰ෮ݩ͍ͨ͠
BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠
BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠ Activity FragmentManager Top༻ͷ NavHostFragment Search༻ͷ NavHostFragment Fav༻ͷ NavHostFragment MyPage༻ͷ
NavHostFragment navi_graph NavigationͰ NavHostFragmentͷ ChildFragmentManager Λ͏ͷͰ͜ΕΛ͚Δ
BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠ • BottomNavigationView. setOnNavigationItemSelectedListener ͰભҠઌͷNavHostFragmentΛࢦఆ͢Δ • Back੍ޚͰ֤λϒͷStack͕ແ͘ͳͬͨ ઌΛTopʹ͢ΔͨΊɺaddToBackStack() ͓ͯ͘͠ setOnNavigationItemSelectedListener
{ item -> val newlySelectedItemTag = graphIdToTagMap[item.itemId] fragmentManager.beginTransaction() .attach(selectedFragment) .setPrimaryNavigationFragment(selectedFragment) .apply { // Detach all other Fragments graphIdToTagMap.forEach { _, fragmentTagIter -> if (fragmentTagIter != newlySelectedItemTag) { detach( fragmentManager.findFragmentByTag(firstFragmentTag)!! ) } } } .addToBackStack(firstFragmentTag) .commit() } src/NavigationExt.kt λϒຖʹอ࣋͢Δ NavHostFragmentͷΓ ସ͑෦
BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠ https://github.com/googlesamples/android-architecture-components/blob/master/NavigationAdvancedSample/app/src/main/ java/com/example/android/navigationadvancedsample/NavigationExtensions.kt • googlesamples/android-architecture-components ʹࢀߟʹͳΔαϯϓϧ͕ެ։͞Ε͍ͯΔ • BackΩʔؔ࿈ͰखΛೖΕ͔ͨͬͨͷͰɺಈతʹੜ͢Δ NavHostFragmentΛ֦ு͠ɺBackΩʔ੍ޚΛݕ౼த
ͦͷଞ Multi-ModuleͰ͍͍ͨ
Fragmentͷ໊લղܾͷλΠϛϯάΛ୳Δ Activityͷىಈ LayoutͷಡΈࠐΈ NavHostFragment ͷॳظԽ NavHostController ͷॳظԽ Graphͷੜ app:startDestination Λىಈ
https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/navigation/navigation-fragment/src/main/java/androidx/ navigation/fragment/NavHostFragment.java#204
findNavController(). navigate() Destination ͷղܾ className͔Β FragmentΛੜ ભҠͱBackStack ͷίϯτϩʔϧ https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/navigation/navigation-fragment/src/main/java/androidx/ navigation/fragment/FragmentNavigator.java#151
໊લղܾͷλΠϛϯάΛ୳Δ
໊લղܾͷλΠϛϯάΛ୳Δ • Build࣌Ͱͳ͘ɺ࣮ߦ࣌ʹ໊લղܾΛߦ͍ͬͯΔ ͭ·ΓɺBuild࣌Ͱ͘ͳ͍ͬͯͯେৎʂ ֆ͕ग़ͳ͍ͷ͕ͪΐͬͱऐ͍͚͠ΕͲ <?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nav_search" app:startDestination="@id/searchFragment"> <fragment android:id="@+id/searchFragment" android:name=“jp.co.yahoo.android.xxx.xxx.SearchTopFragment" android:label="SearchFragment"> <action android:id="@+id/action_contents_search_to_search_result" app:destination="@id/searchResultFragment"/> </fragment> ࣮ߦ࣌ʹղܾͰ͖Δ FragmentΛࢦఆ͢Δ navigation/navigation_graph.xml
ෳͷϞδϡʔϧ͔ΒݺΕΔύλʔϯʹରԠ͢Δ
ෳͷϞδϡʔϧ͔ΒݺΕΔύλʔϯʹରԠ͢Δ • Կߟ͑ͣʹͬͯ͠·͏ͱʁ →ݩͷgraph͕͔Ε͍ͯΔͱɺFragmentͰݺͼݩΛ ɹߟྀͨ͠Action IdΛࢦఆ͢Δඞཁ͕Ͱ͖ͯͯ͠·͏ɻ Top Search ItemDetail ItemDetail
R.id.xxx R.id.yyy navigation_top.xml navigation_search.xml
ෳͷϞδϡʔϧ͔ΒݺΕΔύλʔϯʹରԠ͢Δ • Nested GraphΛ༻͍ΕGraphͷڞ௨Խ͕Ͱ͖Δ ສࣄղܾʂ
ͦͷଞ DialogͰNavigation͕͍ͨ͠ ͋·Γ6*తʹྑ͘ͳͦ͞͏
DialogͰNavigation͕͍ͨ͠ • ͋·ΓDialogͰը໘ભҠ͢Δͷྑ͘ͳ͍(ͱࢥ͏) • ͰͲ͏͍ͯͨ͠͠Μͩʂ ઌͷྫʹ͋ΔΑ͏ʹɺ ಈతʹNavHostFragmentΛ͍͚ͭͬͯ͘Δʂ
DialogͰNavigation͕͍ͨ͠ class SearchModalFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View,
savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val fragment = NavHostFragment.create(R.navigation.modal_navi_graph) childFragmentManager.beginTransaction() .replace(R.id.container_nav_host, fragment) .commit() } • Activityʹஔ͘Α͏ʹɺ <fragment /> Λॻ͘ͱ࠶ੜ࣌ʹΤϥʔʹͳΔ • ಈతʹੜ͢Εे͑Δ ίϯετϥΫλ͕ੜ͑ͯΔͷ ͰͦΕΛ͏ src/SearchModalFragment.kt
ॴײ
ॴײ • طʹ୯७ͳͱ͜ΖͰϋϚΔ͜ͱ΄΅ແ͍ • ͪΐͬͱҳͨ͜͠ͱΛΓͨ͘ͳΔͱɺ ίʔυΛಡ·ͳ͍ͱΠέͳ͍ͷ͍ͭͷAndroid͞Μ • όʔδϣϯΞοϓͰΰϦΰϦมΘ͍ͬͯ͘ͷͰɺ ࠾༻͢Δόʔδϣϯཁݕ౼ɻྗ͕ඞཁͩ
͜Μͳ͜ͱͰ͖ΔΜ͡Όͳ͍͔ͳʁ • Ұ෦ػೳʹݶఆͨ͠֎෦Intentެ։ • ݺͼग़͠༻ͷActivityΛ༻ҙɺಡΈࠐΉgraphΛม͑Εʁ • ݱࡏઃఆ͍ͯ͠ΔgraphΛΩʔʹͨ͠Navigatorʁ • ෳͷgraph͔Βݺͼग़͞ΕΔFragmentͰॊೈੑ֬อ ·ͨௐ͓ͯ͠·͢Ͷʂ
☺
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ