Navigation is a Fragment navigator with a better fallback stack. And Navigation can be used in conjunction and BottomNavigationView/NavigationView/Toolbar, internal use are fragments of the replace rather than show | hidden, so there is no penetration phenomenon. DetachView exists in the life cycle, so the view needs to be reused by itself. Although the use of fragments has become more clear and convenient, I still do not recommend using fragments to replace activities. That’s just adding to the trouble.

I usually project development essential framework

  1. The most powerful network request Net on Android
  2. The strongest list on Android (including StateLayout) BRV
  3. Android’s most powerful default page StateLayout
  4. JSON and long text log printing tool LogCat
  5. Support for asynchronous and global custom toast tool Tooltip
  6. Develop debug window tool DebugKit
  7. One line of code creates the transparent StatusBar StatusBar

KTX (functions that contain some new Kotlin features in addition to basic dependencies)

implementation 'androidx. Navigation: navigation - fragments - KTX: 2.0.0'
implementation 'androidx. Navigation: navigation - UI - KTX: 2.0.0'
Copy the code

Common keywords

The keyword describe
navigation Navigation or fallback stack
graph Navigation map
destination Destination, that is, the page to jump to
pop Out of the stack, will always bedestinationOut of the stack until the specified id page, can be understood as a FragmentsingleTaskmodel
navHost That’s the container for all the pages. Like host in web pages, all paths follow host, and host is fixed.

XML

Click Design in NavResourceFile to view the layout editor, which is divided into three columns.

On the left is the added navigation, and in the middle is page browsing. The toolbar in the middle bar can create and quickly add labels and organize pages, and the property bar on the right is convenient for adding properties.

Navigation this is a nested chart that can be clicked to open a new chart page.

The Activity layout

<LinearLayout
    ./>
    <androidx.appcompat.widget.Toolbar
        ./>
    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/mobile_navigation"
        app:defaultNavHost="true"
        />
    <com.google.android.material.bottomnavigation.BottomNavigationView
        ./>
</LinearLayout>
Copy the code
The android: name = "androidx. Navigation. Fragments. NavHostFragment" fixed writing app: navGraph specified navigation resource file, You can also set the app defaultNavHost to intercept the return key event dynamically by not specifying the following code. False means no rollback is required.Copy the code

Creating navhost Fragments by hand does not automatically complete the code. You can create them using Editor Designer.

Creating a resource File

Res directory create AndroidResourceFile and select Navigation. Then the new – > NavigationResourceFile

Navigation node

App :startDestination="@+id/home_dest" specifies the initial destinationCopy the code

Navigation can be nested with navigation labels. This will appear in the layout editor:

Nested navigation cannot be associated with each other

<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/nav_global"
    app:startDestination="@id/mainFragment">

 <! -- <action android:id="@+id/global_action" app:destination="@id/navigation" />-->

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.frameexample.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main">
        <action
            android:id="@+id/action_mainFragment_to_personInfoFragment"
            app:destination="@id/settingFragment" />
    </fragment>

    <navigation
        android:id="@+id/navigation"
        app:startDestination="@id/settingFragment">
        <fragment
            android:id="@+id/settingFragment"
            android:name="com.example.frameexample.SettingFragment"
            android:label="fragment_setting"
            tools:layout="@layout/fragment_setting" />
    </navigation>

</navigation>
Copy the code

Destination =”@id/settingFragment” You can only navigate to navigation first.

Fragments node

Android :id android:name Specifies the fragment that you want to instantiate. Tools :layout is used to display the layout editor. Android :label Is used to bind to Toolbar and automatically update the titleCopy the code

Argument nodes

android:name="myArg"
app:argType="integer"
android:defaultValue="0"
Copy the code
  • The parameter name
  • The parameter types
  • Parameter Default values

When you jump to the navigation page, you will automatically add arguments to the argument list. Array and Paraclable/Serializable do not support default Settings, and SafeArg can be used to verify parameter type safety issues in the compiler as described below.

The action node

Action Specifies the target page to jump to

