用 PreferenceFragment 构建分组设置界面:解读 cw-omnibus 中的 FragmentsBC 示例

发布时间:2026/10/16 9:02:23

用 PreferenceFragment 构建分组设置界面:解读 cw-omnibus 中的 FragmentsBC 示例 示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载本篇技术指南以 cw-omnibus 仓库The Busy Coders Guide to Android Development配套示例源码集中 Prefs/FragmentsBC/README.markdown 所述示例为核心系统讲解如何借助PreferenceFragment与PreferenceActivity的 header 机制把设置项拆分成多个分组页面。读完本文你将掌握loadHeadersFromResource()加载 header、addPreferencesFromResource()挂载偏好资源、isValidFragment()安全校验以及如何在普通 Fragment 中通过SharedPreferences回读偏好值这一整套实战方案。一、示例要解决的问题一个 Activity 管理多组设置Android 3.0API 11引入PreferenceFragment之后设置界面不再只能由PreferenceActivity单独承载 XML 资源而是可以把每一组设置放进一个 Fragment。FragmentsBC 示例即演示了这种一个设置页 多个偏好分组的组织形态主界面FragmentsDemo是一个普通 Activity仅通过菜单项进入设置页设置页EditPreferences继承PreferenceActivity重写onBuildHeaders()用 header 列表把两组设置串起来两个PreferenceFragment内部类First、Second分别承载两组不同的偏好选项返回主界面后PreferenceContentsFragment从默认SharedPreferences中读取所有设置结果并展示。该示例的配套书籍章节为《using preferences使用偏好设置》在仓库中的完整实现见 Prefs/FragmentsBC 目录。二、整体架构与入口流程2.1 三个 Java 类的分工类文件职责FragmentsDemoFragmentsDemo.java主界面展示当前偏好值菜单触发设置页EditPreferencesEditPreferences.javaPreferenceActivity子类负责 header 路由与 Fragment 白名单PreferenceContentsFragmentPreferenceContentsFragment.java普通 Fragment在onResume()中读取并刷新偏好显示2.2 从主界面到设置页主界面 FragmentsDemo.java 在onCreate()中加载 main.xml后者通过静态fragment标签直接嵌入了PreferenceContentsFragmentfragment xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:namecom.commonsware.android.preffragsbc.PreferenceContentsFragment/菜单方面actions.xml 定义了一个settings菜单项图标使用系统自带的android:drawable/ic_menu_preferences并设置showAsActionnever使其始终出现在溢出菜单。点击后经onOptionsItemSelected()启动EditPreferencesOverride public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.settings: startActivity(new Intent(this, EditPreferences.class)); return(true); } return(super.onOptionsItemSelected(item)); }对应地AndroidManifest.xml 中同时注册了主 Activity 与设置 Activity设置页无需LAUNCHER过滤器activity android:name.FragmentsDemo android:labelstring/app_name intent-filter action android:nameandroid.intent.action.MAIN/ category android:nameandroid.intent.category.LAUNCHER/ /intent-filter /activity activity android:name.EditPreferences android:labelstring/app_name/三、核心实现PreferenceActivity Header 路由3.1 onBuildHeaders 与 loadHeadersFromResourceEditPreferences的关键在于重写onBuildHeaders()把 header 资源喂给系统EditPreferences.java#L22-L26public class EditPreferences extends PreferenceActivity { Override public void onBuildHeaders(ListHeader target) { loadHeadersFromResource(R.xml.preference_headers, target); } ... }preference_headers.xml定义了两个分组入口每个header通过android:fragment指向要展示的PreferenceFragment全限定类名preference_headers.xmlpreference-headers xmlns:androidhttp://schemas.android.com/apk/res/android header android:fragmentcom.commonsware.android.preffragsbc.EditPreferences$First android:summarystring/header1summary android:titlestring/header1title /header header android:fragmentcom.commonsware.android.preffragsbc.EditPreferences$Second android:summarystring/header2summary android:titlestring/header2title /header /preference-headers注意类名的写法内部类使用$分隔即EditPreferences$First。title与summary分别来自 strings.xmlheader1titleSome Settings、header1summaryA handful of values for you to tailor、header2titleMore Settings 等。3.2 isValidFragmentAndroid 4.0 的强制安全闸门从 API 19 开始凡是使用 header 加载 Fragment 的PreferenceActivity系统会调用isValidFragment()校验传入的 Fragment 类名防止外部注入恶意 Fragment。示例中通过白名单方式放行自带的两个内部类EditPreferences.java#L29-L36Override protected boolean isValidFragment(String fragmentName) { if (First.class.getName().equals(fragmentName) || Second.class.getName().equals(fragmentName)) { return(true); } return(false); }从源码结构看这是基于 fragment 的偏好页面必不可少的配套代码一旦 header 引用的 Fragment 类名不在此白名单内isValidFragment()返回false系统将拒绝加载该 Fragment。3.3 两个 PreferenceFragment 内部类两个内部类都继承android.preference.PreferenceFragment在onCreate()里通过addPreferencesFromResource()各自加载一份偏好 XMLEditPreferences.java#L38-L54public static class First extends PreferenceFragment { Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); } } public static class Second extends PreferenceFragment { Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences2); } }First挂载的是 preferences.xmlSecond挂载的是 preferences2.xml。这样一个PreferenceActivity就能承载两个互相独立的设置分组用户通过 header 列表切换。四、偏好资源详解四类常用 Preferencepreferences.xml 聚合了 Android 平台四种最常见的偏好控件是理解PreferenceScreen子节点用法的绝佳范本PreferenceScreen xmlns:androidhttp://schemas.android.com/apk/res/android CheckBoxPreference android:keycheckbox android:summarystring/pref1summary android:titlestring/pref1title/ RingtonePreference android:keyringtone android:showDefaulttrue android:showSilenttrue android:summarystring/pref2summary android:titlestring/pref2title/ EditTextPreference android:dialogTitlestring/dialogtitle android:keytext android:summarystring/pref3summary android:titlestring/pref3title/ ListPreference android:dialogTitlestring/listdialogtitle android:entriesarray/cities android:entryValuesarray/airport_codes android:keylist android:summarystring/pref4summary android:titlestring/pref4title/ /PreferenceScreen各控件要点CheckBoxPreference开关型偏好android:keycheckbox即写入SharedPreferences的键名RingtonePreference铃声选择器android:showDefaulttrue让列表中出现默认铃声项android:showSilenttrue让列表中出现静音项EditTextPreference弹窗文本输入android:dialogTitle指定弹窗标题ListPreference弹窗单选列表android:entries是展示给用户的文本数组android:entryValues是实际写入存储的值数组两者通过数组下标一一对应。第二个分组 preferences2.xml 只包含一个CheckBoxPreferenceandroid:keycheckbox2用于验证多个 Fragment 分组各自独立维护 XML的模式。4.1 配套的数组资源arrays.xml 为ListPreference提供显示名与存储值的成对数据——以宾夕法尼亚州城市为显示名、机场三字码为存储值string-array namecities itemPhiladelphia/item itemPittsburgh/item itemAllentown/Bethlehem/item ... /string-array string-array nameairport_codes itemPHL/item itemPIT/item itemABE/item ... /string-array4.2 文本资源的组织strings.xml 统一维护了 header 标题/摘要、偏好标题/摘要、弹窗标题等文案如pref1titleCheckbox Preference、pref2summaryPick a tone, any tone、listdialogtitleChoose a Pennsylvania city。所有 XML 中均通过string/...引用避免了魔法字符串。五、回读偏好值PreferenceContentsFragment 的 onResume 刷新设置页修改的值保存在应用默认的SharedPreferences中。主界面的 PreferenceContentsFragment.java 在onResume()中统一读取——之所以放在onResume()而不是onCreateView()是为了保证用户从设置页返回时能拿到最新值Override public void onResume() { super.onResume(); SharedPreferences prefs PreferenceManager.getDefaultSharedPreferences(getActivity()); checkbox.setText(Boolean.valueOf(prefs.getBoolean(checkbox, false)).toString()); ringtone.setText(prefs.getString(ringtone, unset)); checkbox2.setText(Boolean.valueOf(prefs.getBoolean(checkbox2, false)).toString()); text.setText(prefs.getString(text, unset)); list.setText(prefs.getString(list, unset)); }要点PreferenceManager.getDefaultSharedPreferences()返回的是与 Preference UI 默认关联的那份偏好文件key 与 XML 中的android:key一一对应每个读取都提供了默认值如false、unset避免首次启动时空指针ringtone键存储的是铃声 URI 字符串checkbox、checkbox2用Boolean.valueOf(...)转为字符串展示。界面侧content.xml 用TableLayoutTableRow布局每行由一个加粗标签 TextView 和一个 30sp 的取值 TextView 组成标签与取值样式定义在 styles.xmllabel样式继承value样式并追加bold与 5dip 右内边距。六、工程配置与运行前提6.1 构建配置app/build.gradlecom.android.application插件compileSdkVersion 21、buildToolsVersion 26.0.2、targetSdkVersion 17根 build.gradleAndroid Gradle 插件版本3.1.4仓库源为google()与jcenter()settings.gradle仅包含:app模块AndroidManifest.xmlminSdkVersion14API 14即 Android 4.0targetSdkVersion14。6.2 主题适配主题在 themes.xml 中基于 Holo 系定义Theme.Holo.Light.DarkActionBar同时在 values-v21/styles.xml 中为 Android 5.0 提供了 Material 变体并指定colorPrimary、colorPrimaryDark、colorAccent三个主题色。运行该示例需要 API 14 以上的设备或模拟器。七、与同目录姊妹示例的横向对照在仓库 Prefs 目录下cw-omnibus 围绕偏好设置提供了多组渐进式示例方便对比不同组织方式示例特点Prefs/Fragment基础版单个PreferenceFragment不使用 headerPrefs/FragmentsBC本文多分组PreferenceActivity header 多个PreferenceFragmentPrefs/NestedScreens进阶在一个PreferenceScreen内嵌套另一个PreferenceScreenPrefs/CustomSubtitle自定义 Fragment 副标题Prefs/DataStore自定义偏好存储后端Prefs/SingleHeader 与 Prefs/SingleHeader2单 header 场景后者额外处理大屏布局对照可见FragmentsBC 的核心价值在于header 驱动的多分组路由它是 NestedScreens单页面内嵌套之外的另一条组织路径——把不同类别的设置拆到不同页面更适合设置项较多、需要按模块划分的应用。八、实战要点小结三层结构PreferenceActivity路由层→ header 资源分组层→PreferenceFragment内容层每层职责单一安全校验不能省API 19 必须重写isValidFragment()并返回白名单判断否则基于 header 的 Fragment 无法加载key 即存储键偏好 XML 中的android:key直接决定SharedPreferences中的键名回读时需保持一致回读时机选 onResume需要实时反映用户修改时把PreferenceManager.getDefaultSharedPreferences()的读取逻辑放在onResume()数组配对ListPreference的entries与entryValues按下标一一对应前者面向用户、后者面向存储。通过本文的源码级拆解你可以直接参考 Prefs/FragmentsBC 的完整实现在自己的应用中快速落地一个设置入口、多组 Fragment 化偏好页面的经典架构。赞分享示例工程教程【免费下载链接】cw-omnibusSource code to omnibus edition of _The Busy Coders Guide to Android Development_项目地址https://gitcode.com/gh_mirrors/cw/cw-omnibus点击查看免费下载相关推荐cw-omnibus 基础组件示例解读用 EditText 构建多行文本输入控件cw omnibus 基础组件示例解读用 EditText 构建多行文本输入控件 导读 本文聚焦 cw omnibus The Busy Coders G示例工程教程cw-omnibus 实战解读基于 SQLiteOpenHelper 的 SQLite 存储示例 ConstantsROWIDcw omnibus 实战解读基于 SQLiteOpenHelper 的 SQLite 存储示例 ConstantsROWID 本篇技术指南以 cw omni示例工程教程循环工程Loop Engineering概念溯源核心来源、思想谱系与仓库落地映射循环工程Loop Engineering概念溯源核心来源、思想谱系与仓库落地映射 导读 本文以仓库中的 Sources Attribution 来源文示例工程教程上一篇手机跑大模型30分钟用koboldcpp在Android上跑通本地AI推理终极实战指南下一篇【亲测免费】 layui-soul-table 项目常见问题解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/16 9:02:23

