1, Navigation introduction

1.1 Background of Navigation birth

The UI architecture mode of nesting multiple fragments with a single Activity has been accepted by most Android engineers. However, managing fragments has always been a hassle, and engineers need to manage switching between fragments through FragmentManager and FragmentTransaction.

In Android, page switching and management include application Appbar management, Fragment animation switching, parameter transfer between fragments and so on. Also, the pure code approach is not particularly user-friendly, and Appbar can be confusing to manage and use. Therefore, Jetpack provides a component called Navigation that is designed to make it easy for developers to manage Fragment pages and appbars.

Compared to FragmentManager and FragmentTransaction, using a Navigation component has the following advantages:

  • Visual page navigation diagram, convenient for us to clarify the relationship between pages
  • Navigate between pages through destinations and actions
  • Easy to add page switch animation
  • Type-safe parameter passing between pages
  • Unified management of menu/bottom Navigation/drawer blue menu Navigation via Navigation UI class
  • Support DeepLink DeepLink

1.2 Navigation elements

Before formally learning Navigation components, we need to have a simple understanding of the main elements of Navigation, which is mainly composed of three parts.

  • Navigation Graph: An XML resource that contains all Navigation and page relationships.
  • NavHostFragment: A special Fragment used to hold navigational content in containers.
  • NavController: Manages the navigation objects of applications and performs operations such as jumping between fragments.

Second, Navigation use

2.1 Adding a Dependency

First, create a new Android project and introduce the following dependency script in build.gradle.

dependencies {
  def nav_version = "2.3.2"
  // Java language implementation
  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"
}

Copy the code

2.2 Creating a Navigation Diagram

In the [Project] window, right-click in the res directory and select [New] -> [Android Resource File] to create the New Resource File dialog box, as shown below.In the popup interface, you can enter File name freely, select Navigation with Resource Type, and then click OK.After clicking OK, the navigation directory will be created under the RES directory, along with the navigation file nav_graph.xml.

2.3 Navigation graph

Open the nav_graph. XML file, and you can see that there is no current content in the Design interface. You can click the “New Destination” icon and then click “Create New Destination” to quickly Create a New Fragment. The FragmentA, FragmentB, and FragmentC fragments are created.

Click on a page and a dot will appear on the right. Drag the dot to the right of the page. For example, set the link to FragmentA -> FragmentB -> FragmentC.Then we switch to the Code panel and see the generated Code shown below.

<? 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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/fragmentA">

    <fragment
        android:id="@+id/fragmentA"
        android:name="com.example.testnavigation.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB"
            app:destination="@id/fragmentB" />
    </fragment>
    <fragment
        android:id="@+id/fragmentB"
        android:name="com.example.testnavigation.FragmentB"
        android:label="fragment_b"
        tools:layout="@layout/fragment_b" >
        <action
            android:id="@+id/action_fragmentB_to_fragmentC"
            app:destination="@id/fragmentC" />
    </fragment>
    <fragment
        android:id="@+id/fragmentC"
        android:name="com.example.testnavigation.FragmentC"
        android:label="fragment_c"
        tools:layout="@layout/fragment_c" />
</navigation>

Copy the code

Several tags are used in the generated code above, with the following meanings.

  • Navigation: The root tag that specifies the default startup page via the startDestination configuration.
  • Fragment: The fragment tag represents a fragment view.
  • Action: The Action tag defines the behavior of the page to jump to, the Destination tag defines the target page to jump to, and can also define the jump animation.

2.4 NavHostFragment

A Fragment needs an Activity container to function properly. NavHostFragment is the container that holds navigation content, and it needs to be bound to an Activity.

<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        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" />

</androidx.constraintlayout.widget.ConstraintLayout>
Copy the code

Among them, the android: name = “androidx. Navigation. Fragments. NavHostFragment” the role of the line of code is to tell the android system this is a special fragments. And if app:defaultNavHost=”true” is true, the Fragment will be returned automatically.

  • Android: name Specifies NavHostFragment
  • App: navGraph specifies the navigation view, which is built nav_graph.xml
  • App :defaultNavHost=true, can intercept the system return key, can be taken as a default to achieve the return key function fragment.

Then we run the program and see that the FragmentA page is displayed by default. This is because the MainActivity layout file has NavHostFragment configured and assigned the FragmentA page to display by default.

