IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android_jectpack_Navigation -> 正文阅读

[移动开发]Android_jectpack_Navigation

简介

Navigation组件用于管理应用内的页面跳转,它默认能支持Activity、Fragment、DialogFragment之间的跳转,但是一般被用来处理Fragment之间的跳转问题,google推出它的目的也是让单 Activity 应用成为首选架构。
使用Navigation组件可以屏蔽处理 FragmentTransaction 的复杂性,自动处理跳转和返回栈问题,此外还支持deepLink,并提供了帮助程序,用于将导航关联到合适的 UI 小部件,例如抽屉式导航栏和底部导航。

简单使用

  1. 导入依赖
val nav_version = "2.3.5"
// 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")
  1. 在res目录下新建navigation文件夹,在文件夹下新建导航的xml文件
    在这里插入图片描述
  2. 编写导航文件
/**
*	导航文件根标签是navigation 
*	startDestination 用于指定默认加载的fragment
*	navigation中默认可以添加三种页面的导航:fragment、activity、dialog
*	其中每个标签的	id 是在跳转时使用
*					name 用于指定跳转到哪个具体页面
*					label 页面说明
*/
<?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"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.dean.playerdemo.ui.home.HomeFragment"
        android:label="@string/title_home"/>

    <activity
        android:id="@+id/play_activity"
        android:name="com.dean.playerdemo.PlayActivity"
        android:label="PlayActivity"/>

    <dialog
        android:id="@+id/exit_dialog"
        android:name="com.dean.playerdemo.ExitDialogFragment"
        android:label="ExitDialogFragment"/>
</navigation>
  1. 在Activity的布局文件中添加页面容器,并用navGraph指定导航文件
/**
*	布局中添加fragment标签
*	name必须是NavHostFragment
*	defaultNavHost 是用来处理返回栈的
*	navGraph 可以在xml中指定导航文件,也可以在代码中通过NavController指定
*/
<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_constraintStart_toStartOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:navGraph="@navigation/mobile_navigation" />

5、 现在直接运行就可以了,现在默认会加载startDestination指定的HomeFragment。

标签详解

fragment、activity、dialog 三个标签下可以使用其他标签用来指定跳转所需参数,页面内跳转事件、deeplink等信息。
这三个标签的具体使用和管理应用内的页面都需要通过NavController,所以先要获取NavController。

  • fragment中获取NavController
//kotlin 中,默认kotlin为Fragment写了扩展函数findNavController
val navController = findNavController()

// java中
NavController navController = Navigation.findNavController(getView());
  • Activity中获取NavController
// kotlin中,默认kotlin为Activity写了扩展函数findNavController
val navController = findNavController(R.id.nav_host_fragment)

// java中
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);

跳转事件 <action>

使用NavController.navigate跳转时,可以直接传入一个已经定义好的具体的页面:

//	R.id.play_activity就是之前定义好的activity
navController.navigate(R.id.play_activity)

但是最好还是使用action来跳转,这样便于管理和添加动画等效果,action可以分为两类

  1. 全局action,在当前graph中任意页面都可以使用
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">
    ......
    <action
        android:id="@+id/global_to_no_internet_tip"
        app:destination="@id/network_tip_fragment" />
     ......
</navigation>
  1. 局部action,只能在定义该action的页面内使用:
定义action:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">
    ......
    <fragment
        android:id="@+id/navigation_home"
        android:name="com.dean.playerdemo.ui.home.HomeFragment"
        android:label="@string/title_home">
        <action
            android:id="@+id/homefragment_action_to_play"
            app:destination="@id/play_activity" />
    </fragment>

	<activity
        android:id="@+id/play_activity"
        android:name="com.dean.playerdemo.PlayActivity"
        android:label="PlayActivity"/>
    ......
</navigation>

跳转(只能在HomeFragment内使用):
findNavController().navigate(R.id.homefragment_action_to_play)