KBEngine 嵌入式 Python 整数对象 C API(PyLong)权威指南

游戏开发后端 【免费下载链接】kbengine A MMOG engine of server. 项目地址: https://gitcode.com/gh_mirrors/kb/kbengine 点击查看 免费下载 本指南以 KBEngine 仓库内嵌的 CPython 3.7.3 官方文档 kbe/src/lib/python/Doc/c-api/long.rst 为骨架,逐…

2026/10/16 9:02:23

STM32F103_串口通信

STM32F103C8T6 串口通信:轮询 / 中断 / DMA 三种方式,附 Keil 逻辑分析仪测波特率这次做的实验是 STM32 串口(USART)。要求挺全:先用 HAL 库实现最基本的串口收发,再把轮询、中断、DMA 三种方式都过一遍&am…

2026/10/16 9:02:23

【附源码】基于SpringBoot的在线骑行网站(源码+数据库+论文+答辩ppt一整套齐全)java开发springboot+vue框架javaweb,可做计算机毕业设计或课程设计

🙊作者简介:多年一线开发工作经验,分享技术代码帮助学生学习。自有计算机毕设的工作室团队,专注计算机毕设开发、定制、远程、文档编写指导等。 🍅 查看主页更多项目 | 计算机毕设工作室 🍅 🍅 …

