前面引入主题的废话不多说,也不会说,Preference的加载过程我们就从addPreferencesFromResource()方法开始研究。
addPreferencesFromResource()方法在PreferenceActivity类和PreferenceFragment类(Android3.0以后才有)中都有实现,两个中内容是差不多的(只是PreferenceActivity中的参数this在PreferenceFragment中变成了getActivity(),这个我想大家都可以理解)。PreferenceActivity中方法体如下:
/**
* Inflates the given XML resource and addsthe preference hierarchy to the current
* preference hierarchy.
*
* @param preferencesResId The XML resourceID to inflate.
*/
public void addPreferencesFromResource(intpreferencesResId) {
requirePreferenceManager()
setPreferenceScreen(mPreferenceManager.inflateFromResource(this,
preferencesResId,getPreferenceScreen()));
}
requirePreferenceManager()方法仅仅是为了确保mPreferenceManager变量不为null,无其他用处了。在看setPreferenceScreen()方法之前,我们先看一下PreferenceManager的inflateFromResource()方法,该方法的部分代码如下所示:
public PreferenceScreen inflateFromResource(Contextcontext, int resId,
PreferenceScreen rootPreferences) {
… …
final PreferenceInflater inflater = newPreferenceInflater(context, this);
rootPreferences = (PreferenceScreen)inflater.inflate(resId, rootPreferences, true);
rootPreferences.onAttachedToHierarchy(this);
… …
return rootPreferences;
}
根据PreferenceInflater类名我们可以猜测出该类同LayoutInflater一样是一个解析类,不同的是LayoutInflater用于解析layout布局,而PreferenceInflater用于解析xml布局。解析后得到当前屏幕的根PreferenceScreen对象(当然该对象中包含哪些Preference在此也已经解析清楚了)。onAttachedToHierarchy()方法是Preference对象绑定到Preference层级界面中时进行的一些初始化工作。
下面来看setPreferenceScreen()方法,PreferenceFragment类和PreferenceActivity类的setPreferenceScreen()有一点点小区别,PreferenceActivity类的setPreferenceScreen()方法为当前屏幕设置了一个标题,而PreferenceFragment中没有(应该在某个地方已经默认将该PreferenceScreen的title设置为屏幕标题了),其他实现的功能都是一样的。下面来看PreferenceActivity类的setPreferenceScreen()方法体:
public voidsetPreferenceScreen(PreferenceScreen preferenceScreen) {
requirePreferenceManager();
if(mPreferenceManager.setPreferences(preferenceScreen)
&& preferenceScreen != null) {
postBindPreferences();
CharSequence title = getPreferenceScreen().getTitle();
// Setthe title of the activity
if(title != null) {
setTitle(title);
}
}
}
requirePreferenceManager()方法我们在前面说过,仅仅是确认mPreferenceManager不为空。而PreferenceManager的setPreferences()方法也仅仅是将preferenceScreen赋值给其局部变量mPreferenceScreen。如果发现mPreferenceScreen值改变则返回true,反正则返回false。而getPreferenceScreen()方法最终也是得到的PreferenceManager对象的mPreferenceScreen变量。设置标题的问题我们这里就不说了,这里我们看postBindPreferences()方法:
private void postBindPreferences() {
if(mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
}
即向mHandler对象发送了一个MSG_BIND_PREFERENCES消息。而在mHandler中接收该消息后仅执行了一个bindPreferences()方法,下面我们继续看bindPreference()方法重要代码:
private void bindPreferences() {
final PreferenceScreen preferenceScreen= getPreferenceScreen();
if (preferenceScreen != null) {
preferenceScreen.bind(getListView());
… …
}
}
不用说,大家也知道下一个需要分析的是PreferenceScreen的bind()方法:
public void bind(ListView listView) {
listView.setOnItemClickListener(this);
listView.setAdapter(getRootAdapter());
onAttachedToActivity();
}
差不多了,看到这里我想大家都知道明白了,这就是我们平时使用ListView几乎必用的代码啊!!从这里也可以看出,一个个Preference,其实就是依附在系统自带的ListView控件上显示的。下面就是我们最后一站了:适配器。继续看getRootAdapter()方法:
public ListAdapter getRootAdapter() {
if (mRootAdapter == null) {
mRootAdapter = onCreateRootAdapter();
}
return mRootAdapter;
}
这里没什么可说的,看onCreateRootAdapter()方法:
protected ListAdapter onCreateRootAdapter() {
return newPreferenceGroupAdapter(this);
}
终于拨开层层云雾见晴天了。PreferenceScreen显示的适配器原来是一个PreferenceGroupAdapter对象。这里要注意的是,PreferenceGroupAdapter实现了OnPreferenceChangeInternalListener接口,其中有个方法onPreferenceHierarchyChange(),当有Preference添加或删除时都会调用该方法,故当Preference添加或删除需要做一些动作(如调整Preference背景等)时可在该方法中实现。就分析到这里了,想看具体显示就看该适配的getView()方法了,分析方法跟分析普通BaseAdapter子类没区别。反正最终会调用到Preference类的onCreateView()方法和onBindView()方法。