2.5 NavController

Navcontrollers are used to manage jumps between fragments. Each NavHost has its own NavController. FindNavController can be used to obtain the NavController and then navigate between pages using NavController’s navigate or navigateUp methods.

Open the Fragmenta.java file and add the following code to the onViewCreated lifecycle method.

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_a, container, false);
        Button btnB = view.findViewById(R.id.btn_b);
        btnB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Navigation.findNavController(v).navigate(R.id.action_fragmentA_to_fragmentB); }});return view;
    }
Copy the code

Run the above code, and then click FragmentA page button, the system will through Navigation. FindNavController (v) navigate Navigation to FragmentB () method.

2.6 Adding Animations

When jumping between fragments, you can also animate the jump. Open the Design option of the nav_graph.xml file, and in the Animations section of the Attributes panel, click the drop-down arrow next to the animation you want to add. Developers can choose from the following types, as shown in the Animations section below.The Attributes panel refers to the following Attributes.

  • EnterAnim: Animation of the target page when jumping
  • ExitAnim: Animation of the original page when jumping
  • PopEnterAnim: Target page animation for rollback
  • PopExitAnim: Original page animation at rollback time

Then, open the Code panel and generate the following Code.

<fragment
        android:id="@+id/fragmentA"
        android:name="com.xzh.jetpack.FragmentA"
        android:label="fragment_a"
        tools:layout="@layout/fragment_a" >
        <action
            android:id="@+id/action_fragmentA_to_fragmentB"
            app:destination="@id/fragmentB"
            app:enterAnim="@android:anim/slide_in_left"
            app:exitAnim="@android:anim/slide_out_right"
            app:popEnterAnim="@anim/fragment_fade_enter"
            app:popExitAnim="@anim/fragment_fade_exit" />
    </fragment>
Copy the code

Parameter transfer

In Android, it is recommended to pass as little data as possible between pages, because the total space available to store all the states on Android is limited. If you need to pass a lot of data, you can use the ViewModel.

Fragment switching is often accompanied by passing parameters. In order to coordinate with the Navigation component to pass parameters when switching fragments, Android Studio provides developers with two methods for passing parameters: Safe Args and Bundle.

3.1 Transferring Data using Bundles

When passing data using the Bundle, the Bundle object is first created and then navigate() is used to pass it to the destination, as shown below.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    tv.setOnClickListener {
    	 Bundle bundle = new Bundle(a); bundle.putString("key"."from fragmentA");
         Navigation.findNavController(v).navigate(R.id.action_fragmentA_to_fragmentB,bundle); }}Copy the code

The recipient then uses the getArguments() method to get the data passed by the Bundle, as shown below.

String keyStr = getArguments().getString("key");
Copy the code

3.2 Transferring data using Safe Args

First, add the CLASspath configuration in your project’s build.gradle, as shown below.

dependencies {
    def nav_version = "2.3.2"
    classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
Copy the code

Then, add the Apply Plugin script to your app’s build.gradle, as shown below.

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

After the configuration is complete, the rebuild project is generated{module}/build/generated/source/navigation-args/{debug}/{packaged}/{Fragment}Dircetions, as shown below.

If you need to pass data to the destination page, first follow these steps to add parameters to the destination page that receives it. Navigation provides a subtag argument that can be used to pass arguments.

First, in the Navigation Editor, click on the destination page to receive the parameters, and in the Attributes panel, click Add (+). Then, in the Add Argument Link window that displays, enter the parameter name, parameter type, whether the parameter can be null, and default value (if required) by clicking the Add button, as shown below.

Click the Text TAB to switch to the XML view, and you’ll see the generated code below.

<argument
     android:name="key"
     app:argType="string"
     app:nullable="false"
     android:defaultValue="Navigation parameter transfer" />
Copy the code

We then pass the data in fragmenta.java using the following code, as shown below.

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_a, container, false);
        Button btnB = view.findViewById(R.id.btn_b);
        btnB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
              FragmentADirections.ActionFragmentAToFragmentB action=FragmentADirections.actionFragmentAToFragmentB().setKey("Parameter passing via safeArgs");
              Navigation.findNavController(v).navigate(action); }});return view;
    }
Copy the code

Accept using the following method in the onCreate method of the accepted page.

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            String name=  FragmentBArgs.fromBundle(getArguments()).getKey();
        }
    }
