-

「Generated by Manus, instructions issued by binbinwang」

本章将深入探讨Android的Fragment组件及其在界面复用中的应用,并与iOS的对应概念进行对比。Fragment是Android UI开发中非常重要的组件,掌握它对于构建灵活、可复用的界面至关重要。

5.1 Fragment基础

Fragment概念与作用

Fragment是Activity中的一个行为或界面部分,可以将其视为”子Activity”。它有自己的生命周期,接收自己的输入事件,可以在Activity运行时添加或移除。

Fragment的主要特点

  1. 模块化:将复杂界面分解为多个可管理的部分
  2. 可复用:同一个Fragment可在不同Activity中使用
  3. 适应性:可根据屏幕尺寸动态调整界面布局
  4. 生命周期:拥有自己的生命周期,但受宿主Activity影响

Fragment与iOS对应概念对比

Android Fragment iOS对应概念
Fragment UIViewController的容器视图控制器模式
FragmentManager UINavigationController/UISplitViewController
Fragment事务 子视图控制器的添加/移除
Fragment生命周期 子视图控制器的生命周期回调

iOS没有与Fragment完全对应的概念,但通常使用容器视图控制器(如UINavigationController、UISplitViewController)或自定义容器视图控制器实现类似功能。

创建与使用Fragment

创建基本Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义Fragment类
class ExampleFragment : Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 加载Fragment布局
return inflater.inflate(R.layout.fragment_example, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// 初始化视图
val textView = view.findViewById<TextView>(R.id.text_view)
val button = view.findViewById<Button>(R.id.button)

button.setOnClickListener {
textView.text = "Button clicked!"
}
}
}

Fragment布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- fragment_example.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">

<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Example Fragment"
android:textSize="18sp" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Click Me" />

</LinearLayout>

在Activity中使用Fragment

  1. 静态添加(通过XML)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:name="com.example.app.ExampleFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</LinearLayout>
  1. 动态添加(通过代码)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// 只在首次创建时添加Fragment,避免重复添加
if (savedInstanceState == null) {
val fragment = ExampleFragment()

// 创建Fragment事务
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit()
}
}
}

与iOS容器视图控制器对比

iOS中的容器视图控制器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 自定义容器视图控制器
@interface ContainerViewController : UIViewController

@property (nonatomic, strong) UIViewController *contentViewController;

- (void)displayContentController:(UIViewController *)content;
- (void)hideContentController:(UIViewController *)content;

@end

@implementation ContainerViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 初始化视图
self.view.backgroundColor = [UIColor whiteColor];

// 添加初始内容控制器
ExampleViewController *initialContent = [[ExampleViewController alloc] init];
[self displayContentController:initialContent];
}

- (void)displayContentController:(UIViewController *)content {
[self addChildViewController:content];
content.view.frame = self.view.bounds;
[self.view addSubview:content.view];
[content didMoveToParentViewController:self];

self.contentViewController = content;
}

- (void)hideContentController:(UIViewController *)content {
[content willMoveToParentViewController:nil];
[content.view removeFromSuperview];
[content removeFromParentViewController];
}

- (void)switchToNewContent {
UIViewController *oldContent = self.contentViewController;
NewViewController *newContent = [[NewViewController alloc] init];

[self hideContentController:oldContent];
[self displayContentController:newContent];
}

@end

主要差异

  1. Android的Fragment是专门设计的UI组件,而iOS使用通用的视图控制器容器模式
  2. Android的FragmentManager提供了专门的事务API,而iOS使用父子视图控制器关系API
  3. Android的Fragment有更复杂的生命周期,而iOS的子视图控制器生命周期相对简单
  4. Android的Fragment可以有回退栈,而iOS通常使用UINavigationController管理导航栈
  5. Android的Fragment可以保留实例状态,而iOS需要开发者手动管理状态保存

5.2 Fragment生命周期

生命周期回调方法

Fragment的生命周期比Activity更复杂,因为它不仅受系统影响,还受宿主Activity的影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
class LifecycleFragment : Fragment() {

// 创建Fragment实例时调用
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Lifecycle", "Fragment onCreate")

// 可以获取参数
arguments?.let {
val param1 = it.getString("param1")
// 使用参数...
}

// 注意:此时视图尚未创建
}

