Android Jetpack组件系列文章:
Android Jetpack组件(一)LifeCycle
Android Jetpack组件(二)Navigation
Android Jetpack组件(三)ViewModel
Android Jetpack组件(四)LiveData
Android Jetpack组件(五)Room
Android JetPack组件(六)DataBinding
Android Jetpack组件(七)Paging
Android Jetpack组件(八)WorkManager
Android Jetpack组件(九)DataStore
Android Jetpack组件(十)App Startup
Android Jetpack组件(十一)Compose
首语
Android开发中流行单个Activity
嵌套多个Fragment
的UI架构模式,但是对Fragment
的管理比较麻烦。Fragment
的切换包括对AppBar的管理、Fragment
间的切换动画以及Fragment
间的参数传递。在此过程中实现代码比较复杂混乱。为此,Jetpack提供了Navigation组件,方便我们管理页面和AppBar。
优点
- 可视化的页面导航图,可以使用 Android Studio 的 Navigation Editor 来查看和编辑导航图。
- 通过
destination
和action
完成页面间的导航。 - 方便添加页面切换动画。
- 页面间类型安全的参数传递。
- 支持深层链接
DeepLink
。 - 通过
NavigationUI
类,对菜单、底部导航、抽屉菜单导航进行统一的管理。
主要元素
Navigation Graph
。这是一种新型的 XML 资源文件,其中包含应用程序所有的页面,以及页面间的关系。NavHostFragment
。这是一个特殊的Fragment
,Navigation Graph
中的 Fragment 正是通过NavHostFragment
进行展示的。NavController
。这是一个 Java/Kotlin 对象,用于在代码中完成Navigation Graph
中具体的页面切换工作。
使用
使用Navigation时,我们需要添加如下依赖:
def nav_version = "2.3.3"
// 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"
// Jetpack Compose Integration
implementation "androidx.navigation:navigation-compose:1.0.0-alpha07"
创建 Navigation Graph
新建一个Android项目后,选择res文件夹—>New Android Resource File,Resource type选择navigation,填写文件名后回车,Navigation Grpah文件创建完成。我们切换到Design面板,可以看到面板左上角提示No NavHostFragments found,接下来添加NavHostFragment
。
添加NavHostFragment
NavHostFragment
是一种特殊的Fragment
,我们需要将它添加到Activity
的布局文件中,作为其它Fragment
的容器。
<!--app:defaultNavHost="true"表示Fragment会自动处理系统返回事件-->
<!--app:navGraph="@navigation/mobile_navigation"设置Fragment对应的导航图-->
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
我们再次回到Nav Graph文件的Design面板,可以看到我们刚才设置的NavHostFragment
。
创建destination
点击Navigation Graph文件的Design面板上的click to add a destination,可以选择现有的Fragment
,也可以点击 Create new destination创建Fragment
。添加完成以后,可以看到Navigation Graph文件的代码。
<?xml version="1.0" encoding="utf-8"?>
<!--app:startDestination="@id/homeFragment"设置起始目的地-->
<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/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.yhj.jetpackstudy.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/dashboardFragment"
android:name="com.yhj.jetpackstudy.ui.dashboard.DashboardFragment"
android:label="fragment_dashboard"
tools:layout="@layout/fragment_dashboard" />
</navigation>
连接destination
在Navigation Graph文件的Design面板中,将鼠标悬停在destination的右侧,会出现一个圆圈,点击圆圈并将光标拖动到导航destination的上面,松开鼠标,会生成一个指示线。切换到Code面板可以看到代码发生了变化。
<fragment
android:id="@+id/homeFragment"
android:name="com.yhj.jetpackstudy.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home" >
<action
android:id="@+id/action_homeFragment_to_dashboardFragment4"
app:destination="@id/dashboardFragment" />
</fragment>
使用NavController完成导航
页面的切换通常有两种方式:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.action_navigation_home_to_navigation_dashboard);
}
});
button.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_navigation_home_to_navigation_dashboard));
添加页面切换效果
在Navigation Graph文件的action下添加要执行的动画
<action
android:id="@+id/confirmationAction"
app:destination="@id/confirmationFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
使用Safe Args插件传递参数
使用Safe Args Gradle插件,该插件可以生成简单的对象和构造器类,支持在destination之间进行类型安全的导航和参数传递。
在Project的build.gradle的dependencies下添加classpath。
dependencies {
classpath "com.android.tools.build:gradle:4.0.1"
def nav_version = "2.3.3"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
如需生成适合Java模块或Java和Kotlin混合模块的代码,可以在app的build.gradle下添加apply plugin。
apply plugin: "androidx.navigation.safeargs"
如需生成适用于 Kotlin 独有的模块的 Kotlin 代码,添加如下代码。
apply plugin: "androidx.navigation.safeargs.kotlin"
在Navigation Graph的添加如下<argument />
标签内代码。
<fragment
android:id="@+id/navigation_home"
android:name="com.yhj.life.LifeFragment"
android:label="fragment_life"
tools:layout="@layout/fragment_life">
<action
android:id="@+id/action_navigation_home_to_navigation_dashboard"
app:destination="@id/navigation_dashboard" />
<argument
android:name="user_name"
android:defaultValue="@null"
app:argType="string" />
<argument
android:name="age"
android:defaultValue="0"
app:argType="integer" />
</fragment>
argType除了支持常见的integer
、float
和long
等基本数据类型,还支持资源引用、自定义 Parcelable
、自定义 Serializable
和自定义 Enum
。
添加完成之后,在app java(generated)下面可以看到Safe Args插件为我们生成的代码,代码文件里包含参数生成的getter()
和setter()
。
- 传递参数
Bundle bundle=new MainFragmentArgs().builder()
.setUserName("yhj")
.setAge(30)
.build().toBundle();
Navigation.findNavController(view).navigate(R.id.action_navigation_home_to_navigation_dashboard);
- 接收参数
Bundle bundle=getArguments();
if(bundle!=null){
String userName=MainFragmentArgs.fromBundle(getArguments()).getUserName();
int age=MainFragmentArgs.fromBundle(getArguments()).getAge();
}
NavigationUI
页面切换的过程中,通常会伴随着AppBar的变化,AppBar中的按钮也可能承担页面切换的工作,既然Navigation和AppBar都需要处理页面切换事件,为了方便管理,Jetpack引入了NavigationUI组件。
在Navigation Graph文件中可以通过android:label
来设置AppBar的标题。
<navigation>
<fragment
android:label="Page title">
...
</fragment>
</navigation>
应用栏类型
NavigationUI支持以下顶部应用栏类型:
Toolbar
AppBarConfiguration
用于AppBar的配置,NavController用于页面的导航和切换。通过NavigationUI.setupWithNavController
绑定起来。
使用Toolbar
时,Navigation组件会自动处理导航按钮的点击事件,因此无需覆盖onSupportNavigateUp()
。
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(navController.getGraph()).build();
Toolbar toolbar = findViewById(R.id.toolbar);
NavigationUI.setupWithNavController(
toolbar, navController, appBarConfiguration);
CollapsingToolbarLayout
CollapsingToolbarLayout layout = findViewById(R.id.collapsing_toolbar_layout);
Toolbar toolbar = findViewById(R.id.toolbar);
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
AppBarConfiguration appBarConfiguration =new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupWithNavController(layout, toolbar, navController, appBarConfiguration);
ActionBar
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
需要覆盖onSupportNavigateUp()
处理向上导航。
@Override
public boolean onSupportNavigateUp() {
return NavigationUI.navigateUp(navController, appBarConfiguration) || super.onSupportNavigateUp();
}
- AppBar的左侧抽屉菜单(
DrawLayout
+Navigation View
)
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(navController.getGraph())
.setDrawerLayout(drawerLayout)
.build();
NavHostFragment navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
NavController navController = navHostFragment.getNavController();
NavigationView navView = findViewById(R.id.nav_view);
NavigationUI.setupWithNavController(navView, navController);
- AppBar上的菜单(menu)
如果通过Activity的onCreateOptionsMenu()
添加菜单,则可以通过覆盖Activity
的onOptionsItemSelected()
以调用onNavDestinationSelected()
,从而将菜单项与目标页相关联。同时需要覆盖onSupportNavigateUp()
处理向上导航。
@Override
public boolean onOptionsItemSelected(MenuItem item) {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.onNavDestinationSelected(item, navController)
|| super.onOptionsItemSelected(item);
}
当我们在Fragment
中添加菜单跳转目标页时,需要覆盖目标页面onCreateOptionsMenu()
,并在该方法中清除上个页面对应的menu。
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
menu.clear();
super.onCreateOptionsMenu(menu, inflater);
}
BottomNavigationView
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
//对于BottomNavigationView支持
NavigationUI.setupWithNavController(navView, navController);
导航事件监听
NavController
提供了一个名为OnDestinationChangedListener
的接口,对页面切换事件进行监听,该接口在页面发生切换或参数改变时调用。
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller,
@NonNull NavDestination destination, @Nullable Bundle arguments) {
//处理事件
}
});
DeepLink
在项目开发中,我们可能需要跳转到应用内指定的页面,Navigation组件提供了DeepLink(深层链接),通过它实现跳转到应用指定页面。它支持两种不同类型的深层链接:显式深层链接和隐式深层链接。
显式深层链接
显式深层链接使用PendingIntent
跳转到指定页面,例如应用程序收到某个通知推送,用户点击此通知时,条抓到展示该通知的内容页面。
当用户通过显式深层链接打开您的应用时,任务返回堆栈会被清除,并被替换为相应的深层链接页面。当用户从深层链接页面按下返回按钮时,他们会返回到相应的导航堆栈。
我们使用NavDeepLinkBuilder
类构造PendingIntent
。
PendingIntent pendingIntent = new NavDeepLinkBuilder(context)
.setGraph(R.navigation.nav_graph)
.setDestination(R.id.android)
.setArguments(args)
.createPendingIntent();
也可以通过navController.createDeepLink()
创建。
Navigation.findNavController(this, R.id.nav_host_fragment)
.createDeepLink()
.setGraph(R.navigation.mobile_navigation)
.setDestination(R.id.action_navigation_home_to_navigation_dashboard2)
.setArguments(bundle)
.createPendingIntent();
需要注意的是,如果提供的上下文不是Activity
,构造函数会使用PackageManager.getLaunchIntentForPackage()
作为默认Activity
启动(如有)。
//NavDeepLinkBuilder的构造函数
public NavDeepLinkBuilder(@NonNull Context context) {
mContext = context;
if (mContext instanceof Activity) {
mIntent = new Intent(mContext, mContext.getClass());
} else {
Intent launchIntent = mContext.getPackageManager().getLaunchIntentForPackage(
mContext.getPackageName());
mIntent = launchIntent != null ? launchIntent : new Intent();
}
mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
隐式深层链接
隐式深层链接指的是应用中特定页面的URI,如用户点击某个链接。
在触发隐式深层链接时,返回堆栈的状态取决于是否使用 Intent.FLAG_ACTIVITY_NEW_TASK
标志启动隐式Intent
。
- 如果该标志已设置,任务返回堆栈就会被清除,并被替换为相应的深层链接页面。与显式深层链接一样。
- 如果该标记未设置,您仍会位于触发隐式深层链接时所在的上一个应用的任务堆栈中。在这种情况下,如果按下返回按钮,您会返回到上一个应用;如果按下向上按钮,就会在导航图中的父级目的地上启动应用的任务。
在Navigation Graph文件中为页面添加<deepLink />
标签。
<deepLink app:uri="www.yanghujun.com/{params}?arg={arg}"/>
注意:
- 没有架构的 URI 会被假定为 http 或 https。
- 形式为
{params}
的路径参数占位符与一个或多个字符相匹配。 - 可以使用查询参数占位符代替路径参数,也可以将查询参数占位符与路径参数结合使用。
- 使用默认值或可为 null 的值所定义的变量的查询参数占位符无需匹配。
- 多余的查询参数不会影响深层链接 URI 匹配。
启用隐式深层链接,还需要向应用的manifest文件中添加nav-graph
标签。
<nav-graph android:value="@navigation/mobile_navigation"/>
构建项目时,Navigation 组件会将 <nav-graph />
标签替换为生成的 <intent-filter />
标签,以匹配导航图中的所有深层链接。
定制FragmentNavigator
阅读FragmentNavigator
的源码时,可以看到页面切换的时候使用的是replace()
,这会造成Fragment
生命周期的重启,界面数据重新加载,不能复用。源码如下。
@SuppressWarnings("deprecation") /* Using instantiateFragment for forward compatibility */
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
@Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
if (mFragmentManager.isStateSaved()) {
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state");
return null;
}
String className = destination.getClassName();
if (className.charAt(0) == '.') {
className = mContext.getPackageName() + className;
}
final Fragment frag = instantiateFragment(mContext, mFragmentManager,
className, args);
frag.setArguments(args);
final FragmentTransaction ft = mFragmentManager.beginTransaction();
int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = enterAnim != -1 ? enterAnim : 0;
exitAnim = exitAnim != -1 ? exitAnim : 0;
popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
}
ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mFragmentManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
}
isAdded = false;
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
isAdded = true;
}
if (navigatorExtras instanceof Extras) {
Extras extras = (Extras) navigatorExtras;
for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
}
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
通过show()/hide()
方式可以实现Fragment
的复用,同时也不会重启生命周期。可以自定义FragmentNavigator
重写navigate()
来达到以上目的。
首先自定义的FragmentNavigator
要和FragmentNavigator
一样,添加@Navigator.Name("新的Navigator名字")
注解。
@Nullable
@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args, @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
if (mManager.isStateSaved()) {
Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
+ " saved its state");
return null;
}
String className = destination.getClassName();
if (className.charAt(0) == '.') {
className = mContext.getPackageName() + className;
}
//实例化过的要重用
//final Fragment frag = instantiateFragment(mContext, mManager,
// className, args);
//frag.setArguments(args);
final FragmentTransaction ft = mManager.beginTransaction();
int enterAnim = navOptions != null ? navOptions.getEnterAnim() : -1;
int exitAnim = navOptions != null ? navOptions.getExitAnim() : -1;
int popEnterAnim = navOptions != null ? navOptions.getPopEnterAnim() : -1;
int popExitAnim = navOptions != null ? navOptions.getPopExitAnim() : -1;
if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
enterAnim = enterAnim != -1 ? enterAnim : 0;
exitAnim = exitAnim != -1 ? exitAnim : 0;
popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim);
}
//当前正在显示的fragment
Fragment fragment = mManager.getPrimaryNavigationFragment();
if (fragment != null) {
//隐藏展示下个页面
ft.hide(fragment);
}
Fragment frag = null;
String tag = String.valueOf(destination.getId());
//查找下个fragment,没有则创建
frag = mManager.findFragmentByTag(tag);
if (frag != null) {
ft.show(frag);
} else {
frag = instantiateFragment(mContext, mManager, className, args);
frag.setArguments(args);
ft.add(mContainerId, frag, tag);
}
//ft.replace(mContainerId, frag);
ft.setPrimaryNavigationFragment(frag);
final @IdRes int destId = destination.getId();
// mBackStack管理的是fragment回退的堆栈,源码中是private的无法获取,通过反射的方式获取
ArrayDeque<Integer> mBackStack = null;
try {
Field field = FragmentNavigator.class.getDeclaredField("mBackStack");
field.setAccessible(true);
mBackStack = (ArrayDeque<Integer>) field.get(this);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
final boolean initialNavigation = mBackStack.isEmpty();
// TODO Build first class singleTop behavior for fragments
final boolean isSingleTopReplacement = navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& mBackStack.peekLast() == destId;
boolean isAdded;
if (initialNavigation) {
isAdded = true;
} else if (isSingleTopReplacement) {
// Single Top means we only want one instance on the back stack
if (mBackStack.size() > 1) {
// If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
mManager.popBackStack(
generateBackStackName(mBackStack.size(), mBackStack.peekLast()),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
ft.addToBackStack(generateBackStackName(mBackStack.size(), destId));
}
isAdded = false;
} else {
ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));
isAdded = true;
}
if (navigatorExtras instanceof Extras) {
Extras extras = (Extras) navigatorExtras;
for (Map.Entry<View, String> sharedElement : extras.getSharedElements().entrySet()) {
ft.addSharedElement(sharedElement.getKey(), sharedElement.getValue());
}
}
ft.setReorderingAllowed(true);
ft.commit();
// The commit succeeded, update our view of the world
if (isAdded) {
mBackStack.add(destId);
return destination;
} else {
return null;
}
}
private String generateBackStackName(int backStackindex, int destid) {
return backStackindex + "-" + destid;
}
然后在Activity中创建自定义的FragmentNavigator
并设置给NavController
,需要注意的是我们要通过自定义的FragmentNavigator
手动来创建Destination(目的地),布局中app:navGraph=" "
设置无效。
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
navController = NavHostFragment.findNavController(fragment);
FixFragmentNavigator fragmentNavigator = new FixFragmentNavigator(this, getSupportFragmentManager(), fragment.getId());
navController.getNavigatorProvider().addNavigator(fragmentNavigator);
NavGraph navDestinations = initNavGraph(navController.getNavigatorProvider(), fragmentNavigator);
navController.setGraph(navDestinations);
//结合BottomNavigationView切换页面
navView.setOnNavigationItemSelectedListener(item -> {
navController.navigate(item.getItemId());
return true;
});
private NavGraph initNavGraph(NavigatorProvider provider, FixFragmentNavigator fragmentNavigator) {
NavGraph navGraph = new NavGraph(new NavGraphNavigator(provider));
//用自定义的导航器来创建目的地
FragmentNavigator.Destination destination1 = fragmentNavigator.createDestination();
destination1.setId(R.id.navigation_home);
destination1.setClassName(HomeFragment.class.getCanonicalName());
navGraph.addDestination(destination1);
FragmentNavigator.Destination destination2 = fragmentNavigator.createDestination();
destination2.setId(R.id.navigation_dashboard);
destination2.setClassName(DashboardFragment.class.getCanonicalName());
navGraph.addDestination(destination2);
FragmentNavigator.Destination destination3 = fragmentNavigator.createDestination();
destination3.setId(R.id.navigation_notifications);
destination3.setClassName(NotificationsFragment.class.getCanonicalName());
navGraph.addDestination(destination3);
navGraph.setStartDestination(destination1.getId());
return navGraph;
}
最后,我们还需要处理onBackPressed()
,因为返回Navigation会操作回退栈,切换到之前显示的页面,我们需要销毁当前页面则要进行拦截。
@Override
public void onBackPressed() {
finish();
}
这样,我们就完成了定制的Fragment页面切换。