1. Preference Header 概述

对于什么是 Preference Header,以及何时使用 Preference Header,请参考我的另一篇博文: 何时使用 Preference Headers
2. 自定义 Preference Header 布局的必要性:
Preference Header 的设计初衷是作为复杂设置选项的一个简单抽象或者概括,所以它本身应该一目了然,让用户一看便知道下一级设置中大概都有哪些设置选项,在布局设计上也应简单,所以谷歌给 header 统一只设置了 icon、title 和 summary 三个布局项。

基于以上原则,谷歌并没有给 Preference Header 提供方便的自定义接口,或许考虑到这本身不需要做什么自定义。但是有时候我们确实需要做一些简单的自定义。比如,你的 APP 需要统一 UI 风格,需要自定义的字体、颜色、selector样式等,这时当你需要用到 Preference Header 的时候就需要考虑对其布局进行
微自定义。

3. 如何自定义?
在本文中,我们将实现对 Preference Header 布局的有限的自定义,实现如下图所示的效果,上图为没有自定义的效果,下图为自定义之后的效果:












3.1 寻找 Preference Headers 的布局方式:
Headers 是在 PreferenceActivity 中进行布局的,PreferenceActivity 其实是继承了一个 ListActivity,所以呈现在我们面前的 Headers 列表其实就是一个 ListView,那么我们很自然地想到每个 Header ,即每个 list item 的布局应该在一个 list adapter 中被 inflated ,查看 PreferenceActivity 的
源代码
发现里面定义了一个 ArrayAdapter<Header> 的子类 HeaderAdapter,请注意 getView 中是如何 inflate header item 布局的:


@Override
public View getView(int position, View convertView, ViewGroup parent) {
HeaderViewHolder holder;
View view; if (convertView == null) {
view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
parent, false);
holder = new HeaderViewHolder();
holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
view.setTag(holder);
} else {
view = convertView;
holder = (HeaderViewHolder) view.getTag();
} ......
return view;
}
3.2 分析 header item 布局文件 preference_header_item.xml :
从 getView 方法中我们确认了 header item 的布局在 preference_header_item.xml 这个文件中,那么要更改布局方式其实就是修改这个文件,我们从源码的资源文件中找到该文件的 
源代码
发现每一个 header item 都由 icon(ImageView)、title(TextView) 和 summary(TextView) 三个元素组成。接下来就可以考虑如何对其进行修改了。


3.3 如何修改 preference_header_item.xml?
这里我们不打算对 header 进行复杂的自定义(比如,增加额外的 header 元素,这个需要对 Header 本身也进行定制,考虑到 Header 的简洁要求,个人觉得没有必要这么做)。

在本文中,我们实现一些比较有用的自定义,具体来说包括如下修改: icon 和 title、summary 位置的互换,title 和 summary 字体颜色、大小的更改,list item 高度的修改,list item 背景的修改(修改 selector),正如上图给出的效果所示。

3.4 定义一个本地布局文件 custom_preference_header_item.xml:
我们将原生的 preference_header_item.xml 文件拷贝到本地工程 layout 文件夹中,修改文件名称为 custom_preference_header_item.xml,并对布局进行 3.3 中所述的修改。

3.5 定义一个 PreferenceActivity 的子类 CustomPreferenceActivity:
在 CustomPreferenceActivity 中重新定义一个 HeaderAdapter,主要将其中的


view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
parent, false);
修改为:

view = mInflater.inflate(R.layout.custom_preference_header_item,
parent, false);
由于 PreferenceActivity 中的 List<Header> 成员 mHeaders 是私有的,而我们要将 mHeaders 作为数据源参数传递给新定义的 HeaderAdapter 中,为了解决这个问题,我们在 CustomPreferenceActivity 中再定义一个 List<Header> 成员 mCopyHeaders,作为 mHeaders 的一个拷贝,并通过重写 loadHeadersFromResource 方法将 mHeaders 赋值给 mCopyHeaders:

     @Override
public void loadHeadersFromResource(int resid, List<Header> target) {
super.loadHeadersFromResource(resid, target);
mCopyHeaders = target;
}
注意:
loadHeadersFromResource
 中的 target 参数即为 mHeaders,详细请参考 PreferenceActivity 源码

在 PreferenceActivity 源码 
中,setListAdapter() 是在 onCreate() 方法中调用的,为此,我们还要重写 onCreate() 方法,将 CustomPreferenceActivity 中的 adapter 设置为自定义的 HeaderAdapter 对象:

     @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); if (mCopyHeaders != null && mCopyHeaders.size() > 0) {
setListAdapter(new HeaderAdapter(this, mCopyHeaders));
}
}
至此,Preference Header 的自定义布局就完成了,接下来就只需要定义一个 CutomPreferenceActivity 的子类,并编写相应的 preference-headers 文件,重新 onBuildHeaders() 方法就可以了:

     @Override
