-----------------------------------------------------
本文主要回答下面这个问题:
Android中,Header是如何被PreferenceActivity进行加载的?
-----------------------------------------------------

在Android应用程序中,我们可以在继承自PreferenceActivity的页面中通过两种方式,加载设置项。

其一:loadHeadersFromResource

[java] view plain copy
  1. @Override
  2. publicvoidonBuildHeaders(List<header>headers){
  3. loadHeadersFromResource(R.xml.settings_headers,headers);
  4. updateHeaderList(headers);
  5. mHeaders=headers;
  6. }</header>

settings_headers.xml

[html] view plain copy
  1. <preference-headers
  2. xmlns:android="http://schemas.android.com/apk/res/android">
  3. <!--WIRELESSandNETWORKS-->
  4. <headerandroid:title="@string/header_category_wireless_networks"/>
  5. <!--Wifi-->
  6. <header
  7. android:id="@+id/wifi_settings"
  8. android:fragment="com.android.settings.wifi.WifiSettings"
  9. android:title="@string/wifi_settings_title"
  10. android:icon="@drawable/ic_settings_wireless"/>
  11. <!--Bluetooth-->
  12. <header
  13. android:id="@+id/bluetooth_settings"
  14. android:fragment="com.android.settings.bluetooth.BluetoothSettings"
  15. android:title="@string/bluetooth_settings_title"
  16. android:icon="@drawable/ic_settings_bluetooth2"/>
  17. </preference-headers>


其二:

[java] view plain copy
  1. addPreferencesFromResource(R.xml.device_info_settings);

device_info_settings.xml

[html] view plain copy
  1. <PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"
  2. android:title="@string/about_settings">
  3. <!--Systemupdatesettings-launchesactivity-->
  4. <PreferenceScreenandroid:key="system_update_settings"
  5. android:title="@string/system_update_settings_list_item_title"
  6. android:summary="@string/system_update_settings_list_item_summary">
  7. <intentandroid:action="android.settings.SYSTEM_UPDATE_SETTINGS"/>
  8. </PreferenceScreen>
  9. <PreferenceScreenandroid:key="additional_system_update_settings"
  10. android:title="@string/additional_system_update_settings_list_item_title">
  11. <intentandroid:action="android.intent.action.MAIN"
  12. android:targetPackage="@string/additional_system_update"
  13. android:targetClass="@string/additional_system_update_menu"/>
  14. </PreferenceScreen>
  15. </PreferenceScreen>

这两种方式都可以满足我们的需求,今天我们主要来看一下第一种方式。

使用Header,内部大体经历了两个阶段,第一、解析xml配置文件,构建Header列表。第二、构建adapter进行Header列表的加载以及显示。

1、在PreferenceActivity.java中,调用loadHeadersFromResource进行Header的加载。

流程如下:

PreferenceActivity.java