Copy the code

4. DeepLink

When an application receives a notification and wants the user to click on the notification and go directly to the page showing the notification, this is the most common scenario for DeepLink, and the Navigation component provides support for DeepLink.

DeepLink can be used in two different scenarios: PendingIntent and real URL links, both of which can be used to jump to the page specified in the application.

4.1 PendingIntent

PendingIntent is typically used for message notifications. When an application receives a notification and wants the user to click on the notification to go directly to the specified page, it does so with PendingIntent.

For example, click the button in MainActivity to bring up the notification bar, and click the notification bar to go to the specified NotificationActivity page.

public class MainActivity extends AppCompatActivity {

    NotificationManager manager=null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init(a); }private void init(a) {
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            // Note that once the notification channel is established, it cannot be modified
            NotificationChannel channel =new NotificationChannel("normal"."Normal",NotificationManager.IMPORTANCE_DEFAULT);
            manager.createNotificationChannel(channel);
            NotificationChannel channel2 =new NotificationChannel("important"."Important",NotificationManager.IMPORTANCE_HIGH);
            manager.createNotificationChannel(channel2);
        }

        Button pending=findViewById(R.id.btn_pendingintent);
        pending.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendNotification();
            }
        });
    }

    void sendNotification(a){
        Intent intent =new Intent(this,NotificationActivity.class);
        PendingIntent pi = PendingIntent.getActivity(this.0,intent,0);
        // use NotificationCompat compatible with systems prior to 8.0
// val notification = NotificationCompat.Builder(this,"normal")
        Notification notification = new NotificationCompat.Builder(this."important").setContentTitle("This is content title").setContentText("This is content text").setStyle(new NotificationCompat.BigTextStyle().bigText("Learn how to build notification,send any sync data,and use voice actions.Get the " +
                        "official Android IDE and developer tools to build apps for Android.")).setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background)))
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background))
                .setContentIntent(pi)
                .setAutoCancel(true).build(a); manager.notify(1,notification); }}Copy the code

Then, we need to create a new notification destination page. To differentiate the MainActivity, we put only a TextView component on the NotificationActivity page, as shown below.

public class NotificationActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_notification); }}Copy the code

Then run the code above to get the final result shown below.

4.2 the URL

When browsing a page on a website using a mobile browser, users can open the corresponding application page using a web browser. If you have our app on your phone, DeepLink will open the page. If not, the web site can navigate to the download page of the application to direct the user to install the application.

First, add a
tag for Destination in the navigation diagram, and fill in the app: URI property with the corresponding Web page address for your site, as shown below.

<fragment
    android:id="@+id/deepLinkSettingsFragment"
    android:name="com.michael.deeplinkdemo.DeepLinkSettingsFragment"
    android:label="fragment_deep_link_settings"
    tools:layout="@layout/fragment_deep_link_settings"> <! -- Add a <deepLink/> tag to destination --> <deepLink app: URI ="www.YourWebsite.com/{params}" />

</fragment>
Copy the code

As shown above, the APP: URI fills in the corresponding Web page address of the site, and the parameters in {params} are passed to the page via the Bundle object.

Then, set the
tag for the corresponding Activity, and your application will listen when the user accesses the link on the Web, as shown below.

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".DeepLinkActivity">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <! -- Set the <nav-graph/> tag for the Activity --> <nav-graph Android :value="@navigation/nav_graph"/>
    </activity>

</application>
Copy the code

After the above Settings, it’s time to test our functionality. We can enter the corresponding Web address in the Google App, or use the ADB tool, using the command line to complete the test operation.

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

After executing the command, the phone opens deepLinkSettingsFragment directly. In this Fragment, we can get the corresponding argument (fromWeb) from the Bundle object to do the following operations.

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
    View view = inflater.inflate(R.layout.fragment_deep_link_settings, container, false);
    Bundle bundle = getArguments(a);if(bundle ! = null){ String params = bundle.getString("params");
        TextView tvDesc = view.findViewById(R.id.tvDesc);
        if(! TextUtils.isEmpty(params)){
            tvDesc.setText(params); }}return view;
}
Copy the code

The running effect is shown in the figure below.Reference:

Lifecycle Android LiveData Android Jetpack (3) ViewModel Android Jetpack (2) Lifecycle Android Jetpack architecture component (I) with AndroidX