Android下PreferenceScreen 加载流程

本文详细剖析了Preference在Android中的加载过程,从addPreferencesFromResource()方法入手,深入PreferenceActivity及PreferenceFragment内部实现,揭示了Preference加载背后的机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  前面引入主题的废话不多说,也不会说,Preference的加载过程我们就从addPreferencesFromResource()方法开始研究。

     addPreferencesFromResource()方法在PreferenceActivity类和PreferenceFragment类(Android3.0以后才有)中都有实现,两个中内容是差不多的(只是PreferenceActivity中的参数this在PreferenceFragment中变成了getActivity(),这个我想大家都可以理解)。PreferenceActivity中方法体如下:

  1. /** 
  2.  * Inflates the given XML resource and addsthe preference hierarchy to the current 
  3.  * preference hierarchy. 
  4.  * 
  5.  * @param preferencesResId The XML resourceID to inflate. 
  6.  */  
  7. public void addPreferencesFromResource(intpreferencesResId) {  
  8.           requirePreferenceManager()  
  9.           setPreferenceScreen(mPreferenceManager.inflateFromResource(this,  
  10.                       preferencesResId,getPreferenceScreen()));  
  11. }  
      requirePreferenceManager()方法仅仅是为了确保mPreferenceManager变量不为null,无其他用处了。在看setPreferenceScreen()方法之前,我们先看一下PreferenceManager的inflateFromResource()方法,该方法的部分代码如下所示:
  1. public PreferenceScreen inflateFromResource(Contextcontext, int resId,  
  2.                       PreferenceScreen rootPreferences) {  
  3.                  … …  
  4.   
  5.                  final PreferenceInflater inflater = newPreferenceInflater(context, this);  
  6.   
  7.                  rootPreferences = (PreferenceScreen)inflater.inflate(resId, rootPreferences, true);  
  8.   
  9.                  rootPreferences.onAttachedToHierarchy(this);  
  10.   
  11.                  … …  
  12.   
  13.                  return rootPreferences;  
  14.   
  15.      }  
      根据PreferenceInflater类名我们可以猜测出该类同LayoutInflater一样是一个解析类,不同的是LayoutInflater用于解析layout布局,而PreferenceInflater用于解析xml布局。解析后得到当前屏幕的根PreferenceScreen对象(当然该对象中包含哪些Preference在此也已经解析清楚了)。onAttachedToHierarchy()方法是Preference对象绑定到Preference层级界面中时进行的一些初始化工作。
     下面来看setPreferenceScreen()方法,PreferenceFragment类和PreferenceActivity类的setPreferenceScreen()有一点点小区别,PreferenceActivity类的setPreferenceScreen()方法为当前屏幕设置了一个标题,而PreferenceFragment中没有(应该在某个地方已经默认将该PreferenceScreen的title设置为屏幕标题了),其他实现的功能都是一样的。下面来看PreferenceActivity类的setPreferenceScreen()方法体:

  1. public voidsetPreferenceScreen(PreferenceScreen preferenceScreen) {  
  2.                  requirePreferenceManager();  
  3.                  if(mPreferenceManager.setPreferences(preferenceScreen)  
  4.                                 && preferenceScreen != null) {  
  5.                           postBindPreferences();  
  6.                           CharSequence title = getPreferenceScreen().getTitle();  
  7.                           // Setthe title of the activity  
  8.                           if(title != null) {  
  9.                                    setTitle(title);  
  10.                           }  
  11.                    }  
  12.      }  
      requirePreferenceManager()方法我们在前面说过,仅仅是确认mPreferenceManager不为空。而PreferenceManager的setPreferences()方法也仅仅是将preferenceScreen赋值给其局部变量mPreferenceScreen。如果发现mPreferenceScreen值改变则返回true,反正则返回false。而getPreferenceScreen()方法最终也是得到的PreferenceManager对象的mPreferenceScreen变量。设置标题的问题我们这里就不说了,这里我们看postBindPreferences()方法:
  1. private void postBindPreferences() {  
  2.                 if(mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;  
  3.                 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();  
  4.     }  
      即向mHandler对象发送了一个MSG_BIND_PREFERENCES消息。而在mHandler中接收该消息后仅执行了一个bindPreferences()方法,下面我们继续看bindPreference()方法重要代码:
  1. private void bindPreferences() {  
  2.                  final PreferenceScreen preferenceScreen= getPreferenceScreen();  
  3.                  if (preferenceScreen != null) {  
  4.                            preferenceScreen.bind(getListView());  
  5.                            … …  
  6.                  }  
  7.     }  
     不用说,大家也知道下一个需要分析的是PreferenceScreen的bind()方法:
  1. public void bind(ListView listView) {  
  2.                  listView.setOnItemClickListener(this);  
  3.                  listView.setAdapter(getRootAdapter());  
  4.   
  5.                  onAttachedToActivity();  
  6.   
  7.    }  
      差不多了,看到这里我想大家都知道明白了,这就是我们平时使用ListView几乎必用的代码啊!!从这里也可以看出,一个个Preference,其实就是依附在系统自带的ListView控件上显示的。下面就是我们最后一站了:适配器。继续看getRootAdapter()方法
  1. public ListAdapter getRootAdapter() {  
  2.             if (mRootAdapter == null) {  
  3.                     mRootAdapter = onCreateRootAdapter();  
  4.             }  
  5.   
  6.            return mRootAdapter;  
  7.     }  
     这里没什么可说的,看onCreateRootAdapter()方法:
  1. protected ListAdapter onCreateRootAdapter() {  
  2.             return newPreferenceGroupAdapter(this);  
  3.     }  

      终于拨开层层云雾见晴天了。PreferenceScreen显示的适配器原来是一个PreferenceGroupAdapter对象。这里要注意的是,PreferenceGroupAdapter实现了OnPreferenceChangeInternalListener接口,其中有个方法onPreferenceHierarchyChange(),当有Preference添加或删除时都会调用该方法,故当Preference添加或删除需要做一些动作(如调整Preference背景等)时可在该方法中实现。就分析到这里了,想看具体显示就看该适配的getView()方法了,分析方法跟分析普通BaseAdapter子类没区别。反正最终会调用到Preference类的onCreateView()方法和onBindView()方法。

原文链接:http://blog.csdn.NET/vipclx/article/details/9002034

补充:

     通过上面的讲解,我们知道各个preference被添加到ListView中,即Preference的显示以列表的形式展现给用户。那这个ListView在哪里设置并实例化的呢?而且,如果对Preference比较熟悉的话,我们知道ListView的id是“@Android:id/list”,这个又是怎么来呢。通过下面的代码可知,PreferenceActivity或者PreferenceFragment在实例化显示过程中会首先inflate一个包含listview(其id为@android:id/list)的Layout布局。inflate完成后,在上面的函数bindPreferences中调用getListView()可以获取此ListView实例。具体的代码逻辑如下:
     在PreferenceActivity的onCreate()函数:

  1. protected void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3.   
  4.         setContentView(com.android.internal.R.layout.preference_list_content);  
  5. }  
