[toc]
基本概念
介绍
可以把 Fragment 想象成一个小型的 Activity,它通常管理一个独立的部分 UI,并处理与该 UI 相关的用户交互。你可以把Fragment看作是管理一部分独立UI的小型的Activity。
当其存在时,Activity 提供了一个容器(fragment_container),Fragment 在这个容器中显示具体内容,并负责处理用户交互。
特性
依赖于 Activity:Fragment 不能独立存在,它必须依附于 Activity。
一个 Activity 里可以包含多个 Fragment:这使得 Fragment 成为创建多窗口、多面板用户界面的理想工具,尤其是在大屏设备(如平板电脑)上表现优异。
可重用性:同一个 Fragment 可以在多个 Activity 中复用,降低代码冗余,增强应用的灵活性。
独立的生命周期和事件处理:Fragment 拥有自己的生命周期方法(类似 Activity),并且可以接收用户的输入事件,这使得它能独立管理自己的视图和行为。
动态管理:在 Activity 运行时,可以通过 FragmentManager 动态添加、移除或替换 Fragment,使得界面能够根据用户操作进行灵活调整。
Fragment核心类
Fragment:
Fragment是所有Fragment的基类。任何Fragment都必须继承自这个类。Fragment提供了生命周期方法和处理视图、交互等相关的功能。
FragmentManager:
FragmentManager负责管理和维护Fragment,例如添加、移除、替换Fragment等操作。FragmentManager是一个抽象类,其具体实现类为FragmentManagerImpl。
FragmentTransaction:
- 对
Fragment的任何操作(如添加、移除、替换)都必须通过FragmentTransaction完成。FragmentTransaction是一个抽象类,具体实现类为BackStackRecord。通过事务的方式来确保Fragment的操作是原子性的,并能够将操作添加到返回栈中。
嵌套 Fragment:
- 从 Android 4.2 开始,
Fragment支持嵌套其他Fragment,允许在一个Fragment中嵌套子Fragment。通过getChildFragmentManager()可以获得管理子Fragment的FragmentManager。同时,子Fragment可以通过getParentFragment()获取到它的父Fragment。
补充:
- 在
Activity中操作Fragment时,需要使用SupportFragmentManager,它提供了一组 API 来管理Fragment的事务。 - 在
Fragment内部嵌套子Fragment时,则使用ChildFragmentManager来进行子Fragment的管理。 - 如果需要获取父
Fragment,可以通过getParentFragment()来找到当前Fragment的父Fragment。 - 具体的
Fragment操作(如添加、移除、替换等)都是通过FragmentManager中的FragmentTransaction类来实现的,它保证了对Fragment的操作以事务的方式进行,从而保持操作的原子性和一致性。
Fragmentmanager拥有FragmentTransaction
getSupportFragmentManager与 activity关联,可以将其视为 activity的FragmentManager
getChildFragmentManager与fragment关联,可以将其视为fragment的FragmentManager
getParentFragmentManager情况稍微复杂,正常情况返回的是该fragment 依附的activity的FragmentManager。如果该fragment是另一个fragment的子fragment,则返回的是其父fragment的 getChildFragmentManager
因此
- 在
activity中使用ViewPager,BottomSheetFragment和DialogFragment时,都应使用getSupportFragmentManager - 在
fragment中使用ViewPager时应该使用getChildFragmentManager
基本使用
静态添加 Fragment
-
方式:通过 XML 直接在布局文件中定义 Fragment。
<fragment android:id="@+id/fragment1" android:name="com.example.Fragment1" android:layout_width="match_parent" android:layout_height="match_parent" /> -
缺点:一旦
Fragment被添加,就无法在运行时动态移除或替换。它的存在是固定的,不能满足动态调整 UI 的需求。
动态添加 Fragment**
动态添加则允许我们在运行时对 Fragment 进行灵活的操作,如添加、移除、替换、加入返回栈等。
1. 在 Activity 布局中添加一个 FrameLayout 容器
在布局文件中,通常我们会用一个 FrameLayout 作为 Fragment 的容器。
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
2. 在 onCreate() 中动态添加 Fragment
在 Activity 的 onCreate() 方法中,通过 FragmentManager 和 FragmentTransaction 将 Fragment 添加到 FrameLayout 中。
if (savedInstanceState == null) { // 防止Fragment重叠问题
getSupportFragmentManager().beginTransaction()
.add(R.id.container, Fragment1.newInstance("hello world"), "f1")
//.addToBackStack("fname") // 可选的回退栈操作
.commit();
}
注意事项:
getSupportFragmentManager()- 因为使用了
support库的Fragment,所以需要使用getSupportFragmentManager()获取FragmentManager。如果是原生的Fragment,使用getFragmentManager()。
- 因为使用了
- **
add()、remove()、replace()**这些是对 Fragment 的操作方法:add():将Fragment添加到指定的容器中。remove():将Fragment从容器中移除。replace():替换容器中的Fragment。
commit()和commitNow()commit()是异步的,会将事务加入队列,稍后处理。commitNow()是同步的,立即执行事务。
addToBackStack()- 使用
addToBackStack()可以将事务添加到返回栈中,当用户按下返回按钮时,会撤销这个事务。如果不使用addToBackStack(),按返回键会直接关闭Activity。
- 使用
生命周期
基本生命周期如下:
onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
onCreate():Fragment被创建时调用。
onCreateView():创建Fragment的布局。
onActivityCreated():当Activity完成onCreate()时调用。
onStart():当Fragment可见时调用。
onResume():当Fragment可见且可交互时调用。
onPause():当Fragment不可交互但可见时调用。
onStop():当Fragment不可见时调用。
onDestroyView():当Fragment的UI从视图结构中移除时调用。
onDestroy():销毁Fragment时调用。
onDetach():当Fragment和Activity解除关联时调用。
和Activity生命周期的关系
如下图:
Activity 启动时:
- 当
Activity启动时,它首先进入onCreate(),此时Fragment也会被添加,进入onAttach()和onCreate()阶段。 - 在
Activity的onCreate()中,Fragment的视图会通过onCreateView()方法被创建。
Activity 进入 onStart() 和 onResume():
- 当
Activity进入onStart()和onResume()时,Fragment也会依次进入onStart()和onResume()。 - 在这两个阶段,
Activity和Fragment都会变得可见且可以与用户交互。
当 Activity 被暂停时 (onPause()):
- 如果
Activity进入onPause(),意味着它不再处于前台,Fragment也会进入onPause()阶段,停止与用户的交互。
Activity 停止时 (onStop()):
- 当
Activity不再可见并进入onStop()阶段时,Fragment也会进入onStop(),停止所有前台操作。
Activity 被销毁时 (onDestroy()):
- 如果
Activity被销毁,它会调用onDestroy(),此时Fragment也会相应地进入onDestroyView(),销毁其视图。 - 接着,
Fragment会进入onDestroy(),然后执行onDetach()与Activity解除关联。
Fragment 添加到返回栈中的情况
假设有两个 Fragment:F1 和 F2。
F1:在Activity的onCreate()中被添加。F2:在点击F1的按钮时,通过replace()方法将F1替换为F2。
在不使用addToBackStack()时,此时使用 replace() 替换 F1 ,由于没有添加到返回栈,F1 将会执行完整的销毁流程,包括 onDestroyView()、onDestroy() 和 onDetach(),意味着该 Fragment 被彻底移除。
在使用addToBackStack()时,Fragment生命周期会产生些许变动。F1 并未完全销毁。它只执行到了 onDestroyView(),意味着其视图被移除,但 Fragment 实例仍然被保留在内存中。onDestroy() 和 onDetach() 并不会被调用。此时当用户按下返回按钮,F1 会从返回栈中恢复,重新调用 onCreateView()、onStart() 和 onResume(),从而恢复到之前的状态。
FragmentTransaction 的基本方法与生命周期
add():会从onAttach()开始,直到onResume()结束。这个方法用于将Fragment添加到布局中。remove():会触发onPause()->onStop()->onDestroyView()->onDestroy()->onDetach()。这个方法用于将Fragment从布局中移除,并销毁它。replace():相当于调用了先移除旧Fragment(remove()),再添加新Fragment(add()) 的操作,因此两个Fragment的生命周期都会受到影响。show()和hide():不调用任何生命周期方法,只是改变Fragment的setVisibility(),用于显示或隐藏已添加的Fragment。detach():会调用onPause()->onStop()->onDestroyView(),但Fragment实例仍然存在于FragmentManager中,只是视图被销毁了。attach():当重新关联已被detach()的Fragment时,会重新创建视图,执行onCreateView()->onStart()->onResume()。
Fragment通信
1. Fragment 向 Activity 传递数据
通过接口通信
-
定义接口:在
Fragment中定义一个接口,并让宿主Activity实现该接口,用于从Fragment向Activity传递数据。 -
在
onAttach()中设置接口: 在Fragment的onAttach()方法中,将Context转换为接口的实现,确保Activity正确实现了该接口。public interface OnFragmentInteractionListener { void onItemClick(String str); } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof OnFragmentInteractionListener) { mListener = (OnFragmentInteractionListener) context; } else { throw new RuntimeException(context.toString() + " must implement OnFragmentInteractionListener"); } } -
调用接口方法传递数据: 在合适的地方调用接口方法,将数据从
Fragment传递到Activity。mListener.onItemClick("hello");
使用 FABridge 进行通信
-
FABridge 简化通信: 使用
FABridge可以通过注解的方式简化Fragment与Activity之间的数据传递,避免手动定义接口。- 添加依赖: 在
build.gradle中添加FABridge依赖。 - 在
Activity中定义方法: 使用@FCallbackId注解为Activity中的方法定义一个 ID。
@FCallbackId(id = FAB_ITEM_CLICK) public void onItemClick(String str) { Toast.makeText(this, str, Toast.LENGTH_SHORT).show(); }- 在
Fragment中调用: 使用Fabridge.call()方法调用指定的 ID 对应的方法,并传递数据。
Fabridge.call(mActivity, FAB_ITEM_CLICK, "data"); - 添加依赖: 在
2. Activity 向 Fragment 传递数据
-
通过调用
Fragment的方法传递数据: 在Fragment中定义公开的方法,Activity可以通过调用这些方法向Fragment传递数据。public void setString(String str) { this.str = str; }然后在
Activity中调用这个方法:fragment.setString("hello");
3. Fragment 之间的通信
- 通过
Activity作为中介: 由于Fragment之间没有直接的依赖关系,建议通过Activity作为中介进行通信。- Fragment A 向 Activity 发送数据: 通过接口或其他方法传递数据给
Activity。 - Activity 向 Fragment B 发送数据:
Activity接收到数据后,再调用Fragment B的方法,完成数据的传递。
- Fragment A 向 Activity 发送数据: 通过接口或其他方法传递数据给
ViewPager+Fragment相关
ViewPager是一个用于实现界面滑动切换的ViewGroup,通常用于结合Fragment来显示多个页面。- 适配器选择:
FragmentPagerAdapter和FragmentStatePagerAdapter是ViewPager的两个常用适配器类,它们专门用于管理Fragment页面。FragmentPagerAdapter:适用于页面数量较少的情况,适配器会将所有Fragment保存在内存中,即使页面不可见时,Fragment实例也不会被销毁。FragmentStatePagerAdapter:适用于页面数量较多的情况,不可见的Fragment会被销毁,只保留其状态,这样可以节省内存。
常见的重写方法:
getItem(int position):返回指定位置的Fragment,必须重写。getCount():返回页面的总数,必须重写。instantiateItem()和destroyItem():管理Fragment的实例化和销毁。getItemPosition():决定Fragment的刷新策略,如果返回POSITION_UNCHANGED,表示不刷新;返回POSITION_NONE表示该Fragment需要重新创建。