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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
380
APNsからLive Activityを開始する話
yumnumm
0
170
Event is what happens
moro
2
960
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
510
全ての永続トークンを、生まれる前に消し去りたい!
sylph01
2
1k
パスワード再設定メールのURLについて知っておこう
uyuki234
0
160
Workers Cache を知る
syumai
0
370
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
280
気持ちぃ〜角丸 Squircle
ryu1013
0
320
JIS keyboard support in Omarchy
lef237
0
140
作れる、でも消せるか?──安全に「枯らして消す」機能削除術 / Drain, Then Delete
chobishiba
2
820
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Kaigi on Rails 2026, Tokyo, Japan
marcoroth
2
300
Featured
See All Featured
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.9k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Chasing Engaging Ingredients in Design
codingconduct
0
360
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
570
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.4k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
930
Designing Experiences People Love
moore
143
24k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
500
Game over? The fight for quality and originality in the time of robots
wayneb77
1
310
Statistics for Hackers
jakevdp
799
230k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
570
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Ͱॊೈੑ֬อ ·ͨௐ͓ͯ͠·͢Ͷʂ
☺
͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