像使用Activity一样使用Fragment


/   今日科技快讯   /

近日,抖音支付已在抖音APP内正式上线,除支付宝和微信支付外,用户可在支付时选择抖音支付。2020年12月24日,北京字节跳动科技有限公司新增“抖音支付”商标申请信息,国际分类涉及36类“金融物管”,商标状态为“注册申请中”。

/   作者简介   /

本篇文章来自fundroid_方卓的投稿,分享了他实现的一个框架Fragivity,目的是让Fragment的使用像Activity一样丝滑,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章!

fundroid_方卓的博客地址:

https://blog.csdn.net/vitaviva

/   前言   /

" An app only needs an Activity, you can use Fragments, just don’t use the backstack with fragments "
– Jake Wharton @Droidcon NYC 2017

近年来,SPA,即单Activity架构逐渐开始受到欢迎,随之而生了很多优秀的三方库,大部分是基于Fragment作为实现方案,其中最有代表性的就是Fragmentation了,后来Jetpack Navigation的诞生也标志着Google从官方立场对SPA架构的肯定。

Navigation的出现并没有加速Fragment对Activity的全面取代,一个重要原因是因为其过与依赖配置(NavGraph),丧失了Activity的灵活性。这一点上Fragmentation做的不错,有接近Activity的使用体验,可惜其不支持Kotlin,且早已停止维护,无法使用近年来在AndroidX中引入的各种新特性。

是否有一个工具,既具备Fragmentation那样灵活性,又能像Navigation那样兼容AndroidX中的新功能呢?Fragivity正是在这个背景下诞生的。

Fragivity的项目地址:

https://github.com/vitaviva/fragivity

Fragmentation的项目地址:

https://github.com/YoKeyword/Fragmentation

/   Use Fragment Like Activity   /

顾名思义,Fragivity希望让Fragment具备Activity一样的使用体验,从而在各种场景中能真正取而代之:

  • 生命周期与Activity行为一致

  • 支持多种LaunchMode

  • 支持OnBackPressed事件处理、支持SwipeBack

  • 支持Transition、SharedElement等转场动画

  • 支持以Dialog样式显示

  • 支持Deep Links

Fragivity底层基于Navigation实现,同时兼具Fragmentation的灵活性,无需配置NavGraph即可实现画面跳转。简单对比一下三者的区别:


Fragmentation

Navigation

Fragivity

自由跳转

yes

no (依赖NavGraph)

yes

Launch Mode

3种

2种

3种

支持Deep Links

no

yes(依赖NavGraph)

yes(使用注解)

kotlin友好

no

yes

yes

生命周期

与Activity不一致(add方式)

与Activity不一致(replace方式)

与Activity一致

Fragment间通信

startFragmentForResult

viewmodel

viewmodel、callback、resultapi等多种方式

过场动画

View Animation

Transition Animation

Transition Animation

Swipe Back

yes(依赖基类)

no

yes (无需基类)

支持Dialog显示

no

yes

yes

OnBackPressed拦截

yes (依赖基类)

yes(无需基类)

yes(无需基类)

通过对比可以发现,比起前两者Fragivity在多个维度上与Activity的行为更加一致。

/   基本使用   /

Fragivity的接入成本很低。

gradle依赖

implementation 'com.github.fragivity:core:$latest_version'


声明NavHostFragment

像Navigation一样,Fragivity需要NavHostFragment作为Parent,然后在ChildFragment之间实现页面跳转。我们在xml中声明NavHostFragment:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:fitsSystemWindows="true">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true" />
</FrameLayout>

加载首页

通常我们需要定义一个MainActivity作为入口,同样,这里通过loadRoot加载一个初始的Fragment:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host) as NavHostFragment

        navHostFragment.loadRoot(HomeFragment::class)

    }
}


页面跳转

接下来便可以在Fragment之间进行跳转了:

//跳转到目标Fragment
navigator.push(DestinationFragment::class)

