导航架构组件 - 启动画面

Navigation Architecture Component - Splash screen

我想知道如何使用导航架构组件实现闪屏。

到目前为止我有这样的东西

用户第一次必须在 ProfileFragment 中设置他的个人资料,并且可以在 ChatFragment 中编辑他们的个人资料。

我的问题是我不知道如何在导航后从堆栈中删除 SplashFragment。我看过 conditional navigation 但不太明白。

Android 上的启动画面一直很奇怪。您只想在单击应用程序图标和创建第一个 Activity 之间显示启动画面。如果您的启动画面是 Fragment,则在创建第一个 Activity 之前,用户仍会看到白色背景,并且您的应用程序的启动时间将会增加,因为必须创建启动画面 Fragment并删除。最佳做法是像 Ian Lake(一位 Android 框架工程师)在 this post.

中解释的那样使用启动 AppTheme

至于导航,您的应用应该有一个固定的目的地,即用户在进入和退出您的应用时看到的第一个和最后一个屏幕,如 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 中所述,如果他们没有。

你可以试试这个,它目前对我有用:

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

只需设置 popUpTo(the current destination)popUpToInclusive(true),这将弹出所有其他屏幕,直到它到达指定的目的地 - 如果 popUpToInclusive() 设置为 [=14,也会弹出目的地=] - 在导航到新目的地之前。

这里有两种方法可以解决上述情况。

一个: 在 NavHostFragment's Activity 中,重写 onBackPress() 方法,如果当前 NavDestinationMainFragment 则只是 finish() Activity.

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

两个: 在 Navigation_graph app:popUpTo="@id/nav_graph"app:popUpToInclusive="true"

中设置 MainFragment 动作
<?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>

求助!

警告:这已被弃用。在 Navigation alpha08 中,清除了 clearTask 标签:Remove the deprecated clearTask and launchDocument flags from NavOptions

在我的项目中,我在action中测试了app:clearTask="true",效果很好~~~

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

你可以试试这个技巧,它对我来说绝对没问题。 将您的初始屏幕作为清单文件中的启动屏幕,然后从初始屏幕启动您的主机 activity。

清单文件代码

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

在你的 java 文件中

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 主机代码 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: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/navigation_graph" />

</android.support.constraint.ConstraintLayout>

你的导航会是这样的

<?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>

当启动画面显示给用户几秒钟时,这是一种常见的误用,用户正在浪费时间查看启动画面,而他们已经可以与应用程序进行交互了。相反,您应该尽快将他们带到他们可以与应用程序交互的屏幕上。由于之前的启动画面在 android 上被认为是反模式。但后来 Google 意识到,从用户点击图标到您的第一个 App 屏幕准备好进行交互之间仍然存在短暂的 window,在此期间您可以显示一些品牌信息。这是实现启动画面的正确方法。

因此,要以正确的方式实现启动画面,您不需要单独的启动画面片段,因为它会在应用程序加载中引入不必要的延迟。为此,您只需要特殊的 主题 。理论上,应用程序主题可以应用于 UI 并比您的应用程序 UI 初始化并变得可见更快地变得可见。所以简而言之,你只需要 SplashTheme 这样的:

<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/splash_background</item>
</style>

splash_background drawable 应该是这样的:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:opacity="opaque"> <!-- android:opacity="opaque" should be here -->
    <item>
        <!--this is your background, you can use color, gradient etc.-->
        <color android:color="@color/colorPrimary"/>
        <!--<shape>
              <gradient
                   android:angle="315"
                   android:endColor="#1a82ff"
                   android:startColor="#2100d3"
                   android:type="linear"/>
        </shape> -->
    </item>
    <item>
        <bitmap android:src="@drawable/ic_logo"
                android:gravity="center"/>
    </item>
</layer-list>

您的片段无论如何都会托管在一些 activity 中,我们称它为 MainActivty。在 Manifest 中,只需将 SplashTheme 添加到您的 Activity(这将显示从用户单击应用程序图标那一刻起的初始屏幕主题):

<activity android:name=".ui.MainActivity"
              android:theme="@style/SplashTheme">

然后在 MainActivity 到 return 到您的常规 AppThemesuper 调用之前在 onCreate 中执行此操作:

override fun onCreate(savedInstanceState: Bundle?) {
    setTheme(R.style.AppTheme)
    super.onCreate(savedInstanceState)
    .....