什么是fragment
fragment是一种可嵌入在Activity中的UI片段,它能让程序更加合理和充分地利用大屏幕地空间,因而在平板上应用得非常广泛。其依靠activity实现,离开activity无法存活,也无法离开。可视为 “子 Activity”。
简单举个例子,一个应用利用listView将所有地数据链接竖状排列展示,但是在平板上可能还有其余地空间,如果只是展示简短地数据链接,那么非常容易浪费空间,因为我们可以左侧展示数据连接,右边展示具体地详情页。这样空间就合理利用起来了。如图所示。
所以就产生了fragment,实现屏幕空间地合理利用化。
- 核心作用:
- 实现界面模块化(如将 Activity 拆分为列表页、详情页 Fragment)。
- 支持动态切换界面(如底部导航栏切换不同 Fragment),提升用户体验。
- 适配不同屏幕尺寸(如平板端使用 Fragment 组合布局,手机端使用单 Fragment)。
1.fragment的简单使用
实现效果,在Activity中嵌入两个fragment
1.首先,创建两个fragment,并让其平分屏幕空间,分别命名为left_fragment.xml和right_fragment.xml.
left_fragment.xml代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Button"/> </LinearLayout>
right_fragment.xml代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:background="#00ff00" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="24sp" android:text="This is right fragment" /> </LinearLayout>
2.再分别新建fragment对应的类
注意:此处代码中继承fragment,要继承自androidx.fragment.app.Fragment,它可以做到让Fragment的特性在所有Android系统版本中保持一致,系统内置的Fragment在Android9.0版本已经废除
对inflate方法说明,i
nflater.inflate()
方法将R.layout.left_fragment
对应的 XML 布局文件实例化为 View 对象。参数false
表示不要立即将加载的布局添加到container
中,而是由系统稍后处理。这是 Fragment 的标准做法,避免重复添加 View 导致异常。View 被添加两次,进而引发IllegalStateException
异常。class LeftFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.left_fragment, container, false) } }
class RightFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.right_fragment, container, false) } }
3.创建Anctivity
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment android:id="@+id/leftFrag" android:name="com.example.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:id="@+id/rightFrag" android:name="com.example.fragmenttest.RightFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
现在我们启动后,两个fragment就被我们成功嵌入到activity中了。
2.动态添加fragment
什么叫动态添加呢,它其实指的是在程序运行时动态添加到Activity中,根据实际情况去添加,而不是我们上面简单实现的固定嵌入。
以上面代码为例,那么我们再新增一个fragment,起名为anthoer_right_fragment.xml,代码的布局文件一致。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:background="#ffff00" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="24sp" android:text="This is another right fragment"/> </LinearLayout>
创建对应的fragment的类:
class AnotherRightFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.another_right_fragment, container, false) } }
修改Activity中的布局文件,之前我们嵌入的是fragment,那么它就是固定死的,此处我们更改下方一个为framLayout。是Android中最简单的一种布局,所有的控件默认都会摆放在布局的左上角。由 于这里仅需要在布局里放入一个Fragment,不需要任何定位,因此非常适合使用 FrameLayout。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment android:id="@+id/leftFrag" android:name="com.example.fragmenttest.LeftFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <FrameLayout android:id="@+id/rightLayout" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" > </FrameLayout> </LinearLayout>
此处再Activity中向Framelayout添加内容,即可实现动态添加Fragment的功能。此处通过设置left_fragment.xml中的按钮的点击事件,然后调用replaceFragment方法将右侧fragment替换。
对OnCreate()方法解释:首先Activity创建后,执行该方法,按顺序执行,加载布局,设置按钮定义事件(不立即执行),然后加载right_fragment.xml,
对replaceFragment()方法解释:自定义方法,首先通过supportFragmentManager获取fragmentManager管理对象,然后开启事务,调用replace方法替换,传入要容纳frgament的容器视图,以及携带参数(即要放置的fragment),提交事务即可。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener { replaceFragment(AnotherRightFragment()) } replaceFragment(RightFragment()) } private fun replaceFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction = fragmentManager.beginTransaction() transaction.replace(R.id.rightLayout, fragment) transaction.commit() } }
3.在fragment中实现返回栈
在 Android 开发中,Fragment 返回栈是由
FragmentManager
管理的一个后进先出(LIFO)的 Fragment 事务队列。它的主要作用是:
- 允许用户通过点击「返回键」回退到之前显示的 Fragment
- 保存 Fragment 切换的历史记录,避免重复创建 Fragment 实例
- 实现类似 Activity 栈的导航逻辑,但更轻量级
栈的结构
返回栈中存储的是 Fragment 事务(Transaction),而非 Fragment 本身。每个事务记录了添加、替换或移除 Fragment 的操作。如下述代码实现返回栈
class MainActivity : AppCompatActivity() { ... private fun replaceFragment(fragment: Fragment) { val fragmentManager = supportFragmentManager val transaction = fragmentManager.beginTransaction() transaction.replace(R.id.rightLayout, fragment) transaction.addToBackStack(null)// 可选参数:栈条目标签 transaction.commit() } }
出栈行为
- 用户点击返回键时,栈顶事务会被弹出,系统回退到前一个状态
- 出栈时会触发 Fragment 的生命周期回调(如
onDestroyView()
、onCreateView()
)- 若栈为空,返回键会退出 Activity
4.fragment和activity之间的交互
此处说明,因为fragment依赖activity,并且activity需要fragment实现多样性,合理使用屏幕等。两者联系紧密,那么肯定离不开交互,那么如何交互呢
1.在activity中获取fragment的实例:
了方便Fragment和Activity之间进行交互,FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取Fragment的实例val fragment = supportFragmentManager.findFragmentById(R.id.leftFrag) as LeftFragment
2.在fragment中获取activity实例
在每个Fragment中都可以通过调用getActivity()方法来得到 和当前Fragment相关联的Activity实例,这里由于getActivity()方法有可能返回null,因此我们需要先进行一个判空处理。有了Activity的实例另外当Fragment 中需要使用Context对象时,也可以使用getActivity()方法,因为获取到的Activity本身就 是一个Context对象。if (activity != null) { val mainActivity = activity as MainActivity }
3.fragment和fragment交互
首先在一个Fragment中 可以得到与它相关联的Activity,然后再通过这个Activity去获取另外一个Fragment的实例, 这样就实现了不同Fragment之间的通信功能。