-
「Generated by Manus, instructions issued by binbinwang」
本章将深入探讨Android的Fragment组件及其在界面复用中的应用,并与iOS的对应概念进行对比。Fragment是Android UI开发中非常重要的组件,掌握它对于构建灵活、可复用的界面至关重要。
5.1 Fragment基础 Fragment概念与作用 Fragment是Activity中的一个行为或界面部分,可以将其视为”子Activity”。它有自己的生命周期,接收自己的输入事件,可以在Activity运行时添加或移除。
Fragment的主要特点 :
模块化:将复杂界面分解为多个可管理的部分
可复用:同一个Fragment可在不同Activity中使用
适应性:可根据屏幕尺寸动态调整界面布局
生命周期:拥有自己的生命周期,但受宿主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 <?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 :
静态添加(通过XML) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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 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
主要差异 :
Android的Fragment是专门设计的UI组件,而iOS使用通用的视图控制器容器模式
Android的FragmentManager提供了专门的事务API,而iOS使用父子视图控制器关系API
Android的Fragment有更复杂的生命周期,而iOS的子视图控制器生命周期相对简单
Android的Fragment可以有回退栈,而iOS通常使用UINavigationController管理导航栈
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生命周期状态:
INITIALIZED :Fragment实例已创建
ATTACHED :Fragment已附加到Activity
CREATED :Fragment的onCreate()已调用,但视图尚未创建
VIEW_CREATED :Fragment的视图已创建
STARTED :Fragment可见
RESUMED :Fragment处于活动状态并可与用户交互
PAUSED :Fragment失去焦点
STOPPED :Fragment不再可见
VIEW_DESTROYED :Fragment视图已销毁
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)
从父容器移除后
主要差异 :
Android Fragment的生命周期更细粒度,区分了实例生命周期和视图生命周期
iOS视图控制器没有明确区分实例和视图的生命周期
Android Fragment有专门的附加/分离回调,而iOS使用willMove/didMoveToParentViewController
Android Fragment可以在视图销毁后保留实例,而iOS通常同时处理
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>