Jetpack Android Navigation Component 2.0 by Laurence de Villers

1b77dd441f657f5aefb3e21283b252e6?s=47 GDG Montreal
September 29, 2019

Jetpack Android Navigation Component 2.0 by Laurence de Villers

This talk will describe what is new in Android Navigation Component, how easy it’s to navigate between fragment et the pros and cons of using the Navigation Component.

https://devfest2019.gdgmontreal.com/schedule/2019-09-29?sessionId=100

1b77dd441f657f5aefb3e21283b252e6?s=128

GDG Montreal

September 29, 2019
Tweet

Transcript

  1. The Navigation Component 2.0 Laurence de Villers @L_de_V

  2. Laurence de Villers Mobile dev at Transit Lead organizer of

    GDG Montreal Women Techmakers Lead Canada Regional Mentor @L_de_V
  3. None
  4. Navigation Component • Simplified setup for commun navigation patterns ◦

    Handles backstack ◦ Automates fragment transaction • Type safe argument passing • Handles transition animations • Simplified deep link
  5. Simplified setup for commun navigation patterns

  6. Challenges of standard navigation: FragmentManager FragmentManager Fragment FragmentTransaction Activity Fragments

    Back Stack
  7. Challenges of standard navigation: Pop Inclusive Home Category Item Size

    • Need to add dynamically code to pop Inclusive some screen with the fragmentManager Remove fragments
  8. Principal of view navigation • Up button vs Back button

    ◦ You need to make sure that both work together and follow Android Developer Guide override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { android.R.id.home -> { onBackPressed() //or finish() true } else -> super.onOptionsItemSelected(item) } } Or @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; }
  9. Up button • The Up button is used to navigate

    with an app based on the hierarchical relationship between screen. Back button • The system Back button is used to navigate, in reverse chronological order, through the history of screens the user has recently worked with. VS
  10. First principle of Navigation There’s Always a starting point

  11. None
  12. None
  13. Navigation component take care of BackStack Fragmentmanager DeepLink

  14. How to get started?

  15. Gradle dependencies { def nav_version = "2.1.0" // Java implementation

    "androidx.navigation:navigation-fragment:$nav_version" implementation "androidx.navigation:navigation-ui:$nav_version" // Kotlin implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" }
  16. Add the navigation graph New → Android resource file

  17. Create your graph By default, android studio will find all

    the fragment that you have in your application
  18. Nested Graph If you have too much screen you can

    separate graph into groups Move to Nested Graph > New Graph
  19. Add a Navigation Host Fragment in your main activity <LinearLayout

    android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> ... <fragment android:id="@+id/my_nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:navGraph="@navigation/navigation_graph" /> </LinearLayout> You need to specify if this navHost is the default. You can have multiple NavHost is you have more than one activity
  20. How it works ? A NavHostFragment swaps different destinations in

    and out as you navigate through the navigation graph.
  21. Navigate between fragments NavController is a special class that triggers

    the fragment swaps in the NavHostFragment You can call methods like navigate() or popBackStack() The NavController will translates these commands into the appropriate framework operations based on the type of destination you are navigating to or from If you call navigate() with an activity destination, the NavController calls startActivity() on your behalf
  22. Navigate between fragments talkViewHolder.itemView.setOnClickListener(v -> { Navigation.findNavController(v).navigate(R.id.action_nav_program_to_talkFragment); } You need

    to use the same id that was generated in your action on the graph
  23. Up button • You need to setup the action bar

    with the NavController Back button • The back button is handle by the navigation graph automatically VS
  24. Setup the up button navigation private fun setupNavigation() { val

    host: NavHostFragment = supportFragmentManager .findFragmentById(R.id.mainNavigationFragment) as NavHostFragment? ?: return val navController = host.navController val topLevelDestinations = HashSet<Int>() topLevelDestinations.add(R.id.nav_program) ... topLevelDestinations.add(R.id.nav_twitter) appBarConfiguration = AppBarConfiguration.Builder(topLevelDestinations) .setDrawerLayout(drawer_layout).build() setupActionBarWithNavController(this, navController, appBarConfiguration) }
  25. Navigate using drawers and navigation view <com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent"

    android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" /> On the activity_main.xml
  26. Auto setup NavigationView with the navigation graph Use the same

    ID in your NavigationView menu and on your graph. <menu xmlns:android="..."> <group android:checkableBehavior="single"> <item android:id="@+id/nav_program" android:icon="@drawable/ic_event_black_24dp" android:menuCategory="container" android:title="@string/program" /> <item android:id="@+id/nav_speakers" android:icon="@drawable/ic_group_black_24px" android:menuCategory="container" android:title="@string/speakers" /> ... </menu> <navigation xmlns:android="..." <fragment android:id="@+id/nav_program" android:name="com.montreal.wtm.ui.fragment.ProgramFragment" android:label="@string/program" tools:layout="@layout/program_fragment" > </fragment> <fragment android:id="@+id/nav_speakers" android:name="com.montreal.wtm.ui.fragment.SpeakersFragment" android:label="@string/speakers" tools:layout="@layout/simple_recycler_list" > </fragment> </navigation>
  27. Code that is not needed anymore override fun onNavigationItemSelected(item: MenuItem):

    Boolean { val id = item.itemId var fragment: Fragment when(id) { R.id.nav_program -> { fragment = ProgramFragment.newInstance() setActionBarName(getString(R.string.program)) } ... R.id.nav_twitter -> { fragment = TwitterFragment.newInstance() setActionBarName(getString(R.string.twitter)) } else -> { fragment = ProgramFragment.newInstance() setActionBarName(getString(R.string.program)) } } val drawer = findViewById<View>(R.id.drawer_layout) as DrawerLayout drawer.closeDrawer(GravityCompat.START) FragmentManager fragmentManager = activity.getFragmentManager(); fragmentManager.beginTransaction().replace(R.id.container, fragment).commit(); return true } val toggle = ActionBarDrawerToggle(this, drawer, toolbar, string.navigation_drawer_open, string.navigation_drawer_close) drawer.addDrawerListener(toggle) toggle.syncState()
  28. Navigate using drawers and navigation view private fun setupNavigationView() {

    val host: NavHostFragment = supportFragmentManager .findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return val navController = host.navController NavigationUI.setupWithNavController(findViewById<NavigationView>(R.id.nav_view), navController) } On the MainActivity.kt override fun onSupportNavigateUp(): Boolean { // Allows NavigationUI to support proper up navigation or the drawer layout // drawer menu, depending on the situation return NavigationUI.navigateUp(findNavController(this, R.id.mainNavigationFragment), appBarConfiguration) }
  29. Wait, how to pass an argument with the new Navigation

    Component?
  30. Passing Bundle talkViewHolder.itemView.setOnClickListener(v -> { Bundle bundle = new Bundle();

    bundle.putParcelable(EXTRA_TALK, talk); Navigation.findNavController(v).navigate(R.id.action_nav_program_to_talkFragment, bundle); }
  31. Type Safe argument passing

  32. Safe Args Safe Arg is a plugin that generates simple

    object and builder classes for type-safe access to arguments specified for destinations and actions. val username = arguments?.getString("usernameKey") Is replace by val username = args.username
  33. Safe Args Gradle Build.gradle dependencies { classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" //... }

    app/build.gradle apply plugin: 'kotlin-android' apply plugin: 'androidx.navigation.safeargs'
  34. Safe Arg in NavGraph <fragment android:id="@+id/talkFragment" android:name="com.montreal.wtm.ui.fragment.TalkFragment" android:label="content_talk" tools:layout="@layout/content_talk"> <argument

    android:name="talk" app:argType="com.montreal.wtm.model.Talk" /> </fragment>
  35. Passing Safe Args The plugin will generate a class {currentFragment}Directions

    with a function action{currentFragment}To{NextFragment)({Object specified } obj) talkViewHolder.itemView.setOnClickListener(v -> { Bundle bundle = new Bundle(); bundle.putParcelable(EXTRA_TALK, talk); Navigation.findNavController(v).navigate(R.id.action_nav_program_to_talkFragment, bundle); } talkViewHolder.itemView.setOnClickListener(v -> { ProgramFragmentDirections.ActionNavProgramToTalkFragment action; action = ProgramFragmentDirections.actionNavProgramToTalkFragment(talk); Navigation.findNavController(v).navigate(action); }
  36. Receiving the Safe Args @Override public View onCreateView(@NonNull LayoutInflater inflater,

    @androidx.annotation.Nullable ViewGroup container, @androidx.annotation.Nullable Bundle savedInstanceState) { ... this.talk = TalkFragmentArgs.fromBundle(getArguments()).getTalk(); ... } The plugin will generate a class {FragmentWithArgs}Args
  37. Pros and cons of SafeArgs Pros • You know easily

    what are the parameter you need to pass for a destination. • You don’t need to cast serializable and parcelable. (You can specify directly a child of those classes) • You can pass Enum Cons • Proguard ◦ You need to prevent all your safe Args (Parcelable, Serializable and Enum class) to be obfuscated
  38. Handles transition animations

  39. Animations Handle via XML animations on an Action

  40. Animations • You can still overwrite onCreateAnimator @Override public Animator

    onCreateAnimator(int transit, boolean enter, int nextAnim) { … } • You can shared Element for transition FragmentNavigator.Extras navigatorExtras = new FragmentNavigator.Extras.Builder() .addSharedElement(image, "imageName").build(); Navigation.findNavController(v).navigate(action, navigatorExtras); • And it easy to add animations for transition between screens
  41. Simplified deep link

  42. Challenges of Deep Links Home Category Item gdg://item For deeplink

    those screen need to be recreated and added to the back sack
  43. Deep Linking with Navigation Component There is two type of

    Deeplink with the Navigation Component • Explicite ◦ Will recreate all the stack following your graph to the destination of your deeplink. ◦ Is for Notification, Widget, App Shortcut • Implicite ◦ If FLAG_ACTIVITY_NEW_TASK is not set, will open the fragment on top of your current navigation
  44. Explicite DeepLink You need to use NavDeepLinkBuilder to create your

    pendingIntent val args = Bundle() args.putString("myarg", "From Widget"); val pendingIntent = NavDeepLinkBuilder(context) .setGraph(R.navigation.mobile_navigation) .setDestination(R.id.deeplink_dest) .setArguments(args) .createPendingIntent() remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent) You can also use navController to create the deeplink Navigation.findNavController(v).createDeepLink() .setGraph(R.navigation.mobile_navigation) .setDestination(R.id.deeplink_dest).createPendingIntent();
  45. Implicite DeepLink To enable implicit deep linking, you need to

    add the Nav-graph in the Manifest <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapplication"> <application ... > <activity name=".MainActivity" ...> ... <nav-graph android:value="@navigation/nav_graph" /> ... </activity> </application> </manifest> When building your projet, all <intent-filter> elements will be generated by the nav-graph <fragment android:id="@+id/talkFragment" android:name="com.montreal.wtm.ui.fragment.TalkFragment" android:label="content_talk" tools:layout="@layout/content_talk"> <argument android:name="talk" app:argType="com.montreal.wtm.model.Talk" /> <deepLink android:id="@+id/deepLink" app:uri="wtm://talk"/> </fragment>
  46. 2.0.0

  47. Change from 1.0.0 to 2.0.0 // Allows NavigationUI to support

    proper up navigation or the drawer layout // drawer menu, depending on the situation override fun onSupportNavigateUp(): Boolean { return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration) } Everything function that hooks navigation drawer, bottom nav bar with the NavController is now on the class NavigationUI override fun onSupportNavigateUp(): Boolean { return NavigationUI.navigateUp(findNavController(this, R.id.mainNavigationFragment), appBarConfiguration) } 2.0.0
  48. Change from 1.0.0 to 2.0.0 2.0.0 private fun setupNavigation() {

    ... val topLevelDestinations = HashSet<Int>() topLevelDestinations.add(R.id.nav_program) ... topLevelDestinations.add(R.id.nav_twitter) appBarConfiguration = AppBarConfiguration.Builder(topLevelDestinations) .setDrawerLayout(drawer_layout).build() setupActionBarWithNavController(this, navController, appBarConfiguration) } 1.0.0 private fun setupNavigation() { val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout) setupActionBarWithNavController(this, navController, appBarConfiguration) }
  49. New in 2.0.0 New Method Navigation.createNavigateOnClickListener(NavDirections) New features • You

    can create ViewModels that are scoped at a navigation graph • You can call navigation on Uri and deeplink • You can create <dialog> destinations
  50. THE END! Questions? Laurence de Villers @L_de_V