//携带参数跳转
val bundle = bundleOf(KEY_ARGUMENT to "some args")
navigator.push(DestinationFragment::class, bundle)

 

页面返回

通过pop方法可以返回上一页面。

//返回上一页面
navigator.pop()

//返回到指定页面
navigator.popTo(HomeFramgent::class)

转场动画

基于Navigation的能力,在画面跳转时可以设置Transition动画。

navigator.push(UserProfile::class, bundle) { //this:NavOptions
    //配置动画
    enterAnim = R.anim.enter_anim
    exitAnim = R.anim.exit_anim
    popEnterAnim = R.anim.enter_anim
    popExitAnim = R.anim.exit_anim
}

借助FragmentNavigatorExtras还可以设置SharedElement,实现更优雅地动画效果。

//跳转时,对imageView设置SharedElement
navigator.push(UserProfile::class, bundle,
               FragmentNavigatorExtras(imageView to "imageView")) { //this:NavOptions

    enterAnim = R.anim.enter_anim
    exitAnim = R.anim.exit_anim
    popEnterAnim = R.anim.enter_anim
    popExitAnim = R.anim.exit_anim

}

class UserProfile : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //目标Fragment中设置共享元素动画
        sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
    }

}

/   无需配置实现页面跳转   /

Navigation需要配置NavGraph才能实现页面间跳转,例如:

<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"
    app:startDestination="@+id/first">

    <fragment
        android:id="@+id/fragment_first"
        android:name=".FirstFagment"
        android:label="@string/tag_first">
        <action
            android:id="@+id/action_to_second"
            app:destination="@id/fragment_second"/>
    </fragment>
    <fragment
        android:id="@+id/fragment_second"
        android:name=".SecondFragment"
        android:label="@string/tag_second"/>
</navigation>

每个<navigation/>对应一个NavGraph对象,<fragment/>会对应到NavGraph中的各个Destination,NavController持有NavGraph通过控制Destination之间的跳转。

依赖配置的页面跳转,无法做到像Activity那样灵活。Fragivity通过动态构建NavGraph,无需配置即可实现跳转:

动态创建Graph

加载首页时,动态创建Graph。

fun NavHostFragment.loadRoot(root: KClass<out Fragment>) {

    navController.apply {
        //添加Navigator
        navigatorProvider.addNavigator(
            FragivityNavigator(
                context,
                childFragmentManager,
                id
            )
        )

        //创建Graph
        graph = createGraph(startDestination = startDestId) {
            val startDestId = root.hashCode()
            //添加startDestination
            destination(
                FragmentNavigatorDestinationBuilder(
                    provider[FragivityNavigator::class],
                    startDestId,
                    root
                ))
        }
    }
}

FragivityNavigator负责处理页面跳转的逻辑,后文会单独介绍。Graph创建后添加startDestination用来加载首页。

动态添加Destination

除startDestination以外,每当跳转到新页面,都需要为Graph动态添加此Destination:

fun NavHost.push(
    clazz: KClass<out Fragment>,
    args: Bundle? = null,
    extras: Navigator.Extras? = null,
    optionsBuilder: NavOptions.() -> Unit = {}
) = with(navController) {
    // 动态创建Destination
    val node = putFragment(clazz)
    // 调用NavController的navigate方法进行跳转
    navigate(
        node.id, args,
        convertNavOptions(clazz, NavOptions().apply(optionsBuilder)),
        extras
    )
}

// 创建并添加Destination
private fun NavController.putFragment(clazz: KClass<out Fragment>): FragmentNavigator.Destination {
    val destId = clazz.hashCode()
    lateinit var destination: FragmentNavigator.Destination
    if (graph.findNode(destId) == null) {
        destination = (FragmentNavigatorDestinationBuilder(
            navigatorProvider[FragivityNavigator::class],
            destId,
            clazz
        )).build()
        graph.plusAssign(destination)// 添加进Graph
    } else {
        destination = graph.findNode(destId) as FragmentNavigator.Destination
    }
    return destination
}

创建Destination后,通过NavController的navigate方法跳转到此Destination。