Android :id="@+ ID /next_action" app:destination="@+ ID /flow_step_two_dest"> popUpTo="@id/home_dest App :popUpToInclusive="true/false" App :launchSingleTop="true/false" app:enterAnim="" App :popEnterAnim="" App :popExitAnim="" Popup stack animationCopy the code

Animation is not supported if you go from a navigation page to a new Activity page. Please use the default Activity Settings animation to support.

A global movement

NavController can only use actions that are children of the current page’s node in the XMl. You cannot use actions under other fragments.

However, you can create a subtag, action, directly to the Navigation TAB. This is a global action that can be used by each Fragment.

<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/nav_main"
    app:startDestination="@id/homeFragment">

    <action
        android:id="@+id/action_categoryFragment_to_main2Activity"
        app:destination="@id/main2Activity" />

    <fragment
        android:id="@+id/categoryFragment"
        android:name="com.example.frame.fragment.CategoryFragment"
        android:label="fragment_category"
        tools:layout="@layout/fragment_category"/>
  
</navigation>
Copy the code

Placeholder page

A placeholder page throws an exception if the runtime does not specify a Class and navigates to the placeholder page

conclusion

Normally, we’ll just define the Fragment node in XML.

I think these action | argument node is mainly for use in SafeArgsGradle plug-ins. It is convenient to automatically generate classes for scanning plug-ins. I do not recommend using two nodes if you do not use plug-ins, which will be discussed in detail at the end of this article. The plug-in handles parameter passing and fetching and destination jumping automatically.

Class relationships

The NavController controls the jump and popup stacks of navigation

NavOptions controls configuration options during the jump, such as animation and singleTop mode

The Navigation utility class creates a click event or retrieves the controller. It might be easier to implement the extension function yourself.

NavHostFragment container for navigation. You can set and retrieve the NavGraph.

NavGraph objects that describe the relationships between pages in the navigation can be added, deleted, and modified to look up pages, set the start page, and so on

NavigationUI tool class used to automatically bind navigation to a set of menu controls

The root interface of the Navigator page, which is customizable if you want to create a new type of page

NavigatorProvider an internal HashMap that holds the [name :Navigator] key-value pair. Generally do not need to use.

NavDeepLinkBuilder builds an Intent that opens the navigation page

Navinflaters are used to create XML objects into NavGraph objects, and NavControllers can create them with more convenient functions without using them directly.

void NavInflater(@NonNull Context context, 
                 @NonNull NavigatorProvider navigatorProvider) // Require the current NavHostFragment internal variable
/ / the constructor, but generally use NavController. GetNavInflater access objects

void NavGraph inflate(@NavigationRes int graphResId)
Copy the code

NavBackStackEntry represents a destination corresponding to

NavController

The NavController is used for controls such as page hopping and parameter passing, and can be instantiated by extending functions.

The instance is obtained through three extension functions

1. Fragment.findNavController() 2. View.findNavController() 3. Activity.findNavController(viewId: Int) // The Id of NavHostFragmentCopy the code

navigate

Jump to

public void navigate(@IdRes int resId, 
                     @Nullable Bundle args, 
                     @Nullable NavOptions navOptions,
                     @Nullable Navigator.Extras navigatorExtras) // You can set animation parameters for shared elements

public void	navigate(Uri deepLink, NavOptions navOptions, Navigator.Extras navigatorExtras)
Copy the code
  • In addition toresIdThese are optional parameters

Create custom objects describing jump targets (actions) and bundles (bundles) by implementing the abstract NavDirections class

public void navigate (NavDirections directions, NavOptions navOptions, Navigator.Extras navigatorExtras)
Copy the code
  • In addition todirectionsThese are optional parameters

The resId can be either the ACTION in XML or the ID of the destination node. If it is an action, the configuration of the action will be attached. If it is a destination, the actions of the sub-nodes under the destination node will not be attached.

Args is the Bundle parameter that needs to be passed between fragments, but the navigation also supports another plugin for passing parameters – the security parameter SafeArgs, mentioned later.

NavOptions are navigation page configuration options (such as animation)

Return to the superior

public boolean navigateUp (a)
public boolean navigateUp (DrawerLayout drawerLayout)
public boolean navigateUp (AppConfiguration appConfiguration)
Copy the code

