导航架构组件 - 启动画面
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()
方法,如果当前 NavDestination
是 MainFragment
则只是 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 到您的常规 AppTheme
在 super
调用之前在 onCreate
中执行此操作:
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
.....
我想知道如何使用导航架构组件实现闪屏。
到目前为止我有这样的东西
用户第一次必须在 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()
方法,如果当前 NavDestination
是 MainFragment
则只是 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"
<?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 到您的常规 AppTheme
在 super
调用之前在 onCreate
中执行此操作:
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
.....