/   BackStack及生命周期   /

如J神所说,Fragment无法很好替代Activity的原因之一是在BackStack管理上的差异,这会影响到生命周期的不同。

设想以下场景:A页面 > (启动)> B页面 >(返回)> A页面

我们知道添加Fragment一般有两种方式:add 、 replace。无论哪种方式其在画面跳转时的生命周期与Activity都不相同:

页面B的启动方式

从B返回时的生命周期

Activity

ActivityB:onPasue -> onStop -> onDestroy
ActivityA:onStart -> onResume

Fragment(add )

FragmentB : onPause -> onStop -> onDestroy
FragmentA : no change

Fragment(replace)

FragmentB: onPause -> onStop -> onDestroy
FragmentA: onCreateView -> onStart -> onResume

如果希望在画面跳转时Fragment的生命周期与Activity行为一致,则至少需要达成以下三个目标:

  • 目标1:回退时,FragmentB不重新onCreateView (add方式满足)

  • 目标2:回退时,FragmentB会触发onStart -> onResume (replace方式满足)

  • 目标3:后台的Fragment不跟随父生命周期发生变化 (replace方式满足)

无论Navigation还是Fragmentation都不能同时满足上面三条。


重写FragmentNavigator

NavController通过FragmentNavigator实现具体的跳转逻辑,FragmentNavigator是Navigator的派生类,专门负责FragmentNavigator.Destination类型的跳转。

navigate()实现了Fragment跳转的具体逻辑,其核心代码如下:

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

    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {

        String className = destination.getClassName();

        //实例化Fragment
        final Fragment frag = instantiateFragment(mContext, mFragmentManager,
        className, args);
        frag.setArguments(args);

        final FragmentTransaction ft = mFragmentManager.beginTransaction();
        ft.replace(mContainerId, frag); // replace方式添加Fragment
        ft.setPrimaryNavigationFragment(frag);

        //事务压栈
        ft.addToBackStack(generateBackStackName(mBackStack.size() + 1, destId));

        ft.setReorderingAllowed(true);
        ft.commit();

    }

}

FragmentNavigator通过replace进行Fragment跳转,前面分析我们知道这在回退时会重新onCreateView,不符合预期。我们实现子类FragivityNavigator,重写navigate()方法,将replace改为add,避免重新onCreateView,达成“目标1”。

public class FragivityNavigator extends FragmentNavigator {
    @Override
    public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {

        final Fragment frag = instantiateFragment(mContext, mFragmentManager,
        className, args);
        //ft.replace(mContainerId, frag); // replace改为add
        ft.add(mContainerId, frag, generateBackStackName(mBackStack.size(), destination.getId()));

    }
}

添加OnBackStackChangedListener

在合适的时机为FragmentManger添加OnBackStackChangedListener,当监听到backstack变化时,手动触发生命周期回调,达成“目标2”。

private final FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener =
        new FragmentManager.OnBackStackChangedListener() {

            @Override
            public void onBackStackChanged() {
                if (mIsPendingAddToBackStackOperation) {
                    mIsPendingAddToBackStackOperation = !isBackStackEqual();

                    if (mFragmentManager.getFragments().size() > 1) {
                        // 切到后台时的生命周期
                        Fragment fragment = mFragmentManager.getFragments().get(mFragmentManager.getFragments().size() - 2);
                        if (fragment instanceof ReportFragment) {
                            fragment.performPause();
                            fragment.performStop();
                            ((ReportFragment) fragment).setShow(false);
                        }
                    }
                } else if (mIsPendingPopBackStackOperation) {
                    mIsPendingPopBackStackOperation = !isBackStackEqual();
                    // 回到前台时的生命周期
                    Fragment fragment = mFragmentManager.getPrimaryNavigationFragment();
                    if (fragment instanceof ReportFragment) {
                        ((ReportFragment) fragment).setShow(true);
                        fragment.performStart();
                        fragment.performResume();
                    }
                }
            }
        };

