Fragment概述

Fragment是一种可以嵌入到Activity中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间,出现的初衷是为了适应大屏幕的平板电脑,可以将其称为一个小型Activity,又称作Ativity片段。

使用Fragment可以把屏幕划分为几块,然后进行分组,进行模块化管理。Fragment不能单独使用,必须由Activity或另一个Fragment托管。

例如,一个响应各种屏幕尺寸的应用程序。在大屏幕上,采用一个静态抽屉式导航栏和一个网格布局的列表。在小屏幕上,采用一个底部导航栏和一个线性布局的列表。在Activity中管理所有这些变化因素会很复杂。将导航元和内容分离,更易于管理。Activity负责显示正确的导航界面,而Fragment采用适当的布局显示列表。如下图:

同一界面采用不同屏幕尺寸的两个版本

 Fragment的特点:

  • Fragment不能独立存在,必须由Activity或另一个Fragment托管
  • 一个Activity里可以有多个Fragment
  • 一个Fragment可以被多个Activity重用
  • Fragment定义和管理自己的布局,有自己的生命周期,并能处理自己的输入事件
  • 能在Activity运行时动态地添加或删除Fragment
  • 应该只为Fragment提供管理它自己的界面所需的逻辑,应避免让一个Fragment依赖于另一个Fragment或从一个Fragment操控另一个Fragment

Fragment的优势:

  • 模块化:我们不必把所有代码全部写在Activity中,而是把代码写在各自的Fragment中
  • 可重用:多个Activity可以重复使用一个Fragment
  • 可适配:根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,用户拥有更好的使用体验。

Fragment生命周期

fragment的生命周期

 

 Fragment的生命周期以及状态之间的转换:

fragment生命周期及状态之间的转换

 

fragment生命周期解析:

onAttach()

当fragment被添加到activity时调用,只调用一次。

这是fragment和activity还没有完全初始化。通常,在该方法中获取到activity的引用,在fragment将来的初始化工作中被使用。

onCreate()

创建fragment时调用,只调用一次。在这里可以实例化一些必要的变量、组件,主要是当fragment被暂停 停止时想要保持的数据,以便被恢复。

如果我们要为fragment启动一个后台线程,可以考虑将代码放于此处。

Bundle savedInstance,用于保存Fragment参数,

Fragment也可以重写onSaveInstanceState(Bundle outState)方法,保存fragment状态

onCreateView()

每次创建、绘制fragment的View时,调用。要返回一个代表fragment根布局的View组件,如果该fragment不提供用户界面,直接返回null。

当系统用到fragment的时候,fragment就要返回它的view,越快越好。

所以尽量在不要在这里做耗时操作,比如从数据库加载大量数据显示listView,当然线程还是可以的。

给当前的fragment绘制ui布局,可以使用线程更新ui。

onActivityCreated()

当fragment所在的activity被启动完成时调用,也就是Activity中的onCreate方法执行完后调用。所以与Activity中UI交互操作可以在onActivityCreated()里面进行。

这个方法主要是初始化那些需要父Activity或者Fragment的UI已经被完整初始化才能初始化的元素。

onStart()

fragment可见时调用该方法

onResume()

fragment可交互时调用该方法

onPause()

fragment转到后台或者被删除/替换 时调用该方法。

onStop()

fragment将要被停止时调用。

可能的情况:activity被stopped了 或者 fragment被移除但被加入到回退栈中。

一个stopped的fragment仍然时活着的如果长时间不要也会被移除

onDestroyView()

销毁fragment中的view时调用。表示fragment销毁相关联的UI布局清除所以跟视图相关的资源

onDestroy()

销毁fragment对象,跟activity类似

onDetach()

fragment和activity解除关联的时候调用,只调用一次。

Fragment基本使用

配置环境

Fragment依赖于AndroidX Fragment库,因此需要将Google Maven存储库添加到project build.gradle文件,以便包含这个依赖项。

buildscript {
    ...

    repositories {
        google()
        ...
    }
}

allprojects {
    repositories {
        google()
        ...
    }
}

为了将AndroidX Fragment库包含在项目中,接着在app的build.gradle文中添加以下依赖项:

dependencies {
    val fragment_version = "1.3.6"
    // Java 
    implementation("androidx.fragment:fragment:$fragment_version")
    // Kotlin
    implementation("androidx.fragment:fragment-ktx:$fragment_version")
}

创建Fragment类

自定义Fragment类,继承自Fragment类或其子类

class ExampleFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_example, container, false)
    }
}

定义Fragment的XML布局文件:

<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=".ExampleFragment">
    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_blank_fragment"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

将Fragment添加到Activity中

有静态和动态两种方式添加fragment,这两种方式都需要在Activity的布局文件中添加FragmentContainerView,它是fragment的容器。

静态加载Fragment

直接在activity_main.xml文件中声明添加fragment,给FragmentContainerView的android:name属性指定要实例化的Fragment的类名。当activity的layout被加载之后,指定的fragment会被实例化。代码如下:

<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=".MainActivity">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.legalsoft.myfirstapp.ExampleFragment"/>
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt的代码:

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

动态加载Fragment

通过编程的方式在MainActivity中添加fragment。仍然需要在activity_main.xml文件中使用FragmentContainerView充当Fragment的容器,和静态加载不同,这里不指定android:name的值,所以不会自动实例化fragment。而是使用FragmentTransaction实例化fragment并将其添加到activity。

activity_main.xml:

<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=".MainActivity">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if(savedInstanceState == null){
            supportFragmentManager.commit {
                setReorderingAllowed(true)
                add<ExampleFragment>(R.id.fragment_container_view)
            }
        }
    }
}

上例中,只有当savedInstanceState为null时才创建fragment transaction。这能够确保fragment只有在activity第一次创建的时候才会被添加,确保之添加一次。当配置更改(比如横竖屏变化、多窗口模式等)activity重新创建时,savedInstanceState不再为null,fragment不需要第二次被添加,因为fragment会自动从savedInstanceState中恢复。

如果fragment需要一些初始数据,可以通过调用FragmentTransaction.add(),将参数通过 Bundle传递到fragment,如下所示:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if(savedInstanceState == null){
            val bundle = bundleOf("some_int" to 0)
            supportFragmentManager.commit {
                setReorderingAllowed(true)
                add<ExampleFragment>(R.id.fragment_container_view,args = bundle)
            }
        }
    }
}

然后通过调用requireArguments()从fragment中获取Bundle,并且可以使用相应的getter方法来

获取每一个参数。

class ExampleFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val someInt = requireArguments().get("some_int")
       ...
    }
}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