Slide 1

Slide 1 text

The Navigation Component 2.0 Laurence de Villers @L_de_V

Slide 2

Slide 2 text

Laurence de Villers Mobile dev at Transit Lead organizer of GDG Montreal Women Techmakers Lead Canada Regional Mentor @L_de_V

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Navigation Component ● Simplified setup for commun navigation patterns ○ Handles backstack ○ Automates fragment transaction ● Type safe argument passing ● Handles transition animations ● Simplified deep link

Slide 5

Slide 5 text

Simplified setup for commun navigation patterns

Slide 6

Slide 6 text

Challenges of standard navigation: FragmentManager FragmentManager Fragment FragmentTransaction Activity Fragments Back Stack

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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; }

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

First principle of Navigation There’s Always a starting point

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Navigation component take care of BackStack Fragmentmanager DeepLink

Slide 14

Slide 14 text

How to get started?

Slide 15

Slide 15 text

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" }

Slide 16

Slide 16 text

Add the navigation graph New → Android resource file

Slide 17

Slide 17 text

Create your graph By default, android studio will find all the fragment that you have in your application

Slide 18

Slide 18 text

Nested Graph If you have too much screen you can separate graph into groups Move to Nested Graph > New Graph

Slide 19

Slide 19 text

Add a Navigation Host Fragment in your main activity ... You need to specify if this navHost is the default. You can have multiple NavHost is you have more than one activity

Slide 20

Slide 20 text

How it works ? A NavHostFragment swaps different destinations in and out as you navigate through the navigation graph.

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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() topLevelDestinations.add(R.id.nav_program) ... topLevelDestinations.add(R.id.nav_twitter) appBarConfiguration = AppBarConfiguration.Builder(topLevelDestinations) .setDrawerLayout(drawer_layout).build() setupActionBarWithNavController(this, navController, appBarConfiguration) }

Slide 25

Slide 25 text

Navigate using drawers and navigation view On the activity_main.xml

Slide 26

Slide 26 text

Auto setup NavigationView with the navigation graph Use the same ID in your NavigationView menu and on your graph. ...

Slide 27

Slide 27 text

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(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()

Slide 28

Slide 28 text

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(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) }

Slide 29

Slide 29 text

Wait, how to pass an argument with the new Navigation Component?

Slide 30

Slide 30 text

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); }

Slide 31

Slide 31 text

Type Safe argument passing

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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'

Slide 34

Slide 34 text

Safe Arg in NavGraph

Slide 35

Slide 35 text

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); }

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Handles transition animations

Slide 39

Slide 39 text

Animations Handle via XML animations on an Action

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Simplified deep link

Slide 42

Slide 42 text

Challenges of Deep Links Home Category Item gdg://item For deeplink those screen need to be recreated and added to the back sack

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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();

Slide 45

Slide 45 text

Implicite DeepLink To enable implicit deep linking, you need to add the Nav-graph in the Manifest ... ... When building your projet, all elements will be generated by the nav-graph

Slide 46

Slide 46 text

2.0.0

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

Change from 1.0.0 to 2.0.0 2.0.0 private fun setupNavigation() { ... val topLevelDestinations = HashSet() 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) }

Slide 49

Slide 49 text

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 destinations

Slide 50

Slide 50 text

THE END! Questions? Laurence de Villers @L_de_V