android下PreferenceScreen 加载流程

前面引入主题的废话不多说,也不会说,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()方法。

原文链接: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()函数:

protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(com.android.internal.R.layout.preference_list_content);}
或者在PreferenceFragment的onCreateView()函数:
@Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        return inflater.inflate(com.android.internal.R.layout.preference_list_fragment, container,                false);    }
上面两个preference的布局包含id为list的ListView,布局定义如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_height="match_parent"    android:layout_width="match_parent"    android:background="@android:color/transparent"    android:layout_removeBorders="true">    <ListView android:id="@android:id/list"        style="?attr/preferenceFragmentListStyle"        android:layout_width="match_parent"        android:layout_height="0px"        android:layout_weight="1"        android:paddingTop="0dip"        android:paddingBottom="@dimen/preference_fragment_padding_bottom"        android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"        android:clipToPadding="false"        android:drawSelectorOnTop="false"        android:cacheColorHint="@android:color/transparent"        android:scrollbarAlwaysDrawVerticalTrack="true" />    <TextView android:id="@android:id/empty"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:padding="@dimen/preference_fragment_padding_side"        android:gravity="center"        android:visibility="gone" />......</LinearLayout>
至于如何Layout的实例化过程这里不再分析。总之,在实例化preference布局之前代码会首先实例化包含ListView的Layout布局,如此preference以List的形式展示出来。

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

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // set customize layout that contains listview        setContentView(com.android.internal.R.layout.layout_customize_list;        // Load the preferences from an XML resource        addPreferencesFromResource(R.xml.preferences);        ......    }



更多相关文章

  1. Android(安卓)Service源码分析
  2. 【Android-tips】 Unable to execute dex: Multiple dex files d
  3. android中GridView关于间距的属性值介绍
  4. Android如何获取视频预览图(或首帧)和获取视频时长
  5. android顶部栏属性ActionBar Options
  6. android中menu的使用
  7. Android(安卓)Animation开机动画的优化
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)监听主进程被杀
  2. Android(安卓)Studio3.0 apk安装时提示受
  3. Android(安卓)开发学习进程0.15 adb card
  4. Android(安卓)Studio 使用WiFi(无线网)调试
  5. android notification的用法
  6. Android四大组件基本介绍及其生命周期
  7. 初学android第一章、第二章
  8. 谷歌浏览器无法保存密码
  9. 38、android studio3.2 Indexing paused
  10. Android文件命名规范