首页 > 移动开发 > 自定义 Preference Header 布局

自定义 Preference Header 布局

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 布局的:
[java][/java] view plaincopy

  1. @Override
  2.         public View getView(int position, View convertView, ViewGroup parent) {
  3.             HeaderViewHolder holder;
  4.             View view;
  5.             if (convertView == null) {
  6.                 view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
  7.                         parent, false);
  8.                 holder = new HeaderViewHolder();
  9.                 holder.icon = (ImageView) view.findViewById(com.android.internal.R.id.icon);
  10.                 holder.title = (TextView) view.findViewById(com.android.internal.R.id.title);
  11.                 holder.summary = (TextView) view.findViewById(com.android.internal.R.id.summary);
  12.                 view.setTag(holder);
  13.             } else {
  14.                 view = convertView;
  15.                 holder = (HeaderViewHolder) view.getTag();
  16.             }
  17.          ......
  18.             return view;
  19.         }
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,主要将其中的
[java][/java] view plaincopy

  1. view = mInflater.inflate(com.android.internal.R.layout.preference_header_item,
  2.                         parent, false);
修改为:
[java][/java] view plaincopy

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

  1. @Override
  2. public void loadHeadersFromResource(int resid, List<Header> target) {
  3.      super.loadHeadersFromResource(resid, target);
  4.      mCopyHeaders = target;
  5. }
注意:loadHeadersFromResource 中的 target 参数即为 mHeaders,详细请参考 PreferenceActivity 源码。
在 PreferenceActivity 源码 中,setListAdapter() 是在 onCreate() 方法中调用的,为此,我们还要重写 onCreate() 方法,将 CustomPreferenceActivity 中的 adapter 设置为自定义的 HeaderAdapter 对象:
[java][/java] view plaincopy

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

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

本文固定链接: http://www.devba.com/index.php/archives/1954.html | 开发吧

报歉!评论已关闭.