Fragment是一种可以嵌入在Activity中的UI片段,用来合理提高大屏幕的利用率,同时提高界面代码在不同屏幕下的利用率。
Fragment的使用方式
Fragment的简单用法
以下记录通过fragment实现一个左右分屏界面的步骤。
- 建立左侧Fragment布局:left_fragment.xml
- 建立右侧Fragment布局:right_fragment.xml
- 创建类
LeftFragment
并继承Fragment
类,重写onCreateView()
函数,使用LayoutInflater加载左侧Fragment布局left_fragment - 创建类
RightFragment
并继承Fragment
类,重写onCreateView()
函数,使用LayoutInflater加载右侧Fragment布局right_fragment - 在Activity加载的layout中分别引入左右两个fragment。注意在标签中通过指定
android:name
的方式指定具体Fragment布局
动态添加Fragment
Fragment真正能让布局更加灵活的原因是它可以在程序运行的过程中动态地添加到Activity中,我认为这也是Fragment真正存在的意义。下面介绍动态加载的步骤。
- 在简单用法的工程中新建fragment:another_right_fragment.xml
- 创建对应类
AnotherRightFragment
并继承Fragment()
,重写函数onCreateView()
- 将Activity对应的layout中的right_fragment从属的fragment标签替换为FrameLayout
- 在MainActivity的代码中添加fragment替换逻辑
思考:是不是以后不再详细介绍这些简单操作,直接描述小节重点,例如这个替换逻辑?
动态添加fragment步骤:
- 创建待添加的Fragment实例:
AnotherRightFragment()
- 获取FragmentManager,
fragmentManager = supportFragmentManager
- 开启一个事务,
val transaction = fragmentManager.beginTransaction()
- 通过事务进行framgnet替换操作,
transaction.repalce(original layout id, new fragment)
- 提交事务,
transaction.commit()
动态添加返回栈
为了实现类似返回栈的效果,使得BACK键可返回上一个Fragment,在替换时使用transaction的函数transaction.addToBackStack()
Fragment与Activity的交互
获取对方实例的情况经常出现,区分这些获取实例的方式也帮助我们区分Activity,Fragment,View,Layout这些概念。
-
Activity中获取Fragment实例:
val fragment = supportFragmentManager.findFragmentById(R.id.fragmentid) as FragmentClass
,进而调用fragment内部函数 -
Fragment获取Activity实例:
getActivity()
,根据Kotlin语法糖可直接调用val mainActivity = activity as MainActivity
,注意getActivity
有可能返回null
,所以调用之前要进行判空处理 -
同一个Activity下两个Fragment之间的通信: Fragment1先获得Activity的实例,再通过Activity获得Fragment2实例
Fragment的生命周期
Fragment作为一种嵌入Activity的UI套件,其生命周期与Activity相同,同时也附加了一些其他的毁掉方法。
Fragment在其生命周期中的状态
- 运行状态:当Fragment所关联的Activity处于运行状态时
- 暂停状态:当Fragment所关联的Activity处于暂停状态时
- 停止状态:当Fragment所关联的Activity处于停止状态,或者Fragment被replace、remove且addToBackStack()时,此时Fragment对于用户不可见,有可能被回收
- 销毁状态:当Fragment所关联的Activity被销毁,或者Fragment被replace、remove且没有addToBackStack()时
附加的回调方法
相比于Activity,Fragment需要主动和Activity关联且主动加载View,所以多出了下列回调方法
- onAttach(): 与Activity建立关联
- onCreateView(): 加载布局
- onActivityCreated(): 确定相关联的Activity已经创建完毕
- onDestroyView(): 移除布局
- onDetach(): 与Acttivity解除关联 ps.先运行onDestroy()再执行onDetach()
Fragment的完整周期
动态加载布局
- 使用限定符:例如对于大屏幕的设备,在layout-large文件夹下创建activity_main.xml,这样大屏幕设备就会自动加载该文件中的layout内容
- 使用最小宽度限定符:例如在屏幕宽度大于等于500dp的设备上运行某个布局,就新建layout-sw500文件夹并创建activity_main.xml
Fragment最佳实践的分析
最佳实践中需要动态处理的是当点击新闻标题后的响应,对于手机来说是新ActivityNewsContentActivity
入栈,对于平板则是调用Fragment类NewsContentFragment
的实例并调用refresh(News)
函数。
这里的解决办法是在类NewsTitleFragment
类中加入标志isTwoPane。在onActivityCreated()
函数中根据Activity中是否有contentLayout判断,即isTwoPane = activity?.findViewById<View>(R.id.contentLayout) != null
。由于TitleFragment内部有滚动展示标题的RecyclerView控件,在设置对应Adapter并添加对title_item的onClickListener
时,根据isTwoPane
的值进行对应处理。
值得一提的是,通过TitleFragment
中的Button
控制ContentFragment
中TextView
的内容属于同Activity下Fragment的通信,具体方法上文有提到。