0
点赞
收藏
分享

微信扫一扫

【Android】Fragment 浅析

艾米吖 2022-04-14 阅读 80
android

Android Fragment 浅析

Fragment 是在 Android 3.0 (API level 11) 开始引入的。每个 Fragment 拥有自己的布局以及生命周期。

Fragment不能独立存在,必须依赖于Activity。一个Activity里可以有多个Fragment,并且一个Fragment可以被多个Activity重用。

Fragment 的基本使用

首先需要创建一个 Fragment,代码如下:

private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"

class BlankFragment : Fragment(R.layout.fragment_blank) {
    private var param1: String? = null
    private var param2: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            param1 = it.getString(ARG_PARAM1)
            param2 = it.getString(ARG_PARAM2)
        }
    }

    companion object {
        fun newInstance(param1: String, param2: String) =
            BlankFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_PARAM1, param1)
                    putString(ARG_PARAM2, param2)
                }
            }
    }
}

接下来编写 Activity 代码,并在 onCreate() 中添加一个 Fragment,代码如下:

class TestActivity : AppCompatActivity() {
    private val binding by lazy(LazyThreadSafetyMode.NONE) {
        ActivityTestBinding.inflate(layoutInflater)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        // add Fragment;这是 fragment-ktx 为我们提供的扩展函数
        supportFragmentManager.commit {
            add(R.id.frag, BlankFragment.newInstance("p1", "p2"))
        }
        // add Fragment (传统的add Fragment方式)
        /* supportFragmentManager.beginTransaction()
            .add(R.id.fragmentContainerView, BlankFragment.newInstance("p1", "p2"))
            .commit() */
    }
}

Activity 对应的 xml 布局代码如下,里面编写了一个 FragmentContainerView ,这是:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=".TestActivity">
    
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

ok,通过以上代码,我们就在 Activity上能正常展示 Fragment 了。

Fragment 的容器

过去我们使用 FrameLayout 作为 Fragment 的容器,在 AndroidX Fragment 1.2.0 后,我们可以使用 FragmentContainerView 来代替 FrameLayout

FragmentContainerView 是专门为 Fragment 设计的 View,它继承自 FrameLayout。

同时,FragmentContainerView 提供了一些属性,能够让我们更够已静态代码的方式来展示 Fragment,

  • android:name:可以指定容器上需要添加的 Fragment
  • android:tag:可以为 Fragment 设置 tag
<androidx.fragment.app.FragmentContainerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/fragment_container_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.example.MyFragment"
        android:tag="my_tag">
 </androidx.fragment.app.FragmentContainerView>

FragmentManager

FragmentManager 负责管理 Activity 或 Fragment 中的 所有 Fragment,它是一个抽象类,用于创建 添加,移除,替换 等 Fragment 的事务(Transaction)。

想要动态的添加一个 Fragment,第一步就是要获取 FragmentManager 。

那么,如何获取 FragmentManager呢 ?

  • 调用 FragmentActivitygetSupportFragmentManager() 方法:返回 ActivityFragmentManager,当 Activity 需要嵌套 Fragment 时调用。
  • 调用 FragmentgetChildFragmentManager() 方法:返回 当前FragmentFragmentManager,当 当前Fragment 需要嵌套 子Fragment 时调用。
  • 调用 FragmentgetParentFragmentManager() 方法:如果当前 Fragment 附属于 Activity ,则该方法返回的是 Activity 的 FragmentManager ;如果当前 Fragment 是另一个 Fragment 的 子Fragment ,则返回的是其 父Fragment 的 ChildFragmentManager。

FragmentTranscation

在 FragmentManager 中,对 Fragment 的所有操作都是通过 FragmentTransaction 来执行的。

添加 / 移除 操作

add()是 Fragment 众多操作中的一种,与之同级的方法还有remove(), replace(), hide() 等,

add()第一个参数是承载 Fragment 的容器的 id ,第二个参数是Fragment对象,第三个参数是fragment的 Tag 名。

指定 Tag 的好处是后续我们可以通过以下代码从 FragmentManager 中取得 Fragment 对象:

val mFragment = supportFragmentManager.findFragmentByTag("my_fragment_tag")

在一次事务中,可以做多个操作,比如同时做add() / remove() / replace()

remove() 方法会使 Fragment 的生命周期执行完 onDetach,之后Fragment的实例也会从 FragmentManager 中移除。

add 和 replace 的区别

  • add() 会将一个 Fragment 添加到容器顶部,且不会移除之前已添加的 Fragment
  • replace() 会替换容器顶部的一个 Fragment

如果当前已有一个 FragmentA 并且通过 add() 方法添加了一个 FragmentB ,则 FragmentA 将仍处于活动状态,因此,按下返回按钮时, FragmentA 不会调用 onCreateView方法

如果创建了 FragmentB 并 replace 顶部的 FragmentA ,则 FragmentA 将被从容器中删除(这时 FragmentA 会执行onDestroy),而 FragmentB 将位于顶部。

另外,如果我们的应用有内存限制,我们需要考虑使用 replace 而不是 add。

提交 操作

每次事务,我们可以通过commit()方法进行提交。

commit() 操作是异步的,内部通过mManager.enqueueAction() 加入处理队列。

commit() 对应的同步方法为commitNow()

commit()内部会有checkStateLoss()操作,如果我们使用不当,比如 commit() 操作在 onSaveInstanceState() 之后,将会会抛出异常,而 commitAllowingStateLoss() 方法则是不会抛出异常版本的commit()方法,但是我们还是应该尽量使用commit(),将开发中的问题提前暴露出来。

添加回退栈

FragmentManager 拥有回退栈(即BackStack),类似于Activity的任务栈。

通过 addToBackStack() 这个方法可以将当前事务加入回退栈,当用户点击返回按钮,会回退该事务,

而如果我们没添加 addToBackStack() 方法,则用户点击返回按钮会直接销毁 Activity

Fragment 的生命周期

Fragment 的生命周期方法一共有 11 个,如图所示:

在这里插入图片描述

我们来看看他们分别对应于什么情况,以及他们的作用:

1、onAttach(context: Context)

2、onCreate(Bundle savedInstanceState)

3、onCreateView()

4、onActivityCreated()

5、onStart()

6、onResume()

7、onPause()

8、onStop()

9、onDestroyView()

10、onDestroy()

11、onDetach()

那么在一些操作场景下,它的生命周期是怎么走的呢?我们来看看。

调用 show() 和 hide() 方法时,

Fragment的正常生命周期方法并不会被执行,此时仅仅是 Fragment 的 View 被显示或者隐藏

调用 add() 方法

onAttach -> onCreate -> onCreateView -> onViewCreated -> onActivityCreated -> onStart -> onResume

调用 remove() 方法

onPause -> onStop -> onDestroyView -> onDestroy -> onDetach

调用 replace() 方法

其实就是执行上一个 Fragment 的 remove() 的生命周期 加上 下一个 Fragment 的 add() 的生命周期

调用 detach() 方法时,Fragment 的生命周期如下:

onPause() -> onStop() -> onDestroyView() 
// 注意,此时并不会执行 `onDestroy()` 和 `onDetach()` ,也就是说 Fragment 的实例还是存在的。

重新调用 attach() 方法后,生命周期如下:

onCreateView() -> onViewCreated() -> onActivityCreated() -> onStart() -> onResume()

由此我们也可以看得出来 add 和 attach 的区别:

当我们按下 home 键回到桌面时,生命周期如下:

onPause() —> onStop()

当我们从桌面回到 Fragment 时,生命周期如下:

onStart() —> onResume()

举报

相关推荐

0 条评论