action 标签有很多其他的属性,想要了解这些属性,需要先创造一个虚拟场景,假设有ABC三个页面,现在已经在栈中的页面是A和B,现在定义一个B跳转到C的Action:

<action
     android:id="@+id/page_b_action_to_page_c"
     app:destination="@id/Page_C"
     app:enterAnim="@anim/fragment_close_enter"
     app:exitAnim="@anim/fragment_close_exit"
     app:popUpTo="@id/Page_A"
     app:popUpToInclusive="true"
     app:popEnterAnim="@anim/fragment_close_exit"
     app:popExitAnim="@anim/fragment_fade_enter"
     app:launchSingleTop="true" />
  • popUpTo 和 popUpToInclusive
    popUpTo指定出栈时回到哪个页面,这边写的是Page_A,那么Page_C出栈时会直接回到Page_A,Page_B跳转到Page_C后的栈内元素是AC。
    popUpToInclusive用来判断到达指定元素时是否把指定元素也出栈,默认是false,如果设置为True,栈内元素就只剩下C,A也会出栈,这一般可以用来清空返回栈。
  • enterAnim 和 popExitAnim
    enterAnim是action目的地进入的动画,即Page_C的入场动画
    popExitAnim是action目的地离开的动画,即Page_C的出场动画
  • exitAnim 和 popEnterAnim
    exitAnim是action所在元素离开的动画,即Page_B的出场动画
    popEnterAnim是action所在元素进入的动画,即Page_B的入场动画
  • launchSingleTop
    效果类似于Activity的SingleTop,栈顶复用模式

指定参数 <argument>

argument用来指定跳转到该页面时可以携带的参数

<argument
      android:name="channelId"
      android:defaultValue="ch000009507"
      app:argType="string" />
  • name:指定参数的名称
  • defaultValue:参数的默认值
  • argType:参数类型,可以是integer、float、long、boolean、string、资源和自定义的类。
    传参以及使用:
跳转时带入参数:
findNavController().navigate(R.id.homefragment_action_to_play, Bundle().apply {
    putString("channelId", "ch0000020110816")
})
跳转后使用:
getArguments().getString("channelId")

深度链接 <deepLink>

深层链接指直接转到应用内特定目的地的链接,一般是从应用外部跳转时使用,深层链接可分为两种:

  1. 显式深层链接:一般用于通知或应用微件中
val pendingIntent = navController.createDeepLink()
				//设置导航视图
				.setGraph(R.navigation.navigation_settings)
				//设置深层链接目的地目的地
                .setDestination(R.id.page_d)
                //传入参数
                .setArguments(args)
                //如果目的地所处的Activity不是目前所处的Activity或者xml中配置的启动页,这边可以指定所需的Activity
                .setComponentName(SettingsActivity::class.java)
                //创建PendingIntent,然后将它设置到通知或者应用微件的点击里面即可。
                .createPendingIntent()

如果没有navController实例,还可以通过NavDeepLinkBuilder(this)来创建PendingIntent:
val pendingIntent = NavDeepLinkBuilder(this)
                .setGraph(R.navigation.navigation_settings)
                .setDestination(R.id.page_d)
                .setArguments(args)
                .setComponentName(SettingsActivity::class.java)
                .createPendingIntent()
  1. 隐式深层链接:用于其他如分享的链接、其他应用跳转等场景:

首先为页面设置deeplink条件:

<fragment
    android:id="@+id/fragment_page_d"
    android:name="com.dean.playerdemo.ui.settings.PageDFragment"
    tools:layout="PageDFragment">
    //deeplink 可以有三种过滤条件 uri、action、mimetype
    <deepLink app:uri="http://smart/app/settings/page/d"/>
</fragment>

其次在Activity中申明能够处理的deeplink

<activity android:name=".NavTestActvity">
    <nav-graph android:value="@navigation/navigation_settings"/>
</activity>

