首选项框架PreferenceFragment部分源码分析

来源:互联网 发布:js function中onclick 编辑:程序博客网 时间:2024/05/18 01:40

     因为要改一些settings里面的bug以及之前在里面有做过勿扰模式,准备对勿扰模式做一个总结,那先分析一下settings的源码,里面的核心应该就是android3.0 上面的首选项框架PreferenceFragment。因为在3.0之前都是这些东西放在PreferenceActivity的,但是3.0之后google建议把setting放在PreferenceFragment,但是PreferenceActivity也同时在用的,下面就此总结一下:

    PreferenceActivity作用是管理所有headers,而每一个header对应一个PreferenceFragment,对应不同的设置种类界面所以我感觉PreferenceActivity偏向系统级的设置,就比如android系统的settings,对于一些简单的应用设置一个PreferenceFragment是可以搞定的。

    我们自定义一个activity extends PreferenceActivity必须重写onBuildHeaders()这个方法,在这里加载一个xml布局显示:

@Overridepublic void onBuildHeaders(List<Header> target) {// TODO Auto-generated method stubloadHeadersFromResource(R.xml.activity_headers, target);}

自己写一个xml文件看下,R.xml.activity_headers:

<?xml version="1.0" encoding="utf-8"?><preference-headers xmlns:android="http://schemas.android.com/apk/res/android" >    <header        android:id="@+id/wifi"        android:title="wifi"        android:icon="@drawable/ic_launcher"        android:fragment="com.example.preferencefragmenttest.WifiFragment"/>         <header        android:id="@+id/net"        android:title="移动网络"        android:icon="@drawable/ic_launcher"        android:fragment="com.example.preferencefragmenttest.NetFragment"/>          <header        android:id="@+id/ee"        android:title="打开activity"        android:icon="@drawable/ic_launcher"        >        <intent android:action="android.intent.action.NEW"></intent></header></preference-headers>

每一个header点击就会进入一个新的界面,有两种情况:要么是fragment要么是intent打开一个activity,看看源码了解这里的原理,为什么点击就会进入相应界面,找到父类PreferenceActivity有个方法:

public void onHeaderClick(Header header, int position) {        if (header.fragment != null) {            if (mSinglePane) {                int titleRes = header.breadCrumbTitleRes;                int shortTitleRes = header.breadCrumbShortTitleRes;                if (titleRes == 0) {                    titleRes = header.titleRes;                    shortTitleRes = 0;                }                startWithFragment(header.fragment, header.fragmentArguments, null, 0,                        titleRes, shortTitleRes);            } else {                switchToHeader(header);            }        } else if (header.intent != null) {            startActivity(header.intent);        }    }

     很明显可以看出它判断了header对应fragment和activity的两种情况,打开activity这个好理解,看看fragment是怎么打开的,进入switchToHeader(),再进去switchToHeaderInner():

private void switchToHeaderInner(String fragmentName, Bundle args, int direction) {        getFragmentManager().popBackStack(BACK_STACK_PREFS,                FragmentManager.POP_BACK_STACK_INCLUSIVE);        if (!isValidFragment(fragmentName)) {            throw new IllegalArgumentException("Invalid fragment for this activity: "                    + fragmentName);        }        Fragment f = Fragment.instantiate(this, fragmentName, args);        FragmentTransaction transaction = getFragmentManager().beginTransaction();        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);        transaction.replace(com.android.internal.R.id.prefs, f);        transaction.commitAllowingStateLoss();    }

    很明确的知道这个跳转的fragment被装载在当前的activity了,也是告诉我们不需要再new 自己的activity来装这个fragment,我们只需要在header里面写好fragment的属性。那么为啥有的header还是用intent定activity呢。我分析是因为有些系统界面需要别的应用直接intent访问,比如WIFI设置界面:

startActivity(new Intent( android.provider.Settings.ACTION_WIRELESS_SETTINGS)); 

    那当然需要想自己再建一个activity了,接着就是PreferenceFragment的用法,也是自定义fragment extendsPreferenceFragment,并且加载xml布局:

@Overridepublic void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);addPreferencesFromResource(R.xml.wifi_preference);}

看一下这个布局,简单的写几个系统自带的preference:

<?xml version="1.0" encoding="utf-8"?><PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">    <CheckBoxPreference        android:key="isopen"        android:title="wifi"        android:defaultValue="true"/>    <ListPreference        android:key="list"        android:title="声音大小"        android:entries="@array/keys"        android:entryValues="@array/values"/>    <Preference        android:key="click"        android:title="单独的"        /></PreferenceScreen>
   
      还可以在里面加入PreferenceCategory标签,作用是分块吧,中间间隔稍微大一些,区分同一页面不同设置类别

<PreferenceCategory >        <cn.com.demo.ota.MyButtonPreference             android:key="start_install"            android:title="@string/update_install"/>    </PreferenceCategory>

      PreferenceFragment里面有个方法onPreferenceTreeClick()可以重写,但是按照上面的布局发现此方法不起作用。看下这个方法源码:

 /**     * {@inheritDoc}     */    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,            Preference preference) {        if (preference.getFragment() != null &&                getActivity() instanceof OnPreferenceStartFragmentCallback) {            return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment(                    this, preference);        }        return false;    }

     看的出来,必须要这个被点击的preference含有对应的fragment,否则返回false点击不起作用,那么一般我们用
update_more = (Preference)findPreference("update_more");<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;"></span></span></span><pre name="code" class="java">update_more.setOnPreferenceClickListener(new OnPreferenceClickListener() {@Overridepublic boolean onPreferenceClick(Preference preference) {}});



       来设置preference的点击事件,最后一点怎么得到我们保存的数值呢?在该应用的目录下可以找到 包名_preference.xml的文件,里面就是我们保存的数值 调用getDefaultSharedPreferences就可以拿到这个SharedPreferences对象从而取到数值。

        当然上面说的都是最基础的用法,看settings的源代码里面的preference都是自定义的,用listview显示不同类型的preference,而且看Settings.java的一个方法:

/**     * Populate the activity with the top-level headers.     */    @Override    public void onBuildHeaders(List<Header> headers) {        loadHeadersFromResource(R.xml.iradar_settings_headers, headers);        updateHeaderList(headers);    }

       在加载完xml文件后还调用了uodateHeadersList(headers)这里其实就是一个动态更新,不同系统版本用户权限不一样,呈现出来的设置界面也不一样。




    

1 0
原创粉丝点击