Out of the stack

Remove fragments from the Nav rollback stack.

public boolean popBackStack (intDestinationId, // destinationIdboolean inclusive) // Whether to include the parameter target
  
public boolean popBackStack (a)
// Display the current Fragment
Copy the code

Destination change listener

This listener is called back each time a page change, such as a jump, is made

public void addOnDestinationChangedListener (NavController.OnDestinationChangedListener listener)
public void removeOnDestinationChangedListener (NavController.OnDestinationChangedListener listener)
Copy the code
public interface OnDestinationChangedListener {
  /** * callback function after the navigation is complete (but maybe the animation is still playing) **@paramControls navigation to the target navigation controller NavController *@paramTarget page *@paramNavigation to the target page parameter */
  void onDestinationChanged(@NonNull NavController controller,
                            @NonNull NavDestination destination, @Nullable Bundle arguments);
}
Copy the code

DeepLink

NavDeepLinkBuilder createDeepLink (a)
// Inside is the constructor called DeepLinkBuilder

boolean	handleDeepLink(Intent intent)
Copy the code
NavDestination getCurrentDestination (a)

NavigatorProvider getNavigatorProvider (a)

NavInflater	getNavInflater(a)

Bundle saveState (a)
void restoreState (Bundle navState)
// This is used to retrieve and restore the NavController's state
Copy the code

The NavGraph function

void setGraph(NavGraph graph, Bundle startDestinationArgs);

void setGraph(int graphResId, 
              Bundle startDestinationArgs);

NavGraph getGraph (a)
Copy the code
  • startDestinationArgsAn optional parameter that is passed to the starting destination

NavDirections

An abstract class is used for custom actions and arguments, and if you want to reuse a jump logic you can custom inherit this class. This class is usually not inherited manually because it is too cumbersome. This class is used as a derived class automatically generated by the SafeArgs plug-in.

Navigator.Extras

The navigatorExtras parameter mentioned earlier is currently a shared element used to support transition animations.

This can be created quickly through extension functions

fun FragmentNavigatorExtras(vararg sharedElements: Pair<View, String>)

fun ActivityNavigatorExtras(activityOptions: ActivityOptionsCompat? = null, flags: Int = 0)
Copy the code

You can see from the source code that the internals are created using the Navigator.Extras.Builder constructor.

NavOptions

Belongs to the navigation of the additional option Settings

XML sample

    <fragment
        android:id="@+id/home_dest"
        android:name="com.example.android.codelabs.navigation.HomeFragment"
        android:label="@string/home"
        tools:layout="@layout/home_fragment">

        <action
            android:id="@+id/next_action"
            app:destination="@+id/flow_step_one_dest"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />

    </fragment>
Copy the code

Creating the NavOptions object corresponds to code that dynamically implements the attribute setting of the

tag in the XML (but cannot specify the target).

Currently, the only functions are set animation and singleTop and popUp.

Create an object

Provide a DSL scope

fun navOptions(optionsBuilder: NavOptionsBuilder.() -> Unit): NavOptions
Copy the code

The sample

val options = navOptions {
  anim {
    enter = R.anim.slide_in_right // Enter the page animation
    exit = R.anim.slide_out_left
    popEnter = R.anim.slide_in_left  // Pop the stack animation
    popExit = R.anim.slide_out_right
  }

  launchSingleTop = true
  popUpTo = R.id.categoryFragment
}

findNavController().navigate(R.id.flow_step_one_dest, null, options)
Copy the code

Pop up the stack

public NavOptions.Builder setPopUpTo (int destinationId, 
                                      boolean inclusive)
Copy the code

The function does not determine the destination, but simply executes a popup stack instruction before navigating to the destination.

Scenario: for example, I am now buying a product to pay successfully, this time I want to put the previous product details; Close the order configuration page and enter the < Payment success > page

By the way, the previous practice was to send the event and finish

NavDestination

Represents the current target object

The NavController can get the NavDestination

findNavController().currentDestination
Copy the code

function

void	addDeepLink(String uriPattern)
boolean	hasDeepLink(Uri deepLink)

Navigator	getNavigator(a)
NavGraph	getParent(a)

NavAction	getAction(int id)
void	putAction(int actionId, NavAction action)
void	putAction(int actionId, int destId)
void	removeAction(int actionId)

void	setDefaultArguments(Bundle args)
void	addDefaultArguments(Bundle args)
Bundle	getDefaultArguments(a)
  
void	setId(int id)
int	getId(a)

void	setLabel(CharSequence label)
CharSequence	getLabel(a)
Copy the code

NavHostFragment

This object provides a container for Navigation

The usual use case is defined directly in the layout, but you can also build instances from the code and then create views from the code (ViewPager, etc.)

public static NavHostFragment create (int graphResId)
Copy the code

The UI binding

Navigation can be linked to a series of view components

BottomNavigationView

Multiple navigation charts can be bound with the extension function and associated with BNV.

fun BottomNavigationView.setupWithNavController(
    navGraphIds: List<Int>,
    fragmentManager: FragmentManager,
    containerId: Int,
    intent: Intent
): LiveData<NavController>
Copy the code
  • navGraphIdsThe set contains the IDS of navGrap. Each BNV button corresponds to a navGrap.

ActionBar

Set the title text to automatically update when navigating to a new page

fun AppCompatActivity.setupActionBarWithNavController(
    navController: NavController,
    drawerLayout: DrawerLayout?). 

fun AppCompatActivity.setupActionBarWithNavController(
    navController: NavController,
    configuration: AppBarConfiguration = AppBarConfiguration(navController.graph)
)
Copy the code

There appeared a parameter AppBarConfiguration, used to configure the Toolbar/ActionBar/CollapsingToolbarLayout.

AppBarConfiguration

The constructor pattern uses The Builder to create instances

AppBarConfiguration.Builder(NavGraph navGraph)
AppBarConfiguration.Builder(Menu topLevelMenu)
AppBarConfiguration.Builder(int. topLevelDestinationIds) AppBarConfiguration.Builder(Set<Integer> topLevelDestinationIds)// The constructor has the same effect
Copy the code

function

AppBarConfiguration.Builder setDrawerLayout(DrawerLayout drawerLayout)
// Bind the Toolbar and a DrawerLayout linkage

AppBarConfiguration.Builder setFallbackOnNavigateUpListener(AppBarConfiguration.OnNavigateUpListener fallbackOnNavigateUpListener)
// Listen for return operations

AppBarConfiguration  build(a)
Copy the code

AppBarConfiguration. OnNavigateUpListener the callback interface will be callback when each click up navigation

public interface OnNavigateUpListener {
/** * Callback handles navigation up **@returnReturns true to navigate up; false does not handle */
boolean onNavigateUp(a);
}
Copy the code

Toolbar

The Toolbar can also be bound to Nav to automatically update the title of the corresponding page

Source:

fun Toolbar.setupWithNavController(
    navController: NavController,
    drawerLayout: DrawerLayout?). {
    NavigationUI.setupWithNavController(this, navController,
        AppBarConfiguration(navController.graph, drawerLayout))
}
Copy the code

CollapsingToolbarLayout

fun CollapsingToolbarLayout.setupWithNavController(
    toolbar: Toolbar,
    navController: NavController,
    configuration: AppBarConfiguration = AppBarConfiguration(navController.graph)
)

fun CollapsingToolbarLayout.setupWithNavController(
    toolbar: Toolbar,
    navController: NavController,
    drawerLayout: DrawerLayout?).
Copy the code

Menu

Bind menu items by clicking Autonavigate

fun MenuItem.onNavDestinationSelected(navController: NavController): Boolean =
        NavigationUI.onNavDestinationSelected(this, navController)
Copy the code

The usage is as follows:

For example, in the onOptionsItemSelected function

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
    }