[java] view plain copy
  1. publicvoidloadHeadersFromResource(intresid,List<Header>target){
  2. XmlResourceParserparser=null;
  3. try{
  4. parser=getResources().getXml(resid);
  5. AttributeSetattrs=Xml.asAttributeSet(parser);
  6. inttype;
  7. while((type=parser.next())!=XmlPullParser.END_DOCUMENT
  8. &&type!=XmlPullParser.START_TAG){
  9. //Parsenextuntilstarttagisfound
  10. }
  11. StringnodeName=parser.getName();
  12. if(!"preference-headers".equals(nodeName)){
  13. thrownewRuntimeException(
  14. "XMLdocumentmuststartwith<preference-headers>tag;found"
  15. +nodeName+"at"+parser.getPositionDescription());
  16. }
  17. BundlecurBundle=null;
  18. finalintouterDepth=parser.getDepth();
  19. while((type=parser.next())!=XmlPullParser.END_DOCUMENT
  20. &&(type!=XmlPullParser.END_TAG||parser.getDepth()>outerDepth)){
  21. if(type==XmlPullParser.END_TAG||type==XmlPullParser.TEXT){
  22. continue;
  23. }
  24. nodeName=parser.getName();
  25. if("header".equals(nodeName)){
  26. Headerheader=newHeader();
  27. TypedArraysa=getResources().obtainAttributes(attrs,
  28. com.android.internal.R.styleable.PreferenceHeader);
  29. header.id=sa.getResourceId(
  30. com.android.internal.R.styleable.PreferenceHeader_id,
  31. (int)HEADER_ID_UNDEFINED);
  32. TypedValuetv=sa.peekValue(
  33. com.android.internal.R.styleable.PreferenceHeader_title);
  34. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  35. if(tv.resourceId!=0){
  36. header.titleRes=tv.resourceId;
  37. }else{
  38. header.title=tv.string;
  39. }
  40. }
  41. tv=sa.peekValue(
  42. com.android.internal.R.styleable.PreferenceHeader_summary);
  43. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  44. if(tv.resourceId!=0){
  45. header.summaryRes=tv.resourceId;
  46. }else{
  47. header.summary=tv.string;
  48. }
  49. }
  50. tv=sa.peekValue(
  51. com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
  52. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  53. if(tv.resourceId!=0){
  54. header.breadCrumbTitleRes=tv.resourceId;
  55. }else{
  56. header.breadCrumbTitle=tv.string;
  57. }
  58. }
  59. tv=sa.peekValue(
  60. com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
  61. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  62. if(tv.resourceId!=0){
  63. header.breadCrumbShortTitleRes=tv.resourceId;
  64. }else{
  65. header.breadCrumbShortTitle=tv.string;
  66. }
  67. }
  68. header.iconRes=sa.getResourceId(
  69. com.android.internal.R.styleable.PreferenceHeader_icon,0);
  70. header.fragment=sa.getString(
  71. com.android.internal.R.styleable.PreferenceHeader_fragment);
  72. sa.recycle();
  73. if(curBundle==null){
  74. curBundle=newBundle();
  75. }
  76. finalintinnerDepth=parser.getDepth();
  77. while((type=parser.next())!=XmlPullParser.END_DOCUMENT
  78. &&(type!=XmlPullParser.END_TAG||parser.getDepth()>innerDepth)){
  79. if(type==XmlPullParser.END_TAG||type==XmlPullParser.TEXT){
  80. continue;
  81. }
  82. StringinnerNodeName=parser.getName();
  83. if(innerNodeName.equals("extra")){
  84. getResources().parseBundleExtra("extra",attrs,curBundle);
  85. XmlUtils.skipCurrentTag(parser);
  86. }elseif(innerNodeName.equals("intent")){
  87. header.intent=Intent.parseIntent(getResources(),parser,attrs);
  88. }else{
  89. XmlUtils.skipCurrentTag(parser);
  90. }
  91. }
  92. if(curBundle.size()>0){
  93. header.fragmentArguments=curBundle;
  94. curBundle=null;
  95. }
  96. target.add(header);
  97. }else{
  98. XmlUtils.skipCurrentTag(parser);
  99. }
  100. }
  101. }catch(XmlPullParserExceptione){
  102. thrownewRuntimeException("Errorparsingheaders",e);
  103. }catch(IOExceptione){
  104. thrownewRuntimeException("Errorparsingheaders",e);
  105. }finally{
  106. if(parser!=null)parser.close();
  107. }
  108. }


上面的这段代码是基于android 4.0版本截取的,代码内容大体可以分为三个阶段。

  • 阶段一:将resId对应的xml文件,加载成为XmlResourceParser、AttributeSet。即:
[java] view plain copy
  1. parser=getResources().getXml(resid);
  2. AttributeSetattrs=Xml.asAttributeSet(parser);
  • 阶段二:进行xml文件解析,解析出来Headers。解析方式为Pull解析,这也是说android内部是使用Pull解析的。(关于Pull解析请移步:http://blog.csdn.net/droyon/article/details/9346885)
[java] view plain copy
  1. if(!"preference-headers".equals(nodeName)){
  2. thrownewRuntimeException(
  3. "XMLdocumentmuststartwith<preference-headers>tag;found"
  4. +nodeName+"at"+parser.getPositionDescription());
  5. }


首先xml的标题头部必须为“preference-headers”,否则,抛出Runntime异常。


[java] view plain copy
  1. if("header".equals(nodeName)){
  2. Headerheader=newHeader();
  3. TypedArraysa=getResources().obtainAttributes(attrs,
  4. com.android.internal.R.styleable.PreferenceHeader);
  5. header.id=sa.getResourceId(
  6. com.android.internal.R.styleable.PreferenceHeader_id,
  7. (int)HEADER_ID_UNDEFINED);
  8. TypedValuetv=sa.peekValue(
  9. com.android.internal.R.styleable.PreferenceHeader_title);
  10. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  11. if(tv.resourceId!=0){
  12. header.titleRes=tv.resourceId;
  13. }else{
  14. header.title=tv.string;
  15. }
  16. }
  17. tv=sa.peekValue(
  18. com.android.internal.R.styleable.PreferenceHeader_summary);
  19. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  20. if(tv.resourceId!=0){
  21. header.summaryRes=tv.resourceId;
  22. }else{
  23. header.summary=tv.string;
  24. }
  25. }
  26. tv=sa.peekValue(
  27. com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
  28. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  29. if(tv.resourceId!=0){
  30. header.breadCrumbTitleRes=tv.resourceId;
  31. }else{
  32. header.breadCrumbTitle=tv.string;
  33. }
  34. }
  35. tv=sa.peekValue(
  36. com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
  37. if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
  38. if(tv.resourceId!=0){
  39. header.breadCrumbShortTitleRes=tv.resourceId;
  40. }else{
  41. header.breadCrumbShortTitle=tv.string;
  42. }
  43. }
  44. header.iconRes=sa.getResourceId(
  45. com.android.internal.R.styleable.PreferenceHeader_icon,0);
  46. header.fragment=sa.getString(
  47. com.android.internal.R.styleable.PreferenceHeader_fragment);
  48. sa.recycle();

这部分解析出来Header对象,并且对Header对象的title,summary,以及id,frament进行赋值。

[java] view plain copy
  1. while((type=parser.next())!=XmlPullParser.END_DOCUMENT
  2. &&(type!=XmlPullParser.END_TAG||parser.getDepth()>innerDepth)){
  3. if(type==XmlPullParser.END_TAG||type==XmlPullParser.TEXT){
  4. continue;
  5. }
  6. StringinnerNodeName=parser.getName();
  7. if(innerNodeName.equals("extra")){
  8. getResources().parseBundleExtra("extra",attrs,curBundle);
  9. XmlUtils.skipCurrentTag(parser);
  10. }elseif(innerNodeName.equals("intent")){
  11. header.intent=Intent.parseIntent(getResources(),parser,attrs);
  12. }else{
  13. XmlUtils.skipCurrentTag(parser);
  14. }
  15. }


这部分解析extra以及Intent。

  • 阶段三、将解析出来的Header加入到列表中。
[java] view plain copy
  1. target.add(header);


这里的target,参见上文,target为:PreferenceActivity中的mHeaders。

有人可能有疑问:为什么要在PreferenceActivity的继承类的onBuildHeaders方法中进行xml加载,onBuildHeaders什么时候调用?

理解了这个问题,也就明白了为什么上文中的target为PreferenceActivity中的mHeaders了。

答案就是,在PreferenceActivity的onCreate方法中,会调用onBuildHeader方法,同时将成员变量mHeader作为参数。

[java] view plain copy
  1. @Override
  2. protectedvoidonCreate(BundlesavedInstanceState){
  3. super.onCreate(savedInstanceState);
  4. setContentView(com.android.internal.R.layout.preference_list_content);
  5. //........
  6. //Weneedtotrytobuild<spanstyle="color:#ff0000;"><strong>theheaders.</strong>
  7. </span><spanstyle="color:#ff0000;"><strong>onBuildHeaders(mHeaders);</strong>
  8. </span>
  9. //Ifthereareheaders,thenatthispointweneedtoshow
  10. //themand,dependingonthescreen,wemayalsoshowin-line
  11. //thecurrentlyselectedpreferencefragment.
  12. if(mHeaders.size()>0){
  13. if(!mSinglePane){
  14. if(initialFragment==null){
  15. Headerh=onGetInitialHeader();
  16. switchToHeader(h);
  17. }else{
  18. switchToHeader(initialFragment,initialArguments);
  19. }
  20. }
  21. }
  22. }
  23. }
  24. //........
  25. }