2026/10/16 10:18:30

基于Python的动漫分析系统全攻略:从数据采集到深度学习建模

很多同学在选毕设题目的时候,一眼就被“基于Python的动漫分析系统”这种题目抓住了眼球。原因很简单:动漫是兴趣,Python是上手快,深度学习是大热点,这三个词叠在一起,摆在任何一个答辩现场,都比…

2026/10/16 10:18:30

Micromine 2016实操:建模制图开发与Win10/11兼容性全解析

做矿业软件这一行,手里的工具版本常常比很多人的工作年限还要长。Micromine 2016就是这样的存在——我最早在一处铜多金属矿的储量复核项目里接触它,当时项目组的老工程师用它出图、建模,电脑还是老款的Win7工作站。几年过去,操作…

2026/10/16 10:18:30

WPF TreeView 拖拽实现详解:从原理到避坑指南

简介:这份资源面向具备一定 WPF 基础的开发者,聚焦 TreeView 控件的拖放(Drag & Drop)功能实现,解决节点顺序调整、数据移动与复制等交互需求。压缩包共 12 个文件,约 11KB,以 6 个 cs 源码…

2026/10/16 10:18:30

Qt程序打包如何补全版本与公司信息:从qmake到Inno Setup全配置

1. 为什么Qt程序默认打包出来是个"三无"安装包 先说个我自己的经历。之前给某公司做的一个模拟项目X收尾,Qt 5.15写的Windows桌面工具,编译、调试、跑自动化测试都通过了,最后用Inno Setup打了个安装包发给测试组。结果对方打开安装…