ReportFragment代理

为了达成“目标3”, 在实例化Fragment时,为其创建ReportFragment作为代理。所谓代理其实是通过ParentFragment对内进行生命周期的分发和控制。

//ReportFragment
internal class ReportFragment : Fragment() {

    internal lateinit var className: String
    private val _real: Class<out Fragment> by lazy {
        Class.forName(className) as Class<out Fragment>
    }
    private val _realFragment by lazy {  _real.newInstance() }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        //将目标Framgent作为child进行管理
        mChildFragmentManager.beginTransaction().apply {
            _realFragment.arguments = arguments
            add(R.id.container, _realFragment)
            commitNow()
        }
    }

}

//ReportFragmentManager
internal class ReportFragmentManager : FragmentManager() {
    //isShow:在后台时,不响应生命周期分发
    internal var isShow = true
    public override fun dispatchResume() {
        if (isShow) super.dispatchResume()
    }

    //...
}

/   支持Launch Mode   /

Fragivity支持三种LaunchMode:Standard、SingleTop、SingleTask。

启动方式非常简单:

navigator.push(LaunchModeFragment::class, bundle) { //this: NavOptions
    launchMode = LaunchMode.STANDARD // 默认可省略
    //launchMode = LaunchMode.SINGLE_TOP
    //launchMode = LaunchMode.SINGLE_TASK
}

这里着重介绍一下SingleTop的实现。Navigation也支持SingleTop,但是在Navigator中完成的,由于我们重写了Navigator(replace改为add),因此对SingleTop的实现也要做相应调整:

@Override
public NavDestination navigate(@NonNull Destination destination, @Nullable Bundle args,
            @Nullable NavOptions navOptions, @Nullable Navigator.Extras navigatorExtras) {

    final Fragment preFrag = mFragmentManager.getPrimaryNavigationFragment();

    //当以singleTop启动时
    if (isSingleTopReplacement) {
            if (mBackStack.size() > 1) {
                ft.remove(preFrag);// 删除旧实例

                //更新FragmentTransaction中的实例信息
                frag.mTag = generateBackStackName(mBackStack.size() - 1, destination.getId());
                if (mFragmentManager.mBackStack.size() > 0) {
                    List<FragmentTransaction.Op> ops =
                            mFragmentManager.mBackStack.get(mFragmentManager.mBackStack.size() - 1).mOps;
                    for (FragmentTransaction.Op op : ops) {
                        if (op.mCmd == OP_ADD && op.mFragment == preFrag) {
                            op.mFragment = frag;
                        }
                    }
                }
            }
        } 
}

SingleTop要求当栈顶类型和目标类型相同时只能存在一个实例,所以需要删除旧实例避免重复添加。同时为了保证BackStack回退时的事务行为正常,需要将添加旧实例的事务中的相关信息更新为新实例。

/   Fragment通信   /

Fragivity支持androidx.fragment的所有通信方式,例如使用ViewModel,或者使用ResultApi(Fragment 版本高于1.3.0-beta02)等。除此之外,Fragivity提供了更简单的基于Callback的通信方式:

//SourceFragment
val cb = { it : Boolean -> 
    //...
}
navigator.push {
    DestinationFragment(cb)
}

//Destination
class DestinationFragment(val cb:(Boolean) -> Unit) {...}

以前Fragment如果必须使用无参的构造函数,否则打包时会出错。感谢AndroidX带来的进步,目前已经取消了此限制,允许自定义带参数的构造函数。因此我们可以通过lambda动态创建Fragment并将callback作为构造参数传入。

inline fun <reified T : Fragment> NavHost.push(
    noinline optionsBuilder: NavOptions.() -> Unit = {},
    noinline block: () -> T
) {
    //...
    push(T::class, optionsBuilder)
}

如上,其内部仍然是使用Fragment的Class作为参数进行跳转,只是借助kotlin的reified特性,获取了泛型的Class信息而已。

/   支持DeepLinks  /

