Android Settings 设置中 PreferenceActivity中,Header是如何被加载的?
-----------------------------------------------------
本文主要回答下面这个问题:
Android中,Header是如何被PreferenceActivity进行加载的?
-----------------------------------------------------
在Android应用程序中,我们可以在继承自PreferenceActivity的页面中通过两种方式,加载设置项。
其一:loadHeadersFromResource
[java] view plain copy- @Override
- publicvoidonBuildHeaders(List<header>headers){
- loadHeadersFromResource(R.xml.settings_headers,headers);
- updateHeaderList(headers);
- mHeaders=headers;
- }</header>
settings_headers.xml
[html] view plain copy- <preference-headers
- xmlns:android="http://schemas.android.com/apk/res/android">
- <!--WIRELESSandNETWORKS-->
- <headerandroid:title="@string/header_category_wireless_networks"/>
- <!--Wifi-->
- <header
- android:id="@+id/wifi_settings"
- android:fragment="com.android.settings.wifi.WifiSettings"
- android:title="@string/wifi_settings_title"
- android:icon="@drawable/ic_settings_wireless"/>
- <!--Bluetooth-->
- <header
- android:id="@+id/bluetooth_settings"
- android:fragment="com.android.settings.bluetooth.BluetoothSettings"
- android:title="@string/bluetooth_settings_title"
- android:icon="@drawable/ic_settings_bluetooth2"/>
- </preference-headers>
其二:
- addPreferencesFromResource(R.xml.device_info_settings);
device_info_settings.xml
[html] view plain copy- <PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/about_settings">
- <!--Systemupdatesettings-launchesactivity-->
- <PreferenceScreenandroid:key="system_update_settings"
- android:title="@string/system_update_settings_list_item_title"
- android:summary="@string/system_update_settings_list_item_summary">
- <intentandroid:action="android.settings.SYSTEM_UPDATE_SETTINGS"/>
- </PreferenceScreen>
- <PreferenceScreenandroid:key="additional_system_update_settings"
- android:title="@string/additional_system_update_settings_list_item_title">
- <intentandroid:action="android.intent.action.MAIN"
- android:targetPackage="@string/additional_system_update"
- android:targetClass="@string/additional_system_update_menu"/>
- </PreferenceScreen>
- </PreferenceScreen>
这两种方式都可以满足我们的需求,今天我们主要来看一下第一种方式。
使用Header,内部大体经历了两个阶段,第一、解析xml配置文件,构建Header列表。第二、构建adapter进行Header列表的加载以及显示。
1、在PreferenceActivity.java中,调用loadHeadersFromResource进行Header的加载。
流程如下:
PreferenceActivity.java
[java] view plain copy- publicvoidloadHeadersFromResource(intresid,List<Header>target){
- XmlResourceParserparser=null;
- try{
- parser=getResources().getXml(resid);
- AttributeSetattrs=Xml.asAttributeSet(parser);
- inttype;
- while((type=parser.next())!=XmlPullParser.END_DOCUMENT
- &&type!=XmlPullParser.START_TAG){
- //Parsenextuntilstarttagisfound
- }
- StringnodeName=parser.getName();
- if(!"preference-headers".equals(nodeName)){
- thrownewRuntimeException(
- "XMLdocumentmuststartwith<preference-headers>tag;found"
- +nodeName+"at"+parser.getPositionDescription());
- }
- BundlecurBundle=null;
- finalintouterDepth=parser.getDepth();
- while((type=parser.next())!=XmlPullParser.END_DOCUMENT
- &&(type!=XmlPullParser.END_TAG||parser.getDepth()>outerDepth)){
- if(type==XmlPullParser.END_TAG||type==XmlPullParser.TEXT){
- continue;
- }
- nodeName=parser.getName();
- if("header".equals(nodeName)){
- Headerheader=newHeader();
- TypedArraysa=getResources().obtainAttributes(attrs,
- com.android.internal.R.styleable.PreferenceHeader);
- header.id=sa.getResourceId(
- com.android.internal.R.styleable.PreferenceHeader_id,
- (int)HEADER_ID_UNDEFINED);
- TypedValuetv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_title);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.titleRes=tv.resourceId;
- }else{
- header.title=tv.string;
- }
- }
- tv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_summary);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.summaryRes=tv.resourceId;
- }else{
- header.summary=tv.string;
- }
- }
- tv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.breadCrumbTitleRes=tv.resourceId;
- }else{
- header.breadCrumbTitle=tv.string;
- }
- }
- tv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.breadCrumbShortTitleRes=tv.resourceId;
- }else{
- header.breadCrumbShortTitle=tv.string;
- }
- }
- header.iconRes=sa.getResourceId(
- com.android.internal.R.styleable.PreferenceHeader_icon,0);
- header.fragment=sa.getString(
- com.android.internal.R.styleable.PreferenceHeader_fragment);
- sa.recycle();
- if(curBundle==null){
- curBundle=newBundle();
- }
- finalintinnerDepth=parser.getDepth();
- while((type=parser.next())!=XmlPullParser.END_DOCUMENT
- &&(type!=XmlPullParser.END_TAG||parser.getDepth()>innerDepth)){
- if(type==XmlPullParser.END_TAG||type==XmlPullParser.TEXT){
- continue;
- }
- StringinnerNodeName=parser.getName();
- if(innerNodeName.equals("extra")){
- getResources().parseBundleExtra("extra",attrs,curBundle);
- XmlUtils.skipCurrentTag(parser);
- }elseif(innerNodeName.equals("intent")){
- header.intent=Intent.parseIntent(getResources(),parser,attrs);
- }else{
- XmlUtils.skipCurrentTag(parser);
- }
- }
- if(curBundle.size()>0){
- header.fragmentArguments=curBundle;
- curBundle=null;
- }
- target.add(header);
- }else{
- XmlUtils.skipCurrentTag(parser);
- }
- }
- }catch(XmlPullParserExceptione){
- thrownewRuntimeException("Errorparsingheaders",e);
- }catch(IOExceptione){
- thrownewRuntimeException("Errorparsingheaders",e);
- }finally{
- if(parser!=null)parser.close();
- }
- }
上面的这段代码是基于android 4.0版本截取的,代码内容大体可以分为三个阶段。
- 阶段一:将resId对应的xml文件,加载成为XmlResourceParser、AttributeSet。即:
- parser=getResources().getXml(resid);
- AttributeSetattrs=Xml.asAttributeSet(parser);
- 阶段二:进行xml文件解析,解析出来Headers。解析方式为Pull解析,这也是说android内部是使用Pull解析的。(关于Pull解析请移步:http://blog.csdn.net/droyon/article/details/9346885)
- if(!"preference-headers".equals(nodeName)){
- thrownewRuntimeException(
- "XMLdocumentmuststartwith<preference-headers>tag;found"
- +nodeName+"at"+parser.getPositionDescription());
- }
首先xml的标题头部必须为“preference-headers”,否则,抛出Runntime异常。
- if("header".equals(nodeName)){
- Headerheader=newHeader();
- TypedArraysa=getResources().obtainAttributes(attrs,
- com.android.internal.R.styleable.PreferenceHeader);
- header.id=sa.getResourceId(
- com.android.internal.R.styleable.PreferenceHeader_id,
- (int)HEADER_ID_UNDEFINED);
- TypedValuetv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_title);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.titleRes=tv.resourceId;
- }else{
- header.title=tv.string;
- }
- }
- tv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_summary);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.summaryRes=tv.resourceId;
- }else{
- header.summary=tv.string;
- }
- }
- tv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_breadCrumbTitle);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.breadCrumbTitleRes=tv.resourceId;
- }else{
- header.breadCrumbTitle=tv.string;
- }
- }
- tv=sa.peekValue(
- com.android.internal.R.styleable.PreferenceHeader_breadCrumbShortTitle);
- if(tv!=null&&tv.type==TypedValue.TYPE_STRING){
- if(tv.resourceId!=0){
- header.breadCrumbShortTitleRes=tv.resourceId;
- }else{
- header.breadCrumbShortTitle=tv.string;
- }
- }
- header.iconRes=sa.getResourceId(
- com.android.internal.R.styleable.PreferenceHeader_icon,0);
- header.fragment=sa.getString(
- com.android.internal.R.styleable.PreferenceHeader_fragment);
- sa.recycle();
这部分解析出来Header对象,并且对Header对象的title,summary,以及id,frament进行赋值。
[java] view plain copy- while((type=parser.next())!=XmlPullParser.END_DOCUMENT
- &&(type!=XmlPullParser.END_TAG||parser.getDepth()>innerDepth)){
- if(type==XmlPullParser.END_TAG||type==XmlPullParser.TEXT){
- continue;
- }
- StringinnerNodeName=parser.getName();
- if(innerNodeName.equals("extra")){
- getResources().parseBundleExtra("extra",attrs,curBundle);
- XmlUtils.skipCurrentTag(parser);
- }elseif(innerNodeName.equals("intent")){
- header.intent=Intent.parseIntent(getResources(),parser,attrs);
- }else{
- XmlUtils.skipCurrentTag(parser);
- }
- }
这部分解析extra以及Intent。
- 阶段三、将解析出来的Header加入到列表中。
- target.add(header);
这里的target,参见上文,target为:PreferenceActivity中的mHeaders。
有人可能有疑问:为什么要在PreferenceActivity的继承类的onBuildHeaders方法中进行xml加载,onBuildHeaders什么时候调用?
理解了这个问题,也就明白了为什么上文中的target为PreferenceActivity中的mHeaders了。
答案就是,在PreferenceActivity的onCreate方法中,会调用onBuildHeader方法,同时将成员变量mHeader作为参数。
[java] view plain copy- @Override
- protectedvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(com.android.internal.R.layout.preference_list_content);
- //........
- //Weneedtotrytobuild<spanstyle="color:#ff0000;"><strong>theheaders.</strong>
- </span><spanstyle="color:#ff0000;"><strong>onBuildHeaders(mHeaders);</strong>
- </span>
- //Ifthereareheaders,thenatthispointweneedtoshow
- //themand,dependingonthescreen,wemayalsoshowin-line
- //thecurrentlyselectedpreferencefragment.
- if(mHeaders.size()>0){
- if(!mSinglePane){
- if(initialFragment==null){
- Headerh=onGetInitialHeader();
- switchToHeader(h);
- }else{
- switchToHeader(initialFragment,initialArguments);
- }
- }
- }
- }
- }
- //........
- }
我们在继承自PreferenceActivity的子类中:
- publicclassSettingsextendsPreferenceActivity{
- @Override
- publicvoidonBuildHeaders(List<Header>headers){
- loadHeadersFromResource(R.xml.settings_headers,headers);
- updateHeaderList(headers);
- mHeaders=headers;
- }
- }
回答完毕。
通过上面的介绍,我们完成了第一步,即:xml配置文件的解析以及Header的列表加载。
第二、adapter的构建。
通过第一步,PreferenceActivity得到了一个Header的List集合,mHeaders。
同样是在PreferenceActivity的onCreate中,会执行如下代码:
[java] view plain copy- setListAdapter(newHeaderAdapter(this,mHeaders));
PreferenceActivity继承自ListActivity,内部拥有ListView的引用。
上段代码就是我们常用的Adapter,ListView形式,不需要再介绍了吧。
附:HeaderAdapter的源码:
[java] view plain copy- privatestaticclassHeaderAdapterextendsArrayAdapter<Header>{
- privatestaticclassHeaderViewHolder{
- ImageViewicon;
- TextViewtitle;
- TextViewsummary;
- }
- privateLayoutInflatermInflater;
- publicHeaderAdapter(Contextcontext,List<Header>objects){
- super(context,0,objects);
- mInflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
- @Override
- publicViewgetView(intposition,ViewconvertView,ViewGroupparent){
- HeaderViewHolderholder;
- Viewview;
- if(convertView==null){
- view=mInflater.inflate(com.android.internal.R.layout.preference_header_item,
- parent,false);
- holder=newHeaderViewHolder();
- 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();
- }
- //Allviewfieldsmustbeupdatedeverytime,becausetheviewmayberecycled
- Headerheader=getItem(position);
- holder.icon.setImageResource(header.iconRes);
- holder.title.setText(header.getTitle(getContext().getResources()));
- CharSequencesummary=header.getSummary(getContext().getResources());
- if(!TextUtils.isEmpty(summary)){
- holder.summary.setVisibility(View.VISIBLE);
- holder.summary.setText(summary);
- }else{
- holder.summary.setVisibility(View.GONE);
- }
- returnview;
- }
- }
以上就是PreferenceActivity加载Header的整个流程。不当之处,欢迎大家提出宝贵意见,共同学习。
更多相关文章
- Android移动操作系统源代码
- android中activity的四种加载模式
- Android有效解决加载大图片时内存溢出的问题
- Android 图片加载图片_OOM异常解决
- Android Studio系列(二)使用Android Studio开发/调试整个android系
- Android开发常用代码片段(三)
- Android 性能优化之Java(Android)代码优化 (三)
- Android常用代码之普通及系统权限静默安装APK
- 求助: Android 加载 webview, 点击webview 中网页时间, 如何触发