2026/10/16 10:18:30

YOLO免环境训练工具全流程指南:从自动标注到模型转换

简介:这份资源面向深度学习目标检测方向的开发者与算法学习者,提供一套免环境搭建的YOLO系列训练与标注工具,解决从数据标注到模型训练、转换流程繁琐的问题。包内共14个文件,以txt技术解析、html说明页、doc文档及jpg示意图为主&…

2026/10/16 10:13:29

Gitee不只能存代码:从SSH到Pull Request评审的团队协作进阶指南

如果你的日常工作流还停留在“把Gitee当成免费网盘,clone下来push上去就完事”,那你可能只用了这个代码托管平台一半以上的能力。我在过去几年带过好几个小团队,从最开始互相clone压缩包传文件,到后来把需求管理、代码评审、自动构…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/16 4:03:48

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/15 13:42:16

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/16 0:01:11

Cephadm实战指南:Linux系统工程师的生产级Ceph集群部署与排障

简介:本资源是一份完整的Ceph分布式存储系统中文技术文档合集,面向Linux系统运维工程师、云平台架构师及存储方向开发者,帮助读者系统掌握Ceph集群的部署、配置与运维核心能力。文档覆盖对象存储(RGW)、块设备&#xf…

2026/10/16 0:01:11

ITK中SpatialObject几何体系解析:坐标系、变换与管道集成

1. 空间对象的定位:为什么 ITK 需要一套几何描述体系很多刚开始接触 ITK 的朋友,包括我自己,最初都是被它的图像滤波、配准、分割这些“正统”功能吸引进来的。等真正上手做点实际项目,比如把分割出来的器官边界做量化分析、给血管…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