Navigation Architecture Component - Splash screen

2019-02-09 17:34发布

问题:

I would like to know how to implement splash screen using Navigation Architecture Component.

Till now I have something like this

A user has to setup his profile in ProfileFragment for the first time and can edit their profile from ChatFragment.

My problem is I don't know how to remove SplashFragment from stack after navigation. I've seen conditional navigation but didn't quite understand.

回答1:

Splash screens have always been weird on Android. You only want to show a splash screen between clicking the app icon and the creation of the first Activity. If your splash screen is a Fragment the user will still see the white background until the first Activity is created and the startup time of your app will increase because a splash Fragment has to be created and removed. The best practice is to use a splash AppTheme like Ian Lake (an Android Framework Engineer) explains in this post.

As for navigation, your app should have a fixed destination which is the first and last screen a user sees when entering and exiting your app like explained in the principles of navigation. In your case it would make sense to make the ChatFragment the fixed destination. In the onCreate of the ChatFragment you should check if the user already has a profile and redirect them to the ProfileFragment using conditional navigation if they don't.



回答2:

You can try this, it currently works for me:

 <action
        android:id="@+id/action_splashFragment_to_profileFragment"
        app:destination="@id/signInFragment"
        app:launchSingleTop="true"
        app:popUpTo="@id/splashFragment"
        app:popUpToInclusive="true" />

Just set the popUpTo(the current destination) and popUpToInclusive(true), this will pop all other screens until it gets to the specified destination - also pop the destination if popUpToInclusive() is set to true - before navigating to the new destination.



回答3:

Here is two way to solve the above situation.

One: In the NavHostFragment's Activity, override the onBackPress() method,adn if the current NavDestination is MainFragment then just finish() the Activity.

@Override
public void onBackPressed() {
    NavDestination navDestination = mNavController.getCurrentDestination();
    if (navDestination != null
            && navDestination.getId() == R.id.mainFragment) {
        finish();
        return;
    }
    super.onBackPressed();
}

Two: Set the to MainFragment action in Navigation_graph app:popUpTo="@id/nav_graph" and app:popUpToInclusive="true"

<?xml version="1.0" encoding="utf-8"?>
<navigation
    android:id="@+id/nav_graph"
    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"
    app:startDestination="@id/splashFragment">

    <fragment
        android:id="@+id/splashFragment"
        android:name="xxx.fragment.splash.SplashFragment"
        android:label="fragment_splash"
        tools:layout="@layout/fragment_splash">
        <action
            android:id="@+id/action_splashFragment_to_mainFragment"
            app:destination="@id/mainFragment"
            app:enterAnim="@anim/anim_right_in"
            app:exitAnim="@anim/anim_left_out"
            app:popEnterAnim="@anim/anim_left_in"
            app:popExitAnim="@anim/anim_right_out"
            app:popUpTo="@id/nav_graph"
            app:popUpToInclusive="true"/>
        <action
            android:id="@+id/action_splashFragment_to_guideFragment"
            app:destination="@id/guideFragment"
            app:enterAnim="@anim/anim_right_in"
            app:exitAnim="@anim/anim_left_out"
            app:popEnterAnim="@anim/anim_left_in"
            app:popExitAnim="@anim/anim_right_out"
            app:popUpTo="@id/nav_graph"
            app:popUpToInclusive="true"/>
    </fragment>

    <fragment
        android:id="@+id/guideFragment"
        android:name="xxx.fragment.guide.GuideFragment"
        android:label="GuideFragment"
        tools:layout="@layout/fragment_guide">
        <action
            android:id="@+id/action_guideFragment_to_mainFragment"
            app:destination="@id/mainFragment"
            app:enterAnim="@anim/anim_right_in"
            app:exitAnim="@anim/anim_left_out"
            app:popEnterAnim="@anim/anim_left_in"
            app:popExitAnim="@anim/anim_right_out"
            app:popUpTo="@id/nav_graph"
            app:popUpToInclusive="true"/>
    </fragment>

    <fragment
        android:id="@+id/mainFragment"
        android:name="xxx.fragment.main.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main">

    </fragment>

</navigation>

Wish for help!

Warning:this is deprecated. In Navigation alpha08, the clearTask tag is removed:Remove the deprecated clearTask and launchDocument flags from NavOptions

In my project, I test app:clearTask="true" in the action,and it works well.~~~

<fragment
    android:id="@+id/splashFragment"
    android:name="xxx.SplashFragment"
    android:label="fragment_splash"
    tools:layout="@layout/fragment_splash">
    <action
        android:id="@+id/action_splashFragment_to_mainFragment"
        app:destination="@id/mainFragment"
        app:enterAnim="@anim/anim_right_in"
        app:exitAnim="@anim/anim_left_out"
        app:popEnterAnim="@anim/anim_left_in"
        app:popExitAnim="@anim/anim_right_out"
        app:clearTask="true"/>
</fragment>


回答4:

You can try this trick it's woking absolutely fine for me. Make your splash screen as launcher screen from the manifest file and start your host activity form the splash screen.

Manifest file code

`<activity android:name=".SplashScreen"
          android:theme="@style/Theme.AppCompat.Light.NoActionBar">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
</activity>`

In your java file

         `new Handler().postDelayed(new Runnable(){
                    @Override
                    public void run() {
                        /* Create an Intent that will start the Navigation host activity . */
                        Intent mainIntent = new Intent(getApplicationContext(), MainActivity.class);
                        startActivity(mainIntent);
                        finish();
                    }
                }, 5000);`

Xml code of your host activity

`<?xml version="1.0" encoding="utf-8"?>
    android.support.constraint.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/nav_host_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="0dp"
                android:layout_height="0dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/navigation_graph" />
</android.support.constraint.ConstraintLayout>`

And you navigation will be like this

`<?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/navigation_graph"
        app:startDestination="@id/homeFragment"
        >
      <fragment
         android:id="@+id/homeFragment"
         android:name="com.devgenesis.breaker.ice.navigationmproject.HomeFragment"
         android:label="fragment_home"
         tools:layout="@layout/fragment_home" />
    </navigation>`