这样在跳转时只要intent附带的data是 Page_D能够处理的,就能直接跳转到D:

startActivity(Intent().apply {
    data = Uri.parse("http://smart/app/settings/page/d")
})

其原理大致是 当 nav-graph 设置到对应的activity中时,会自动解析 里面所包含的deeplink并且附加到该activity的 行为当中,其效果相当于为Activity直接申明 intent-filter。这样使用隐式跳转时就能定位到所需的activity,至于能够定位到Page_D,则还是通过NavController,上面代码中没有出现的原因是系统帮我们默认处理了,但是只有Activity启动模式是standard时才会自动处理,如果不是standard启动模式,则还需手动处理一下,在Activity的onNewIntent中添加:

override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    findNavController(R.id.nav_host_fragment).handleDeepLink(intent)
}

NavController其他常用api

NavController:导航控制器,它负责操作Navigation框架下的Fragment的跳转与退出、动画、监听当前Fragment信息,下面看看它的一些常用api:

  • setGraph:一般项目中为了便于管理,不同业务的页面使用的Navigation是分开的,比如设置功能会有个navigation_settings.xml文件,里面放置设置里面会有的页面;新闻功能会有个navigation_news.xml文件,里面放置新闻功能涉及的页面。而切换Navigation就是使用setGraph
controller.setGraph(R.navigation.navigation_settings)

controller.setGraph(R.navigation.navigation_news)
  • popBackStack:当前Fragment出栈,返回true代表返回成功,假设当前栈中有ABCD四个页面
如果调用popBackStack(),D出栈,此时栈中剩下ABC,C显示出来
controller.popBackStack()

如果调用popBackStack(R.id.page_b, false),CD出栈,B显示出来
controller.popBackStack(R.id.page_b, false)

如果调用popBackStack(R.id.page_b, true),BCD出栈,A显示出来
controller.popBackStack(R.id.page_b, true)
  • navigateUp:功能和popBackStack类似,也是当前页面出栈,不过效果不同,navigateUp多了层保护,popBackStack() 如果当前的返回栈是空的就会报错,因为栈是空的了,navigateUp() 则不会,还是停留在当前界面
controller.navigateUp()
  • getCurrentDestination:获取当前目的地的信息
controller.getCurrentDestination(); //获取当前目的地的信息
Log.d(TAG, "onCreate: NavigatorName = ${navDestination.getNavigatorName()}");
Log.d(TAG, "onCreate: id = ${navDestination.getId()}");
Log.d(TAG, "onCreate: Parent = ${navDestination.getParent()}");
  • addOnDestinationChangedListener:设置监听,用来监听NavController导航页面变更信息
navController.addOnDestinationChangedListener { controller, destination, arguments ->
    ...
}
  • getNavInflater:可以根据资源文件来获取到NavGraph 对象,然后就可以动态添加页面或者删除
val settingsGraph = navController.navInflater.inflate(R.navigation.navigation_settings)
settingsGraph.addDestination(newDestination)
settingsGraph.remove(oldDestination)        

原理浅析

Android Jectpack 中大部分架构组件都能自带生命周期处理,这是这系列组件的优势,能让开发者放心使用而不需要额外手动处理页面生命周期带来的问题。纵观这些架构组件源码,不难发现都是通过一些特殊的fragment完成这件事,比如LifeCycle是通过ReportFragment 来完成的,Navigation 也不例外,它的入口代码在NavHostFragment中,首先查看它的oncreate方法:

public void onCreate(@Nullable Bundle savedInstanceState) {
        ......
        //创建出NavHostController
        mNavController = new NavHostController(context);
        ......
        onCreateNavController(mNavController);
        ......
}

摘取出来的这两行代码可以说是主要干了一件事,就是声明该NavHostController能够控制的页面类型。

public NavController(@NonNull Context context) {
	.......
	mNavigatorProvider.addNavigator(new NavGraphNavigator(mNavigatorProvider));
	mNavigatorProvider.addNavigator(new ActivityNavigator(mContext));
}