// 创建Fragment视图时调用
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("Lifecycle", "Fragment onCreateView")
return inflater.inflate(R.layout.fragment_lifecycle, container, false)
}

// 视图创建完成后调用
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("Lifecycle", "Fragment onViewCreated")

// 初始化视图组件
}

// Fragment可见时调用
override fun onStart() {
super.onStart()
Log.d("Lifecycle", "Fragment onStart")
}

// Fragment获取焦点,可与用户交互时调用
override fun onResume() {
super.onResume()
Log.d("Lifecycle", "Fragment onResume")
}

// Fragment即将失去焦点时调用
override fun onPause() {
super.onPause()
Log.d("Lifecycle", "Fragment onPause")
}

// Fragment不再可见时调用
override fun onStop() {
super.onStop()
Log.d("Lifecycle", "Fragment onStop")
}

// Fragment视图被销毁时调用
override fun onDestroyView() {
super.onDestroyView()
Log.d("Lifecycle", "Fragment onDestroyView")

// 清理视图引用
}

// Fragment实例被销毁时调用
override fun onDestroy() {
super.onDestroy()
Log.d("Lifecycle", "Fragment onDestroy")
}

// Fragment与Activity解除关联时调用
override fun onDetach() {
super.onDetach()
Log.d("Lifecycle", "Fragment onDetach")
}

// Fragment与Activity关联时调用
override fun onAttach(context: Context) {
super.onAttach(context)
Log.d("Lifecycle", "Fragment onAttach")
}

// 保存Fragment状态
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.d("Lifecycle", "Fragment onSaveInstanceState")

// 保存状态
outState.putString("saved_text", "Some text to save")
}
}

生命周期状态与转换

Fragment生命周期状态:

  1. INITIALIZED:Fragment实例已创建
  2. ATTACHED:Fragment已附加到Activity
  3. CREATED:Fragment的onCreate()已调用,但视图尚未创建
  4. VIEW_CREATED:Fragment的视图已创建
  5. STARTED:Fragment可见
  6. RESUMED:Fragment处于活动状态并可与用户交互
  7. PAUSED:Fragment失去焦点
  8. STOPPED:Fragment不再可见
  9. VIEW_DESTROYED:Fragment视图已销毁
  10. DETACHED:Fragment与Activity解除关联

与iOS子视图控制器生命周期对比

iOS子视图控制器生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@implementation ChildViewController

- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"Child viewDidLoad");

// 视图加载完成,初始化视图组件
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"Child viewWillAppear");

// 视图即将显示
}

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"Child viewDidAppear");

// 视图已显示,可以开始动画或更新
}

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(@"Child viewWillDisappear");

// 视图即将隐藏,可以保存状态
}

- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
NSLog(@"Child viewDidDisappear");

// 视图已隐藏
}

- (void)didMoveToParentViewController:(UIViewController *)parent {
[super didMoveToParentViewController:parent];

if (parent) {
NSLog(@"Child added to parent");
} else {
NSLog(@"Child removed from parent");
}
}

- (void)willMoveToParentViewController:(UIViewController *)parent {
[super willMoveToParentViewController:parent];

if (parent) {
NSLog(@"Child will be added to parent");
} else {
NSLog(@"Child will be removed from parent");
}
}

@end

生命周期方法对比

Android Fragment iOS UIViewController 说明
onAttach willMoveToParentViewController 添加到父容器前
onCreate 无直接对应 创建实例
onCreateView loadView 创建视图
onViewCreated viewDidLoad 视图创建完成
onStart viewWillAppear 视图即将可见
onResume viewDidAppear 视图完全可见并可交互
onPause viewWillDisappear 视图即将隐藏
onStop viewDidDisappear 视图完全隐藏
onDestroyView 无直接对应 视图被销毁
onDestroy 无直接对应 实例被销毁
onDetach didMoveToParentViewController(nil) 从父容器移除后