Activity可以通过URI隐式启动,为了覆盖此类使用场景,需要为Fragment提供Deep Links支持。Navigation在NavGraph中为Destination配置URI信息;Fragivity虽然没有NavGraph,但可以通过注解配置URI。基本思想类似于ARouter的路由原理:

  • 在编译期通过kapt解析注解,获取URI信息,并与Fragment相关联

  • 在Activity的入口处拦截Intent,解析URI并跳转到相关联的Fragment


添加kapt依赖

kapt 'com.github.fragivity:processor:$latest_version'

配置URI

定义Fragment时,使用@DeepLink配置URI。

const val URI = "myapp://fragitiy.github.com/"

@DeepLink(uri = URI)
class DeepLinkFragment : AbsBaseFragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_deep_link, container, false)
    }
}

处理Intent

在MainActivity入口处,处理Intent中的URI。

//MainActivity#onCreate
override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     setContentView(R.layout.activity_main)

     val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host) as NavHostFragment

     navHostFragment.handleDeepLink(intent)

}

handleDeepLink内部最终会调用NavController的相关方法对URI进行解析:

//NavController
public void navigate(@NonNull Uri deepLink) {
    navigate(new NavDeepLinkRequest(deepLink, null, null));
}

之后,我们就可以从APP外部通过URI的方式跳转到目标Fragment了:

val intent = Intent(Intent.ACTION_VIEW, Uri.parse("myapp://fragitiy.github.com/"))
startActivity(intent)

/   OnBackPressed事件拦截   /

Fragment没有Activity的OnBackPressed方法,Fragmentation通过继承的方式增加了onBackPressedSupport方法,但这会引入新的基类,对业务代码的侵入性较高。

Fragivity基于androidx.activity的OnBackPressedDispatcher,以更加无侵的方式拦截back键事件。OnBackPressedDispatcher通过责任链模式保证了back事件消费的顺序,同时感知Lifecycle,在适当的时机自动注销,避免泄露。

参考:

https://developer.android.com/guide/navigation/navigation-custom-back

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requireActivity().onBackPressedDispatcher.addCallback( this,
        object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // 拦截back键事件
            }
        })
}

back键返回与pop()返回

Fragivity提供pop方法,通过代码实现返回,其内部最终会调用Navigator#popBackStack。为了保证回退逻辑统一,我们希望back键回退时也由popBackStack统一处理。Navigation通过NavHostFragment进行了实现:

//NavHostFragment#onCreate
public void onCreate(@Nullable Bundle savedInstanceState) {
        //...
     mNavController = new NavHostController(context);
        mNavController.setOnBackPressedDispatcher(requireActivity().getOnBackPressedDispatcher());
        //...

}

//NavController#setOnBackPressedDispatcher
void setOnBackPressedDispatcher(@NonNull OnBackPressedDispatcher dispatcher) {
    if (mLifecycleOwner == null) {
        throw new IllegalStateException("You must call setLifecycleOwner() before calling "
                + "setOnBackPressedDispatcher()");
    }
    // Remove the callback from any previous dispatcher
    mOnBackPressedCallback.remove();
    // Then add it to the new dispatcher
    dispatcher.addCallback(mLifecycleOwner, mOnBackPressedCallback);
}

//NavController#mOnBackPressedCallback
private final OnBackPressedCallback mOnBackPressedCallback =
        new OnBackPressedCallback(false) {
    @Override
    public void handleOnBackPressed() {
        popBackStack(); // 最终回调Navigator#popBackStack
    }
};

/   SwipeBack   /

Navigation没有提供滑动返回的能力,我们从Fragmentation中找到解决方案:onCreateView的时候,将SwipeLayout作为Container容器。使用方式非常简单:

class SwipeBackFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_swipe_back, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        swipeBackLayout.setEnableGesture(true) //一句话开启SwipeBack
    }

}

借助ReportFragment代理,避免了额外基类的引入。swipeBackLayout是扩展属性,实际获取的是parentFragment(ReportFragment)的实例。

