Fragment
一种可以嵌入在 Activity 当中的 UI 片段,能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用得非常广泛
- 与 Activity 类似, 能包含布局, 拥有自己的声明周期
- 但 Activity 中可以包含多个 Fragment 实现分页效果
Quick Start
- 为 Fragment 设置好布局
1 |
|
- 新建 Fragment 类, 使其继承
androidx.fragment.app.Fragment
实现对 Fragment 布局的加载
1 | class LeftFragment: Fragment() { |
- 在主 Activity 中的布局中引入 Fragment
1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
动态添加 Fragment
- 在主 Activity 中为动态替换的 Fragment 留有布局
1 | <FrameLayout |
- 通过点击或其他事件触发 fragment 的替换加载
1 | // 将待添加 Fragment 的实例作为参数传入 |
带有参数的 Fragment
Fragment 显示的内容可能需要其父 Activity 告知, 因此初始化时需要配置相应的参数
- xml 中用 FrameLayout 为 Fragment 占位
1 | <FrameLayout |
- Activity 中选择适当的时机动态加载该 Fragment
1 | val toolbarFragment = ToolbarFragment() |
- Fragment 中在 onCreateView 中接收参数进行处理
1 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { |
与主 Activity 的交互
类似于父组件管理多个子组件, 主要通过 supportFragmentManager
实现
- 获取 fragment View 实例:
supportFragmentManager.findFragmentById(R.id.leftFrag) as LeftFragment
- 获取主 Activity 实例:
val mainActivity = getActivity() as MainActivity
生命周期
与 Activity 类似, 拥有四种状态: 运行状态, 暂停状态, 停止状态, 销毁状态
- 运行状态: 当一个 Fragment 所关联的 Activity 正处于运行状态时,该 Fragment 也处于运行状态
- 暂停状态: 同上
- 停止状态: 同上 或 通过 FragmentTransaction 的 remove() replace() 方法将 Fragment 从 Activity 中移除并且调用了 addToBackStack() 也会进入停止状态
- 销毁状态: 当 Activity 被销毁时,与它相关联的 Fragment 就会进入销毁状态 或者 同上但没有调用 addToBackStack() 将会进入销毁状态
特有的状态回调方法:
onAttach()
: Fragment 与 Activity 建立关联onCreateView()
: 为 Fragment 创建视图(加载布局) 时调用onActivityCreated()
: 确保与 Fragment 相关联的 Activity 已经创建完毕时调用onDestroyView()
: 当与 Fragment 关联的视图被移除时onDetach()
: 当 Fragment 和 Activity 解除关联时调用
动态布局加载技巧
旨在能更灵活的根据分辨率, 屏幕大小等在运行时决定加载哪个布局
限定符 qualifier
准备好单页模式的布局 layout/activity_main.xml
与双页模式的布局 layout-large/activity_main.xml
这里面的 large 就是一个内置的限定符
实际过程中你可以按照双页模式进行布局的绑定初始化, 但是两种布局会根据设备的大小进行展示
因此如果想要程序有更多的适配, 我们就要为各类限定符作好布局, 常见的限定符有
大小类限定符
- small: 小屏幕
- normal: 中等屏幕
- large: 大屏幕
- xlarge: 超大平步
- smallest-width qualifier: 最小宽度限定符, 允许对屏幕的宽度指定一个最小值(单位 dp) 屏幕宽度大于这个值的设备就加载这个布局, 小于这个值的就加载另一个布局, 具体后缀可缩写为
-sw600dp
分辨率类限定符:
- ldpi: 120 dpi 以下
- mdpi: 120 - 160 dpi
- hdpi: 160 - 240 dpi
- xhdpi: 240 - 320 dpi
- xxhdpi: 320 - 480 dpi
方向:
- land: 横屏
- port: 竖屏
Project
编写兼容手机和平板的应用程序
- MainActivity 中根据 layout 进行动态布局加载, 选择性加载单页布局
newsTitleFrag
和双页布局newsContentFrag
- 针对单页模式设置其 Fragment
NewsTitleFragment
: 在其中加载标题列表所对应的布局news_title_frag
; 在生命周期onViewCreated
中加载RecyclerView
的数据NewsAdapter
;NewsAdapter
中通过ViewHolder
实例为每个数据项添加点击处理事件holder.itemView.setOnClickListener
, 点击处理事件中通过判断当前 Activity 中是否是双页布局来选择性定义点击处理方式isTwoPane = (activity?.findViewById<View>(R.id.newsContentLayout)) != null
:
- 如果是双页布局, 则定位到目标 fragment 对象后刷新内容即可
- 如果是单页布局, 则需要启动一个新的 Activity 用于展示目标新闻的详细信息
1 | holder.itemView.setOnClickListener { |
- 针对双页模式设置其 Fragment
NewsContentFragment
: 在其中加载新闻内容所对应的布局, 并定义布局内容刷新的接口函数refresh
供 MainActivity 调用
1 | fun refresh(title: String, content: String) { |
- 针对单页模式的点击事件, 定义新的 Activity 展示新闻内容
NewsContentActivity
通过companion object
定义静态方法actionStart
以向外部提供快速启动该 Activity 的接口; 启动后则根据传递来的数据渲染界面即可
1 | fun actionStart(context: Context, title: String, content: String) { |