Copy the code

NavigationView

fun NavigationView.setupWithNavController(navController: NavController) {
    NavigationUI.setupWithNavController(this, navController)
}

fun BottomNavigationView.setupWithNavController(navController: NavController) {
    NavigationUI.setupWithNavController(this, navController)
}
Copy the code

A look at the source code shows that these extension functions are essentially static functions that encapsulate the NavigationUI.

DeepLink

Deep links belong to the open page of the new rollback stack. Navigation supports two ways to enable DeepLink.

ID to open

Very simple

findNavController().createDeepLink()
									.setDestination(R.id.dynamicFragment)
									.createPendingIntent()
									.send()
Copy the code

Link to open

Nav declares that a DeepLink simply requires adding a subtag to the Fragment

The following two child nodes need to be added to the Activity of the corresponding page in the AndroidManifest

<activity> <action android:name="android.intent.action.VIEW" /> <nav-graph android:value="@navigation/mobile_navigation"  /> </activity>Copy the code

Deep links

<deepLink app:uri="www.example.com/{myarg}" />
Copy the code
  • http://Can be omitted

Pass ADB tests

adb shell am start -a android.intent.action.VIEW -d "http://www.YourWebsite.com/fromWeb"
Copy the code

2334456 is the passed parameter

Fields wrapped by {} are variables, and * can match any character