val Fragment.swipeBackLayout
    get() = (parentFragment as ReportFragment).swipeBackLayout

ReportFragment中的处理非常简单,将SwipeLayout作为Container即可。

internal class ReportFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        swipeBackLayout =
            SwipeBackLayout(requireContext()).apply {
                attachToFragment(
                    this@ReportFragment,
                    inflater.inflate(R.layout.report_layout, container, false)
                        .apply { appendBackground() } // add a default background color to make it opaque

                )
                setEnableGesture(false) //default false
            }
        return swipeBackLayout
    }

为了避免滑动过程中的背景穿透,调用applyBackgroud()为Fragment添加与当前主题相同的默认背景色。

private fun View.appendBackground() {
    val a: TypedArray =
        requireActivity().theme.obtainStyledAttributes(intArrayOf(android.R.attr.windowBackground))
    val background = a.getResourceId(0, 0)
    a.recycle()
    setBackgroundResource(background)
}

/   ShowDialog   /

Activity通过设置Theme可以以Dialog样式启动,使用DialogFragment同样可以实现Dialog样式的Fragment。Navigation对DialogFragment已经做了支持,Fragivity只要调用相关方法即可:

定义DialogFragment

class DialogFragment : DialogFragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_dialog, container, false)
    }
}

显示Dialog

navigator.showDialog(DialogFragment::class)

DialogFramgent也需要在Graph上动态添加Destination,只是与普通的Fragment有所区别,其配套的Navigator类型是DialogFragmentNavigator:

//创建Destination
val destination = DialogFragmentNavigatorDestinationBuilder(
       navigatorProvider[DialogFragmentNavigator::class],
       destId, clazz ).apply {
            label = clazz.qualifiedName
       }.build()

//添加到Graph      
graph.plusAssign(destination)

/   最后   /

Fragivity在核心逻辑上力求最大程度复用Navigation的能力,并保持与最新版本同步,这有利于保证框架的先进性和稳定性。同时Fragivity致力于打造与Activity相近的使用体验,以帮助开发者更低成本地转向单Activity架构。

工程源码中有本文介绍的各种API的demo,欢迎大家下载体验,提issue,觉得好用别忘了start~

Fragivity地址如下所示:

https://github.com/vitaviva/fragivity

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

Activity精选内容,看看还有没有你的盲点!

使用协程,让网络世界更加美好

欢迎关注我的公众号

学习技术或投稿

长按上图,识别图中二维码即可关注

Activity使用 Fragment 进行页面切换时,可以通过 FragmentManager 和 FragmentTransaction 来管理和执行 Fragment 的添加、移除和替换等操作。而在 Fragment使用Fragment 进行页面切换时,可以通过 getChildFragmentManager() 方法来获取子 FragmentManager,然后再像在 Activity 中一样使用 FragmentTransaction 来进行子 Fragment 的添加、移除和替换等操作。 例如,以下代码演示了在一个父 Fragment使用Fragment 进行页面切换的示例: ```java // 获取子 FragmentManager FragmentManager childFragmentManager = getChildFragmentManager(); // 创建并添加子 Fragment ChildFragment childFragment = new ChildFragment(); FragmentTransaction transaction = childFragmentManager.beginTransaction(); transaction.add(R.id.child_container, childFragment, "ChildFragment"); transaction.commit(); // 切换到另一个子 Fragment AnotherChildFragment anotherChildFragment = new AnotherChildFragment(); transaction = childFragmentManager.beginTransaction(); transaction.replace(R.id.child_container, anotherChildFragment, "AnotherChildFragment"); transaction.addToBackStack(null); transaction.commit(); ``` 在上述示例中,我们首先通过 getChildFragmentManager() 方法获取了子 FragmentManager,然后使用 FragmentTransaction 来添加了一个 ChildFragment。接着,我们又使用 FragmentTransaction 来替换 ChildFragment 为 AnotherChildFragment,并将其加入回退栈中,以便用户可以通过返回键回到上一个子 Fragment
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值