protected void onCreateNavController(@NonNull NavController navController) {
    navController.getNavigatorProvider().addNavigator(
            new DialogFragmentNavigator(requireContext(), getChildFragmentManager()));
    navController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}

而声明该NavHostController能够控制的页面类型就是将对应类型的Navigator添加到NavHostController的NavigatorProvider中。可以看到添加了四种Navigator:NavGraphNavigator、ActivityNavigator、DialogFragmentNavigator、FragmentNavigator。
这和之前说的navigation默认处理三种页面导航不太一样,其实NavGraphNavigator就是之前的资源文件中的根标签navigation,它的主要作用只有一个,就是加载startDestination指定的页面,它是找出startDestination指定的页面的具体类型,最后还是通过相应类型的Navigator来实现加载。
这四种Navigator都是抽象类Navigator的子类
在这里插入图片描述
以FragmentNavigator为例,查看FragmentNavigator类的声明:

@Navigator.Name("fragment")
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
	.......
}

在实现Navigator时需要通过注解Name来标识当前Navigator能够处理的页面名称,这个Name很重要,它是一个纽带。首先当FragmentNavigator被添加到NavHostController的NavigatorProvider中时,其实是加到一个HashMap中的,而这个hashmap使用的key就是Name注解中的值 “fragment”:

public class NavigatorProvider {
	......
	private final HashMap<String, Navigator<? extends NavDestination>> mNavigators =
	            new HashMap<>();
	......
}

其次是之前xml中定义的fragment标签在解析完成后会生成NavDestination对象,而对象中的属性mNavigatorName就保存了该节点名称 “fragment”:

public class NavDestination {
	......
	private final String mNavigatorName;
    private NavGraph mParent;
    private int mId;
    private String mIdName;
    private CharSequence mLabel;
    private ArrayList<NavDeepLink> mDeepLinks;
    private SparseArrayCompat<NavAction> mActions;
    private HashMap<String, NavArgument> mArguments;
	......
}

最后在调用NavHostController的跳转方法时就是使用mNavigatorName在NavigatorProvider的HashMap中查找,找到后就调用相应的Navigator的navigate方法来完成跳转。

public class NavController {
	.......
	private void navigate(@NonNull NavDestination node, @Nullable Bundle args,
	            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
        .......
        Navigator<NavDestination> navigator = mNavigatorProvider.getNavigator(
                node.getNavigatorName());
        Bundle finalArgs = node.addInDefaultArgs(args);
        NavDestination newDest = navigator.navigate(node, finalArgs,
                navOptions, navigatorExtras);
        .......
    }
    .......
}

自此navigation的简单原理就是这样,下面图便于理解:
在这里插入图片描述

实战中fragment 默认replace问题修改

使用Navigation来跳转Fragment时会发现每次都是替换之前的Fragment,每次跳转时原来的Fragment的view都会被销毁,当前的Fragment是新创建出来的:

D/smartFragments: PageAFragment onCreateView
D/smartFragments: PageBFragment onCreateView
D/smartFragments: PageAFragment onDestroyView
D/smartFragments: PageCFragment onCreateView
D/smartFragments: PageBFragment onDestroyView

之前知道Fragment的跳转是通过FragmentNavigator 来完成的,来查看FragmentNavigator 的navigate方法就能找到该问题的原因所在:

@Navigator.Name("fragment")
public class FragmentNavigator extends Navigator<FragmentNavigator.Destination> {
    @Nullable
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {
            ......
            final FragmentTransaction ft = mFragmentManager.beginTransaction();
            ......
            ft.replace(mContainerId, frag);
            ......
            ft.commit();
    }
}

