Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Navigationの採用を検討してみて、 色々考えた話を共有するよ

Navigationの採用を検討してみて、 色々考えた話を共有するよ

Df866cf39671a846639b2d2011b1dca8?s=128

kobashin

July 25, 2019
Tweet

Transcript

  1. Mix Leap Study #47 Android x Flutterษڧձ Navigationͷ࠾༻Λݕ౼ͯ͠Έͯɺ ৭ʑߟ͑ͨ࿩Λڞ༗͢ΔΑ @kobashinG

    ʢ͜͹͠Μʣ 2019.7.25
  2. ࣗݾ঺հ ͜͹͠Μ (@kobashinG) Ϡϑʔגࣜձࣾ ϑϩϯτΤϯυશൠ, etc… Android, Linux, k8s, ˏϠϑʔγϣοϐϯά

  3. ΏΔΏΔͱ͓࿩͠·͢ ࠓ೔ͷAgenda • Android Jetpack Navigationͱ͸ʁ • SingleActivity or Multi-Activityʁ

    • BottomNavigationͱҰॹʹ࢖͍͍ͨ • ͦͷଞ (※࣌ؒ଍Γͳ͍৔߹͸ඈ͹͔͢΋)
  4. Android Jetpack Navigationͱ͸ʁ (ຊ೔͸ɺNavigation 2.1.0-beta02࣌఺ͷ࿩Λ͠·͢)

  5. Android Jetpack Navigationͱ͸ʁ

  6. Android Jetpack Navigationͱ͸ʁ • Navigation GraphΛ࢖ͬͨUIભҠͷ࣮૷ • UIͷભҠΛActivity/Fragment͔ΒҠৡՄೳ • ൥ࡶͳFragmentTransactionΛॻ͔ͳͯ͘ࡁΉ

    • Fragmentؒͷσʔλड͚౉͠Λܕ҆શʹʢSafeArgsʣ • DeepLink౳Λ؆қ࣮૷Մೳʹ
  7. 3෼࢖͍ํߨ࠲ Navigation Editor (·ͨ͸xml) Ͱը໘ભҠΛ࡞੒͢Δ

  8. 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
  9. 3෼࢖͍ํߨ࠲ ͜Ε͚ͩ

  10. ࠷ۙͷ New Features(ݸਓతʹྑ͍ͱࢥͬͨ΍ͭ) 2.1.0-alpha02 • NaviGraphͷείʔϓʹ߹ΘͤͨViewModel͕ੜ੒Մೳʹ private val viewModel: SearchViewModel

    by navGraphViewModels<SearchViewModel>(R.id.nav_search) ͜ͷείʔϓͰར༻͍ͨ͠ ViewModeΛએݴͰ͖Δ src/SearchFragment.kt
  11. ࠷ۙͷ 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
  12. Single-Activity or Multi-Activities? Navigationొ৔ޙɺͲ͏ͯ͠·͢ʁ

  13. ୲౰͍ͯ͠ΔΞϓϦʹ૊ΈࠐΉͱͨ͠Βʁ ͍· • Multi Activity • ࣌ʹFragment • Navigationܥ
 Library͸ແ͍

    • Single ActivityԽʁ • Navigationʁ Ͳ͏͢Δʁ
  14. Ϣʔβʔͷಋઢ͔Βݕ౼͢Δ Top SearchTop Search ItemDetail

  15. Ϣʔβʔͷಋઢ͔Βݕ౼͢Δ Top SearchTop Search ItemDetail Top TOP SearchTop TOP SearchTop

    Search TOP SearchTop Search ItemDetail ͜͜ͷભҠΛؾ࣋ͪΑ͘ ͍ͨ͠ʂ
  16. ActivityͱFragmentͷભҠͬͯ௕͘܁Γฦ͢ͱʁ • ActivityભҠɺFragmentભҠΛ܁Γฦ͢ • ؀ڥ • Pixcel3 • OS ver

    9 • UI͸௒ద౰ɻϘλϯ͚̍ͭͩɻ ݕূͯ͠ΈΔɻ
  17. ద౰ͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ 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
  18. ద౰ͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ Activityฤ ։࢝௚ޙ͸ɺ͍͍ͩͨ 300msఔ౓Ͱը໘ભҠͯ͠ ͍Δ ऴྃ௚લ͸ɺ͍͍ͩͨ1200ms ఔ౓Ͱը໘ભҠ͍ͯ͠Δ ϝϞϦ࢖༻ྔ͕1Gఔ౓·Ͱ ૿ՃˠGCΛ܁Γฦ͢ ։࢝௚ޙ

    ऴྃ௚લ
  19. ద౰ͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ 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
  20. ద౰ͳΞϓϦͰը໘ભҠΛ܁Γฦͯ͠ΈΔ ը໘ભҠͷಈ͖͕͏·͘औΕ͍ͯͳ͍͕ɺςετ࣮ ߦ࣌ؒ͸5minऑʢActivity൛͸15minڧʣ ϝϞϦ࢖༻ྔ͸GCΛ܁Γฦ͠ͳ͕Βඍ૿ɻ ࠷ऴతʹ100Mఔ౓·Ͱ૿Ճ Fragmentฤ

  21. ભҠ͚ͩʹண໨͢Ε͹Fragmentͷํ͕Αͦ͞͏ʁ • ஫ҙɿը૾΋഑ஔ͍ͯ͠ͳ͍ద౰ͳςετέʔεͰ͋Δ • ActivityΛੜ੒͢Δίετ͸܁Γฦ͢౓ʹ૿͍ͯ͘͠ • FragmentΛ༻͍ͨભҠͷύλʔϯ͸ίετ͸খͦ͞͏ͩ • Fragment΋BackStackʹੵΈଓ͚ΔҝɺϦιʔεΛ
 อ࣋͢ΔύλʔϯͩͱϝϞϦޮ཰͸ѱԽ͍ͯ͘͠ʁ

    • FragmentΛ࢖ͬͨύλʔϯ͸ϝϞϦ؅ཧ͕೉͍͠ɻɻ
  22. ݁ہ΍ΔͳΒͲ͏͢Δ൑அͳͷʁ • Ϣʔβʔͷಈ͘ଠ͍ಋઢ෦෼͸FragmentͰભҠ
 ͦͷଞ͸ActivityͰભҠ͢ΔΞʔΩςΫνϟʹͨ͠ • ௕࣌ؒ܁Γฦ͠ར༻ͯ͠ཉ͍͠ը໘ؒͰͷભҠΛ
 FragmentͰͷભҠͱ͢Δ͜ͱͰUXΛྑ͔ͨͬͨ͘͠ ݕ౼՝୊ɿ ɹϦϦʔε࣌ʹ͸௿εϖοΫͰͷ୺຤΍ଞOS VerͰͷݕূ

  23. BottomNavigationͱҰॹʹ࢖͍͍ͨ ಋೖ͚ͩͳΒɺ؆୯ɻͻͶΔͱͲ͏ͳΔʁ

  24. 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
  25. 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ͱҰॹʹ࢖͏ʹ͸ʁ
  26. BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠ • Top -> SearchͰλϒ͕ҠΔ • λϒҠಈͰλϒ಺ͷભҠ͕ඈͿ ՝୊ ΍Γ͍ͨ͜ͱ •

    ֤λϒຖʹStackΛอ࣋ • λϒͷߦ͖དྷͰ෮ݩ͍ͨ͠
  27. BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠

  28. BottomNavigationͷλϒຖʹBackStackΛอ͍࣋ͨ͠ Activity FragmentManager Top༻ͷ NavHostFragment Search༻ͷ NavHostFragment Fav༻ͷ NavHostFragment MyPage༻ͷ

    NavHostFragment navi_graph NavigationͰ͸ NavHostFragmentͷ ChildFragmentManager Λ࢖͏ͷͰ͜ΕΛ෼͚Δ
  29. 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ͷ੾Γ ସ͑෦෼
  30. 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Ωʔ੍ޚΛݕ౼த

  31. ͦͷଞ Multi-ModuleͰ΋࢖͍͍ͨ

  32. 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
  33. 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

    ໊લղܾͷλΠϛϯάΛ୳Δ
  34. ໊લղܾͷλΠϛϯάΛ୳Δ • 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
  35. ෳ਺ͷϞδϡʔϧ͔Βݺ͹ΕΔύλʔϯʹରԠ͢Δ

  36. ෳ਺ͷϞδϡʔϧ͔Βݺ͹ΕΔύλʔϯʹରԠ͢Δ • Կ΋ߟ͑ͣʹ΍ͬͯ͠·͏ͱʁ
 →ݩͷgraph͕෼͔Ε͍ͯΔͱɺFragment಺ͰݺͼݩΛ
 ɹߟྀͨ͠Action IdΛࢦఆ͢Δඞཁ͕Ͱ͖ͯͯ͠·͏ɻ Top Search ItemDetail ItemDetail

    R.id.xxx R.id.yyy navigation_top.xml navigation_search.xml
  37. ෳ਺ͷϞδϡʔϧ͔Βݺ͹ΕΔύλʔϯʹରԠ͢Δ • Nested GraphΛ༻͍Ε͹Graphͷڞ௨Խ͕Ͱ͖Δ
 ສࣄղܾʂ

  38. ͦͷଞ Dialog಺Ͱ΋Navigation͕͍ͨ͠ ͋·Γ6*తʹྑ͘ͳͦ͞͏

  39. Dialog಺Ͱ΋Navigation͕͍ͨ͠ • ͋·ΓDialog಺Ͱը໘ભҠ͢Δͷ͸ྑ͘ͳ͍(ͱࢥ͏) • Ͱ΋Ͳ͏ͯ͠΋͍ͨ͠Μͩʂ ઌͷྫʹ΋͋ΔΑ͏ʹɺ ಈతʹNavHostFragmentΛ͍͚ͭͬͯ͘Δʂ

  40. 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
  41. ॴײ

  42. ॴײ • طʹ୯७ͳͱ͜ΖͰϋϚΔ͜ͱ͸΄΅ແ͍ • ͪΐͬͱҳ୤ͨ͜͠ͱΛ΍Γͨ͘ͳΔͱɺ
 ίʔυΛಡ·ͳ͍ͱΠέͳ͍ͷ͸͍ͭ΋ͷAndroid͞Μ • όʔδϣϯΞοϓͰΰϦΰϦมΘ͍ͬͯ͘ͷͰɺ
 ࠾༻͢Δόʔδϣϯ͸ཁݕ౼ɻ࿹ྗ͕ඞཁͩ

  43. ͜Μͳ͜ͱͰ͖ΔΜ͡Όͳ͍͔ͳʁ • Ұ෦ػೳʹݶఆͨ͠֎෦Intentެ։ • ݺͼग़͠༻ͷActivityΛ༻ҙɺಡΈࠐΉgraphΛม͑Ε͹ʁ • ݱࡏઃఆ͍ͯ͠ΔgraphΛΩʔʹͨ͠Navigatorʁ • ෳ਺ͷgraph͔Βݺͼग़͞ΕΔFragmentͰ΋ॊೈੑ֬อ ·ͨௐ΂͓ͯ࿩͠·͢Ͷʂ

  44. ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