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
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
270
Android CLI
fornewid
0
190
Go言語とトイモデルで学ぶTransformerの気持ち / fukuokago23-transformer
monochromegane
0
150
Build-to-own AI: Agentic Development for Humans
inesmontani
PRO
0
130
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
560
Apache Hive: そしてCloud Native Lakehouseへ
okumin
1
190
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
3.2k
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
270
メールのエイリアス機能を履き違えない
isshinfunada
0
150
使用 Meilisearch 建立新聞搜尋工具
johnroyer
0
190
Lean は証明の正しさを確認するためだけのツールって思ってませんか?
inoueasei
1
120
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
1
260
Featured
See All Featured
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.8k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.2k
The World Runs on Bad Software
bkeepers
PRO
72
12k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
270
Believing is Seeing
oripsolob
1
170
Test your architecture with Archunit
thirion
1
2.3k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
460
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Writing Fast Ruby
sferik
630
63k
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
270
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Ͱॊೈੑ֬อ ·ͨௐ͓ͯ͠·͢Ͷʂ
☺
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