Navigation虽然帮助开发者屏蔽处理 FragmentTransaction 的复杂性,但是默认用的是replace来替换之前的Fragment,这与大部分实际项目需求不太符合,下面是自定义Fragment的Navigator,来替换系统默认的FragmentNavigator。

  1. 新建一个用于Fragment跳转的Navigator,它继承自FragmentNavigator:
@Navigator.Name("smartFragment")
class SmartFragmentNavigator(private val mContext: Context,
                             private val  mFragmentManager: FragmentManager,
                             private val  mContainerId: Int)
    : FragmentNavigator(mContext, mFragmentManager, mContainerId) {
    
}
  1. 重写navigate方法,其中大部分是从FragmentNavigator拷贝来的,只修改replace涉及的必要部分:
override fun navigate(
    destination: Destination, args: Bundle?,
    navOptions: NavOptions?, navigatorExtras: Navigator.Extras?
): NavDestination? {
	...
    //获取当前正在显示的fragment
    val currentFragment =
        mFragmentManager.primaryNavigationFragment
    if (currentFragment != null) {
        ft.hide(currentFragment)
    }

    //寻找需要显示的Fragment
    var needShowFragment =
        mFragmentManager.findFragmentByTag(destination.id.toString())
    if (needShowFragment != null) {
        //如果找到直接show
        ft.show(needShowFragment)
    } else {
        //如果没找到就创建,并且添加进去
        needShowFragment = instantiateFragment(
            mContext, mFragmentManager,
            className, args
        )
        needShowFragment.arguments = args
        ft.add(mContainerId, needShowFragment)
    }
    ft.setPrimaryNavigationFragment(needShowFragment)
    ...
}
  1. 点击Android studio》Build》Make Project,然后就可以在xml中使用了,将之前的所有fragment标签都替换成smartFragment
<?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/mobile_navigation"
    app:startDestination="@+id/fragment_page_a">

    <smartFragment
        android:id="@+id/fragment_page_a"
        android:name="com.dean.playerdemo.ui.settings.PageAFragment"
        tools:layout="PageAFragment">
        <action
            android:id="@+id/action_fragment_page_a_to_fragment_page_b"
            app:destination="@id/fragment_page_b" />
    </smartFragment>

    <smartFragment
        android:id="@+id/fragment_page_b"
        android:name="com.dean.playerdemo.ui.settings.PageBFragment"
        tools:layout="PageBFragment">
        <action
            android:id="@+id/action_fragment_page_b_to_fragment_page_c"
            app:destination="@id/fragment_page_c" />
    </smartFragment>

    <smartFragment
        android:id="@+id/fragment_page_c"
        android:name="com.dean.playerdemo.ui.settings.PageCFragment"
        tools:layout="PageCFragment">
        <action
            android:id="@+id/action_fragment_page_c_to_fragment_page_d"
            app:destination="@id/fragment_page_d" />
    </smartFragment>

    <smartFragment
        android:id="@+id/fragment_page_d"
        android:name="com.dean.playerdemo.ui.settings.PageDFragment"
        tools:layout="PageDFragment">
        <deepLink app:uri="http://smart/app/settings/page/d"/>
    </smartFragment>


</navigation>
  1. 将新定义的Navigator添加至NavController中:
val navController = findNavController(R.id.nav_host_fragment)
navController.navigatorProvider.addNavigator("smartFragment",
    SmartFragmentNavigator(this,
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment)!!.childFragmentManager,
        R.id.nav_host_fragment)
)
//因为需要添加自定义Navigator,所以要等添加完再指定Graph,因此不需要再在xml中指定Graph
//删除之前的app:navGraph="@navigation/navigation_settings":
navController.setGraph(R.navigation.navigation_settings)
  1. 运行结果
D/smartFragments: PageAFragment onCreateView
D/smartFragments: PageBFragment onCreateView
D/smartFragments: PageCFragment onCreateView
D/smartFragments: PageDFragment onCreateView

最后附上自定义Navigator全部源码:

package com.dean.playerdemo.utils