Create DeepLink Intent using NavDeepLinkBuilder

NavDeepLinkBuilder	setArguments(Bundle args)

NavDeepLinkBuilder	setDestination(int destId)

NavDeepLinkBuilder	setGraph(int navGraphId)
NavDeepLinkBuilder	setGraph(NavGraph navGraph)
Copy the code

Creating a PendingIntent can be used to open an interface (for example, to send a Notification)

PendingIntent	createPendingIntent(a)
TaskStackBuilder	createTaskStackBuilder(a)
Copy the code

SafeArgs

Security type plug-in, based on Gradle implementation of the plug-in.

The goal is to generate a utility class based on the argument tag you declared in NavRes, and then use the utility class entirely to get and set the arguments instead of strings. Avoid crash due to inconsistent parameter types.

The plug-in

buildscript {
    repositories {
        jcenter()
        google()
    }
    dependencies {
				classpath 'android. Arch. Navigation: navigation - safe - the args - gradle - plugin: 1.0.0'}}Copy the code

Application of plug-in

apply plugin: 'androidx.navigation.safeargs'
Copy the code

The class is automatically generated when the

tag is declared in NavigationResourceFile

The plug-in automatically generates a {current class name}Directions class based on the page. This class contains all the jump actions that the page can use (both global actions and its own actions).

The generated class will contain a regular static function to get Directions’ implementation class (*Directions’ static inner class). The function name rule is

Action {page name}To{target page name} The global action name rule is as follows: public static ActionMainFragmentToPersonInfoFragment actionMainFragmentToPersonInfoFragment()Copy the code

Here’s what the NavDirections interface means

Public interface NavDirections {/** * Return action id ** @return id of an action */ @idres int getActionId(); /** * Return the target argument */ @nonnull Bundle getArguments(); }Copy the code

Can be summarized as including carry parameters and actions.

However, if navRes also contains an

tag, the corresponding {current class name}Args class is generated.

Complete navigation page and pass data writing

Jump destination

findNavController().navigate(CategoryFragmentDirections.actionCategoryFragmentToDynamicFragment())
Copy the code

If the destination you jump to requires passing the parameter flag

findNavController().navigate(CategoryFragmentDirections.actionCategoryFragmentToDynamicFragment().setFlag(2))
Copy the code

Get the parameters in the Fragment

tv.text = PersonInfoFragmentArgs.fromBundle(arguments!!) .nameCopy the code

Essentially, the plugin restricts the difference between optional and required passing parameters when a developer passes them to a jump destination

conclusion

I would like to say my opinion about Google’s drive to build apps with SingleActivity. I think it is troublesome to use one Activity for the entire app.

List the problems

  1. Default animation cannot be set for fragments. Each page needs to be animated separately.
  2. Fragments are unnecessary because they reduce memory overhead and are not even perceived by the user.
  3. Many frameworks implemented based on the Activity or | function (such as routing, the status bar), may be some project architecture will be limited

I think Navigation is handy instead of FragmentManger, and the Navigation diagram looks logical.