主要差异

  1. Android Fragment的生命周期更细粒度,区分了实例生命周期和视图生命周期
  2. iOS视图控制器没有明确区分实例和视图的生命周期
  3. Android Fragment有专门的附加/分离回调,而iOS使用willMove/didMoveToParentViewController
  4. Android Fragment可以在视图销毁后保留实例,而iOS通常同时处理
  5. Android Fragment生命周期与宿主Activity紧密耦合,而iOS子视图控制器相对独立

5.3 Fragment通信

Fragment与Activity通信

Fragment和Activity之间的通信是常见需求,有多种实现方式:

1. 接口回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 在Fragment中定义接口
class DataFragment : Fragment() {

// 定义回调接口
interface OnDataListener {
fun onDataSelected(data: String)
}

private var listener: OnDataListener? = null

override fun onAttach(context: Context) {
super.onAttach(context)
// 确保Activity实现了接口
if (context is OnDataListener) {
listener = context
} else {
throw RuntimeException("$context must implement OnDataListener")
}
}

override fun onDetach() {
super.onDetach()
listener = null
}

// 触发回调
private fun sendDataToActivity(data: String) {
listener?.onDataSelected(data)
}

// 在视图中使用
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

view.findViewById<Button>(R.id.send_button).setOnClickListener {
sendDataToActivity("Hello from Fragment")
}
}
}

// 在Activity中实现接口
class HostActivity : AppCompatActivity(), DataFragment.OnDataListener {

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

if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, DataFragment())
.commit()
}
}

// 实现接口方法
override fun onDataSelected(data: String) {
// 处理从Fragment接收的数据
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
}

2. ViewModel共享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 共享ViewModel
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<String>()

fun select(item: String) {
selected.value = item
}
}

// Fragment发送数据
class SenderFragment : Fragment() {

private lateinit var viewModel: SharedViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 获取Activity作用域的ViewModel
viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

view.findViewById<Button>(R.id.send_button).setOnClickListener {
viewModel.select("Data from SenderFragment")
}
}
}

// Fragment接收数据
class ReceiverFragment : Fragment() {

private lateinit var viewModel: SharedViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 获取Activity作用域的ViewModel
viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val textView = view.findViewById<TextView>(R.id.text_view)

// 观察数据变化
viewModel.selected.observe(viewLifecycleOwner) { item ->
textView.text = item
}
}
}

3. Activity直接调用Fragment方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class HostActivity : AppCompatActivity() {

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

findViewById<Button>(R.id.activity_button).setOnClickListener {
// 获取Fragment实例
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as? DataFragment
// 调用Fragment方法
fragment?.updateData("Data from Activity")
}
}
}

class DataFragment : Fragment() {

private lateinit var textView: TextView

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

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
textView = view.findViewById(R.id.text_view)
}

// 公开方法供Activity调用
fun updateData(data: String) {
textView.text = data
}
}

Fragment间通信

Fragment之间的通信通常通过以下方式实现:

1. 通过宿主Activity

```
// FragmentA将数据发送给Activity
class FragmentA : Fragment() {

interface OnMessageSendListener {
    fun onMessageSend(message: String)
}

private var listener: OnMessageSendListener? = null

override fun onAttach(context: Context) {
    super.onAttach(context)
    if (context is OnMessageSendListener) {
        listener = context
    } else {
        throw RuntimeException("$context must implement OnMessageSendListener")
    }
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    view.findViewById<Button>(R.id.send_button).setOnClickListener {
        listener?.onMessageSend("Message from FragmentA")
    }
}

}

// Activity接收数据并转发给FragmentB
class HostActivity : AppCompatActivity(), FragmentA.OnMessageSendListener {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_host)
    
    if (savedInstanceState == null) {
        supportFragmentManager.beginTransaction()
            .add(R.id.fragment_a_container, FragmentA())
            .add(R.id.fragment_b_container, FragmentB())
            .commit()
    }
}

override fun onMessageSend(message: String) {
    // 将消息转发给FragmentB
    val fragmentB = support<response clipped><NOTE>To save on context only part of this file has been shown to you. You should retry this tool after you have searched inside the file with `grep -n` in order to find the line numbers of what you are looking for.</NOTE>