import android.content.Context
import android.os.Bundle
import android.util.Log
import androidx.annotation.IdRes
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.FragmentNavigator
import java.util.*

@Navigator.Name("smartFragment")
class SmartFragmentNavigator(private val mContext: Context,
                             private val  mFragmentManager: FragmentManager,
                             private val  mContainerId: Int)
    : FragmentNavigator(mContext, mFragmentManager, mContainerId) {

    val TAG = SmartFragmentNavigator::class.java.simpleName

    override fun navigate(
        destination: Destination, args: Bundle?,
        navOptions: NavOptions?, navigatorExtras: Navigator.Extras?
    ): NavDestination? {
        if (mFragmentManager.isStateSaved) {
            Log.i(TAG, "Ignoring navigate() call: FragmentManager has already"
                        + " saved its state")
            return null
        }

        //mBackStack 在父类中是私有的,这边需要通过反射来获取
        var mBackStack: ArrayDeque<Int>? = null
        try {
            val field =
                FragmentNavigator::class.java.getDeclaredField("mBackStack")
            field.isAccessible = true
            mBackStack = field[this] as ArrayDeque<Int>
        } catch (e: Exception) {
            e.printStackTrace()
        }
        var className = destination.className
        if (className[0] == '.') {
            className = mContext.packageName + className
        }
        val ft = mFragmentManager.beginTransaction()
        var enterAnim = navOptions?.enterAnim ?: -1
        var exitAnim = navOptions?.exitAnim ?: -1
        var popEnterAnim = navOptions?.popEnterAnim ?: -1
        var popExitAnim = navOptions?.popExitAnim ?: -1
        if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) {
            enterAnim = if (enterAnim != -1) enterAnim else 0
            exitAnim = if (exitAnim != -1) exitAnim else 0
            popEnterAnim = if (popEnterAnim != -1) popEnterAnim else 0
            popExitAnim = if (popExitAnim != -1) popExitAnim else 0
            ft.setCustomAnimations(enterAnim, exitAnim, popEnterAnim, popExitAnim)
        }

        //获取当前正在显示的fragment
        val currentFragment =
            mFragmentManager.primaryNavigationFragment
        if (currentFragment != null) {
            ft.hide(currentFragment)
        }

        //寻找需要显示的Fragment
        var needShowFragment =
            mFragmentManager.findFragmentByTag(destination.id.toString())
        if (needShowFragment != null) {
            //如果找到直接show
            ft.show(needShowFragment)
        } else {
            //如果没找到就创建,并且添加进去
            needShowFragment = instantiateFragment(
                mContext, mFragmentManager,
                className, args
            )
            needShowFragment.arguments = args
            ft.add(mContainerId, needShowFragment)
        }
        ft.setPrimaryNavigationFragment(needShowFragment)
        @IdRes val destId = destination.id
        val initialNavigation = mBackStack!!.isEmpty()
        // TODO Build first class singleTop behavior for fragments
        val isSingleTopReplacement = (navOptions != null && !initialNavigation
                && navOptions.shouldLaunchSingleTop()
                && mBackStack.peekLast() == destId)
        val isAdded: Boolean
        isAdded = if (initialNavigation) {
            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))
            }
            false
        } else {
            ft.addToBackStack(generateBackStackName(mBackStack.size + 1, destId))
            true
        }
        if (navigatorExtras is Extras) {
            for ((key, value) in navigatorExtras.sharedElements) {
                ft.addSharedElement(key!!, value!!)
            }
        }
        ft.setReorderingAllowed(true)
        ft.commit()
        // The commit succeeded, update our view of the world
        return if (isAdded) {
            mBackStack.add(destId)
            destination
        } else {
            null
        }
    }

    private fun generateBackStackName(backStackIndex: Int, destId: Int): String? {
        return "$backStackIndex-$destId"
    }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-13 17:34:48  更:2021-07-13 17:36:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/29 0:31:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码