public void onBuildHeaders(List<Header> target) {
this.loadHeadersFromResource(R.xml.preference_headers, target);
}
完整的工程请下载:CustomPreferenceHeader




自定义 Preference Header 布局的更多相关文章

  1. Android设置选项开发及自定义Preference样式

    一个完整的Android应用程序都应该提供选项(或者叫偏好设置等等)让用户对APP的表现形式能够进行设置,比如说是否加入用户体验计划,或者是否自动升级.定时提醒.开启自启动.后台运行等等.提供一个好的 ...

  2. 自定义 Collection View 布局

    自定义 Collection View 布局 answer-huang 29 Mar 2014 分享文章 UICollectionView 在 iOS6 中第一次被引入,也是 UIKit 视图类中的一 ...

  3. iOS系列译文:自定义Collection View布局

    原文出处: Ole Begemann   译文出处: 黄爱武(@answer-huang).欢迎加入技术翻译小组. UICollectionView在iOS6中第一次被介绍,也是UIKit视图类中的一 ...

  4. 常用样式制作思路 自定义按钮~自适应布局~常见bug seajs简记 初学者必知的HTML规范 不容忽略的——CSS规范

    常用样式制作思路   学习常用样式总结参考来自这里 带点文字链接列表利用:before实现 1 <!DOCTYPE html> 2 <html lang="en" ...

  5. 自定义Collection View布局

    转自answer-huang的博客 原文出自:Custom Collection View Layouts    UICollectionView在iOS6中第一次被介绍,也是UIKit视图类中的一颗 ...

  6. Android动画效果之自定义ViewGroup添加布局动画

    前言: 前面几篇文章介绍了补间动画.逐帧动画.属性动画,大部分都是针对View来实现的动画,那么该如何为了一个ViewGroup添加动画呢?今天结合自定义ViewGroup来学习一下布局动画.本文将通 ...

  7. Android自定义Dialog及其布局

     实际项目开发中默认的Dialog样式无法满足需求,需要自定义Dialog及其布局,并响应布局中控件的事件. 上效果图: 自定义Dialog,LogoutDialog: 要将自定义布局传入构造函数中, ...

  8. iOS-AFNetworking封装Get(自定义HTTP Header)和Post请求及文件下载

    前面提到AFNetworking是一个很强大的网络三方库,首先你需要引入AFNetworking三方库:如封装的有误还请指出,谢谢! 1.Get请求 /**Get请求 url 服务器请求地址 succ ...

  9. Android中自定义Preference

    一.需求 开发横屏设备的app时,发现preference显示的都是上下结构,因此需要自定义preference实现横屏显示. 二.layout实现 <?xml version="1. ...

随机推荐

  1. Linux查看机器是多少位

    命令: file /bin/ls或者file /sbin/init 示例: 如上所示,我的机器是32位的.

  2. BZOJ 2693 jzptab

    http://www.lydsy.com/JudgeOnline/problem.php?id=2693 题解: 考虑把lcm转化成gcd那答案就是然后神奇的设:就有:一样可以枚举 的取值,这是O(√ ...

  3. 【HDOJ】1315 Basic

    这道题目巨坑啊,注意__int64,int wa了一个下午. #include <cstdio> #include <cstring> #include <cstdlib ...

  4. UNION ALL vs UNION

    一直没意识到它们之间的区别,只知道UNION ALL在性能上优于UNION,忽略一个很重要的区别:UNION会去掉重复的行,而UNION ALL是包括所有行.

  5. The Black Tux | IT桔子

    The Black Tux | IT桔子 The Black Tux theblacktux.com

  6. java中paint repaint update 之间的关系

    最近总结了一下java中的paint,repaint和updata三者之间的关系,首先咱们都知道用paint方法来绘图,用repaint重绘,用update来写双缓冲.但是他们之间是怎么来调用的呢,咱 ...

  7. jTemplates——学习(1)

    这里介绍一个基于jQuery开发的模板引擎. jTemplates目前最新的版本是0.7.8,由tPython开发.官方网站:http://jtemplates.tpython.com 两个附件, 一 ...

  8. Web服务的体系架构

    Web简介: Web是WWW(World Wide Web)的简称,又称为万维网,是建立在客户机/服务器上的,以HTML语言和HTML协议为基础,提供面向Internet服务的,有一致用户界面的一种信 ...

  9. Word01-从正文处开始插入页码

    一份正式的文档应该由以下几部分组成:封面.目录.摘要.正文…… 现在要求前三页不需要插入页码,从正文部分插入页码为第一页,原文档如下: 步骤: 第一步:将光标移动到摘要页的末尾,选择页面部局--> ...

  10. linux 常用find命令

    1.查找当前目录下以test开头的所有文件-会进入子目录中去查找 [root@rusky hgfs]# find -name test* 2.查找当前目录下名为test.txt的文件-会进入子目录中去 ...