我们在继承自PreferenceActivity的子类中:

[java] view plain copy
  1. publicclassSettingsextendsPreferenceActivity{
  2. @Override
  3. publicvoidonBuildHeaders(List<Header>headers){
  4. loadHeadersFromResource(R.xml.settings_headers,headers);
  5. updateHeaderList(headers);
  6. mHeaders=headers;
  7. }
  8. }

回答完毕。

通过上面的介绍,我们完成了第一步,即:xml配置文件的解析以及Header的列表加载。

第二、adapter的构建。

通过第一步,PreferenceActivity得到了一个Header的List集合,mHeaders。

同样是在PreferenceActivity的onCreate中,会执行如下代码:

[java] view plain copy
  1. setListAdapter(newHeaderAdapter(this,mHeaders));

PreferenceActivity继承自ListActivity,内部拥有ListView的引用。

上段代码就是我们常用的Adapter,ListView形式,不需要再介绍了吧。

附:HeaderAdapter的源码:

[java] view plain copy
  1. privatestaticclassHeaderAdapterextendsArrayAdapter<Header>{
  2. privatestaticclassHeaderViewHolder{
  3. ImageViewicon;
  4. TextViewtitle;
  5. TextViewsummary;
  6. }
  7. privateLayoutInflatermInflater;
  8. publicHeaderAdapter(Contextcontext,List<Header>objects){
  9. super(context,0,objects);
  10. mInflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  11. }
  12. @Override
  13. publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
  14. HeaderViewHolderholder;
  15. Viewview;
  16. if(convertView==null){
  17. view=mInflater.inflate(com.android.internal.R.layout.preference_header_item,
  18. parent,false);
  19. holder=newHeaderViewHolder();
  20. holder.icon=(ImageView)view.findViewById(com.android.internal.R.id.icon);
  21. holder.title=(TextView)view.findViewById(com.android.internal.R.id.title);
  22. holder.summary=(TextView)view.findViewById(com.android.internal.R.id.summary);
  23. view.setTag(holder);
  24. }else{
  25. view=convertView;
  26. holder=(HeaderViewHolder)view.getTag();
  27. }
  28. //Allviewfieldsmustbeupdatedeverytime,becausetheviewmayberecycled
  29. Headerheader=getItem(position);
  30. holder.icon.setImageResource(header.iconRes);
  31. holder.title.setText(header.getTitle(getContext().getResources()));
  32. CharSequencesummary=header.getSummary(getContext().getResources());
  33. if(!TextUtils.isEmpty(summary)){
  34. holder.summary.setVisibility(View.VISIBLE);
  35. holder.summary.setText(summary);
  36. }else{
  37. holder.summary.setVisibility(View.GONE);
  38. }
  39. returnview;
  40. }
  41. }


以上就是PreferenceActivity加载Header的整个流程。不当之处,欢迎大家提出宝贵意见,共同学习。

更多相关文章

  1. Android移动操作系统源代码
  2. android中activity的四种加载模式
  3. Android有效解决加载大图片时内存溢出的问题
  4. Android 图片加载图片_OOM异常解决
  5. Android Studio系列(二)使用Android Studio开发/调试整个android系
  6. Android开发常用代码片段(三)
  7. Android 性能优化之Java(Android)代码优化 (三)
  8. Android常用代码之普通及系统权限静默安装APK
  9. 求助: Android 加载 webview, 点击webview 中网页时间, 如何触发

随机推荐

  1. android 自定义主题样式
  2. android笔记
  3. android例子
  4. android时间控件DigitalClock的使用
  5. Android(安卓)Desigin Library
  6. 收集android的三个小tip
  7. 【30篇突击 android】源码统计七
  8. android 逐帧动画
  9. android:windowSoftInputMode属性使用
  10. Android开发学习之Gallery和GridView浅析