Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Navigationの採用を検討してみて、 色々考えた話を共有するよ
Search
kobashin
July 25, 2019
Programming
320
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
770
Systemアプリ開発入門
shinjikobayashi
6
14k
Other Decks in Programming
See All in Programming
wkhtmltopdfの次どうするか問題2026
willnet
2
1.8k
海上で動くGoサーバー: goroutineとchannelでさばく航行データストリーム
atsuki_seo
0
1k
FreeBSDでZabbixを動かす
kenkino
0
340
すこし踏み込む CancellationToken
htkym
2
1.5k
巨大モノリシックアプリ モダン化大作戦
ktcryomm
1
1.2k
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Rails World 2026, Austin, Texas
marcoroth
0
130
iOSDC Japan 2026 - Swiftで作って学ぼう!データベース自作入門
kaseken
2
400
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
170
Workers Cache を知る
syumai
0
310
一人だけ、Kiroが静止する日
hideg
0
140
速習iPhone Duo対応
yuukiw00w
2
910
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
390
Featured
See All Featured
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Product Roadmaps are Hard
iamctodd
55
13k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.6k
Design in an AI World
tapps
1
340
Balancing Empowerment & Direction
lara
6
1.3k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
エンジニアに許された特別な時間の終わり
watany
109
250k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
380
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
31k
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Ͱॊೈੑ֬อ ·ͨௐ͓ͯ͠·͢Ͷʂ
☺
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