Navigation Architecture Component 入門 / shibuya.apk #30

80a3a3857a55f154d23acb705eff72cc?s=47 star_zero
December 13, 2018

Navigation Architecture Component 入門 / shibuya.apk #30

80a3a3857a55f154d23acb705eff72cc?s=128

star_zero

December 13, 2018
Tweet

Transcript

  1. Navigation Architecture Component 入門 shibuya.apk #30

  2. About Me • Kenji Abe • Cookpad Inc. • Android

    Developer • Twitter: @STAR_ZERO
  3. Navigation Architecture Component

  4. ⚠注意⚠ まだ alpha08

  5. Navigation Architecture Component • XMLで画面遷移を定義できる • Android StudioでGUIが使える • 引数も定義できる

    (SafeArgs) • DeepLinkできる • Shared Element Transitionsできる • カスタマイズできる
  6. Navigation Architecture Component • 基本は Fragment -> Fragment • Fragment

    -> Activityはできる • Activity -> Activityは簡単にはできない
 (できないことはない)
  7. 基本的な使い方

  8. build.gradle // build.gradle buildscript { dependencies { classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0- alpha08"

    } } // app/build.gradle apply plugin: "androidx.navigation.safeargs" dependencies { implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha08" implementation "android.arch.navigation:navigation-ui-ktx:1.0.0-alpha08" }
  9. Navigation Graph (Navigation Editor) res/navigation/nav_graph.xml

  10. Navigation Graph (XML) <?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_graph"

    app:startDestination="@id/firstFragment"> <fragment android:id="@+id/firstFragment" android:name="com.star_zero.samplenavigation.FirstFragment" android:label="FirstFragment"> <action android:id="@+id/action_firstFragment_to_secondFragment" app:destination="@id/secondFragment" /> </fragment> <fragment android:id="@+id/secondFragment" android:name="com.star_zero.samplenavigation.SecondFragment" android:label="SecondFragment"> <action android:id="@+id/action_secondFragment_to_thirdFragment" app:destination="@id/thirdFragment" /> </fragment> <fragment android:id="@+id/thirdFragment" android:name="com.star_zero.samplenavigation.ThirdFragment" android:label="ThirdFragment" /> </navigation>
  11. Navigation Graph (XML) <?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_graph"

    app:startDestination="@id/firstFragment"> <fragment android:id="@+id/firstFragment" android:name="com.star_zero.samplenavigation.FirstFragment" android:label="FirstFragment"> <action android:id="@+id/action_firstFragment_to_secondFragment" app:destination="@id/secondFragment" /> </fragment> </navigation> Fragmentの定義
  12. Navigation Graph (XML) <?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_graph"

    app:startDestination="@id/firstFragment"> <fragment android:id="@+id/firstFragment" android:name="com.star_zero.samplenavigation.FirstFragment" android:label="FirstFragment"> <action android:id="@+id/action_firstFragment_to_secondFragment" app:destination="@id/secondFragment" /> </fragment> </navigation> 最初に表示されるFragment
  13. Navigation Graph (XML) <?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_graph"

    app:startDestination="@id/firstFragment"> <fragment android:id="@+id/firstFragment" android:name="com.star_zero.samplenavigation.FirstFragment" android:label="FirstFragment"> <action android:id="@+id/action_firstFragment_to_secondFragment" app:destination="@id/secondFragment" /> </fragment> </navigation> 画面遷移の定義
  14. Layout <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"

    app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph" />
  15. Layout <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"

    app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph" /> Navigtion用のFragment BackButtonの制御 画面遷移を定義したXML
  16. Code fun navigate() { val navController = findNavController() navController .navigate(R.id.action_firstFragment_to_secondFragment)

    } NavController取得 <action android:id="@+id/action_firstFragment_to_secondFragment" app:destination="@id/secondFragment" /> Nav Graphに設定した <action>のid
  17. 引数

  18. Navigation Graph (Navigation Editor)

  19. <fragment android:id="@+id/secondFragment" android:name="com.star_zero.samplenavigation.SecondFragment" android:label="SecondFragment"> <action android:id="@+id/action_secondFragment_to_thirdFragment" app:destination="@id/thirdFragment" /> <argument android:name="user_id"

    app:argType="integer" /> <argument android:name="user_name" android:defaultValue="ABC" app:argType="string" /> </fragment> Navigation Graph (XML)
  20. 引数の型いろいろ <!-- boolean --> <argument android:name="flag" android:defaultValue="false" app:argType="boolean" /> <!--

    int array --> <argument android:name="integers" app:argType="integer[]" /> <!-- enum --> <argument android:name="type" android:defaultValue="Hoge" app:argType="com.star_zero.samplenavigation.Type" />
  21. <!-- Parcelable --> <argument android:name="user" app:argType="com.star_zero.samplenavigation.User" app:nullable="true" /> <!-- Parcelable

    array --> <argument android:name="users" app:argType="com.star_zero.samplenavigation.User[]" /> 引数の型いろいろ https://android.googlesource.com/platform/frameworks/support/+/ 6f59e3d74d0da5a4bf79c8480f56964e3af47f1c/navigation/safe-args-generator/src/main/kotlin/ androidx/navigation/safe/args/generator/Types.kt
  22. Code (ワタス側) val userId = getUserId() val userName = getUserName()

    val action = FirstFragmentDirections .actionFirstFragmentToSecondFragment(userId) action.setUserName(userName) findNavController().navigate(action)
  23. Code (ワタス側) val userId = getUserId() val userName = getUserName()

    val action = FirstFragmentDirections .actionFirstFragmentToSecondFragment(userId) action.setUserName(userName) findNavController().navigate(action) ワタス側のFragmentクラス名 + Directions
  24. Code (ワタス側) val userId = getUserId() val userName = getUserName()

    val action = FirstFragmentDirections .actionFirstFragmentToSecondFragment(userId) action.setUserName(userName) findNavController().navigate(action) <action>のidから生成されたメソッド これに引数をワタス(default値がないもののみ)
  25. Code (ワタス側) val userId = getUserId() val userName = getUserName()

    val action = FirstFragmentDirections .actionFirstFragmentToSecondFragment(userId) action.setUserName(userName) findNavController().navigate(action) default値が設定されてるものは setterにワタス
  26. Code (ワタス側) val userId = getUserId() val userName = getUserName()

    val action = FirstFragmentDirections .actionFirstFragmentToSecondFragment(userId) action.setUserName(userName) findNavController().navigate(action) 最後にnavigateメソッドにワタス
  27. Code (モラウ側) val args = SecondFragmentArgs.fromBundle(arguments) // Activityの場合 val args

    = SecondActivityArgs.fromBundle(intent.extras) Timber.d("user_id: ${args.userId}") Timber.d("user_name: ${args.userName}") fromBundleで引数を取得
  28. Deep Link

  29. Navigation Graph (Navigation Editor)

  30. Navigation Graph (XML) <fragment android:id="@+id/deepLinkFragment" android:name="com.star_zero.samplenavigation.DeepLinkFragment" android:label="DeepLinkFragment"> <deepLink app:uri="star-zero.com/{id}" />

    </fragment> スキーマ省略時はhttpとhttpsに対応 {id}はプレースホルダー パラメーターとして取得可能
  31. AndroidManifest.xml <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />

    </intent-filter> <nav-graph android:value="@navigation/nav_graph" /> </activity>
  32. Code (パラメーターをモラウ) val args = arguments?.getString("id") Timber.d("args: ${args}")

  33. NavigationUI

  34. ActionBar

  35. None
  36. ActionBar class MainActivity : AppCompatActivity() { private lateinit var appBarConfiguration:

    AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navController = findNavController(R.id.nav_host_fragment) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) } override fun onSupportNavigateUp(): Boolean { return findNavController(R.id.nav_host_fragment) .navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } }
  37. ActionBar class MainActivity : AppCompatActivity() { private lateinit var appBarConfiguration:

    AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navController = findNavController(R.id.nav_host_fragment) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) } override fun onSupportNavigateUp(): Boolean { return findNavController(R.id.nav_host_fragment) .navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } } AppBarConfigurationを使って NavGraphとActionBarを連携
  38. ActionBar class MainActivity : AppCompatActivity() { private lateinit var appBarConfiguration:

    AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navController = findNavController(R.id.nav_host_fragment) appBarConfiguration = AppBarConfiguration(navController.graph) setupActionBarWithNavController(navController, appBarConfiguration) } override fun onSupportNavigateUp(): Boolean { return findNavController(R.id.nav_host_fragment) .navigateUp(appBarConfiguration) || super.onSupportNavigateUp() } } 左上の矢印ボタンを押したとき
  39. Toolbar

  40. Toolbar class MainActivity : AppCompatActivity() { private lateinit var binding:

    ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil .setContentView(this, R.layout.activity_main) val navController = findNavController(R.id.nav_host_fragment) val appBarConfiguration = AppBarConfiguration(navController.graph) binding.toolbar .setupWithNavController(navController, appBarConfiguration) } }
  41. Toolbar class MainActivity : AppCompatActivity() { private lateinit var binding:

    ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil .setContentView(this, R.layout.activity_main) val navController = findNavController(R.id.nav_host_fragment) val appBarConfiguration = AppBarConfiguration(navController.graph) binding.toolbar .setupWithNavController(navController, appBarConfiguration) } } ActionBarの時と同様 onSupportNavigateUpは設定しなくて良い
  42. Menu Item

  43. None
  44. Navigation Graph - Menu Item <fragment android:id="@+id/menuFragment" android:name="com.star_zero.samplenavigation.MenuFragment" android:label="MenuFragment" />

    <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menuFragment" android:icon="@drawable/ic_star" android:title="@string/menu" app:showAsAction="ifRoom" /> </menu>
  45. Navigation Graph - Menu Item <fragment android:id="@+id/menuFragment" android:name="com.star_zero.samplenavigation.MenuFragment" android:label="MenuFragment" />

    <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/menuFragment" android:icon="@drawable/ic_star" android:title="@string/menu" app:showAsAction="ifRoom" /> </menu> idを一致させる
  46. Code override fun onOptionsItemSelected(item: MenuItem): Boolean { val navController =

    findNavController() return item.onNavDestinationSelected(navController) || super.onOptionsItemSelected(item) }
  47. Bottom Navigation

  48. Navigation Graph <fragment android:id="@+id/menu_home" android:name="com.star_zero.samplenavigation.HomeFragment" android:label="HomeFragment" /> <fragment android:id="@+id/menu_favorite" android:name="com.star_zero.samplenavigation.FavoriteFragment"

    android:label="FavoriteFragment" /> <fragment android:id="@+id/menu_search" android:name="com.star_zero.samplenavigation.SearchFragment" android:label="SearchFragment" />
  49. bottom_nav.xml <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_home" android:icon="@drawable/ic_home" android:title="@string/menu_home" /> <item android:id="@+id/menu_favorite"

    android:icon="@drawable/ic_favorite" android:title="@string/menu_favorite" /> <item android:id="@+id/menu_search" android:icon="@drawable/ic_search" android:title="@string/menu_search" /> </menu>
  50. Navigation Graph - bottom_nav.xml <fragment android:id="@+id/menu_home" android:name="com.star_zero.samplenavigation.HomeFragment" android:label="HomeFragment" /> <!--

    ... --> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_home" android:icon="@drawable/ic_home" android:title="@string/menu_home" /> <!-- ... --> </menu> idを一致させる
  51. Code class MainActivity : AppCompatActivity() { private lateinit var binding:

    ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil .setContentView(this, R.layout.activity_main) val navController = findNavController(R.id.nav_host_fragment) binding.bottomNav.setupWithNavController(navController) } }
  52. Shared Element Transitions https://github.com/STAR-ZERO/navigation-shared-element

  53. Activity val options = ActivityOptionsCompat.makeSceneTransitionAnimation( requireActivity(), view, "image" ) val

    extras = ActivityNavigator.Extras.Builder() .setActivityOptions(options) .build() findNavController().navigate( R.id.action_hoge_to_foo, null, null, extras )
  54. Fragment val extra = FragmentNavigatorExtras(view to "image") findNavController().navigate( R.id.action_gridFragment_to_imageFragment, null,

    null, extra )
  55. カスタマイズ https://github.com/STAR-ZERO/navigation-keep-fragment-sample

  56. Navigator @Navigator.Name("keep_state_fragment") class KeepStateNavigator( private val context: Context, private val

    manager: FragmentManager, private val containerId: Int ) : FragmentNavigator(context, manager, containerId) { override fun navigate( destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras? ): NavDestination? { // ... } }
  57. Navigation Graph <?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_graph" app:startDestination="@id/firstFragment">

    <keep_state_fragment android:id="@+id/firstFragment" android:name="com.star_zero.samplenavigation.FirstFragment" android:label="FirstFragment" /> <keep_state_fragment android:id="@+id/secondFragment" android:name="com.star_zero.samplenavigation.SecondFragment" android:label="SecondFragment" /> </keep_state_fragment> <keep_state_fragment android:id="@+id/thirdFragment" android:name="com.star_zero.samplenavigation.ThirdFragment" android:label="ThirdFragment" /> </navigation>
  58. Activity val navController = findNavController(R.id.nav_host_fragment) val navHostFragment = supportFragmentManager .findFragmentById(R.id.nav_host_fragment)!!

    val navigator = KeepStateNavigator( this, navHostFragment.childFragmentManager, R.id.nav_host_fragment ) navController.navigatorProvider += navigator navController.setGraph(R.navigation.nav_graph)
  59. まとめ

  60. まとめ • 単純な画面遷移は難しくない • SafeArgs便利 • ちょっと凝ったことする時はカスタマイズ必要 • ちょっとずつ導入可能 •

    Single Activity
  61. ありがとうございました