或者在PreferenceFragment的onCreateView()函数:

  1. @Override  
  2.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  3.             Bundle savedInstanceState) {  
  4.         return inflater.inflate(com.android.internal.R.layout.preference_list_fragment, container,  
  5.                 false);  
  6.     }  
上面两个preference的布局包含id为list的ListView,布局定义如下:
  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:orientation="vertical"  
  3.     android:layout_height="match_parent"  
  4.     android:layout_width="match_parent"  
  5.     android:background="@android:color/transparent"  
  6.     android:layout_removeBorders="true">  
  7.   
  8.     <ListView android:id="@android:id/list"  
  9.         style="?attr/preferenceFragmentListStyle"  
  10.         android:layout_width="match_parent"  
  11.         android:layout_height="0px"  
  12.         android:layout_weight="1"  
  13.         android:paddingTop="0dip"  
  14.         android:paddingBottom="@dimen/preference_fragment_padding_bottom"  
  15.         android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"  
  16.         android:clipToPadding="false"  
  17.         android:drawSelectorOnTop="false"  
  18.         android:cacheColorHint="@android:color/transparent"  
  19.         android:scrollbarAlwaysDrawVerticalTrack="true" />  
  20.   
  21.     <TextView android:id="@android:id/empty"  
  22.         android:layout_width="match_parent"  
  23.         android:layout_height="match_parent"  
  24.         android:padding="@dimen/preference_fragment_padding_side"  
  25.         android:gravity="center"  
  26.         android:visibility="gone" />  
  27. ......  
  28. </LinearLayout>  
至于如何Layout的实例化过程这里不再分析。总之,在实例化preference布局之前代码会首先实例化包含ListView的Layout布局,如此preference以List的形式展示出来。

      在实际的开发过程中,我们会遇到在同一个UI中显示Preference项和自定义的布局项的清形,如何实现呢。方法就是自定义Layout,用于显示定制化界面,并包含listview(其id为@android:id/list),用于显示Preference项;然后重载上面提到的函数onCreate()或者onCreateView(),并inflate自定义的Layout。以PreferenceActivity为例实现如下:

  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.   
  5.     // set customize layout that contains listview  
  6.     setContentView(com.android.internal.R.layout.layout_customize_list;  
  7.     // Load the preferences from an XML resource  
  8.     addPreferencesFromResource(R.xml.preferences);  
  9.     ......  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值