PreferenceActivity与PreferenceFragment理解

一.概要

PreferenceActivity介绍:

  

 PreferenceActivity实际是用来显示一个或多个 preferences header的,而 一个preference header 实际上就是一个与之对应的PreferenceFragment。因此实际上PreferenceActivity中包含了一个活着多个PreferenceFragment,里面有一个 mPrefsContainer(ViewGroup)来保存,Fragment在Activiy中就是以ViewGroup来看待的。想要使用这个类一般需要去实现onBuildHeaders(List)  (除非不包含PreferenceFragment),一般在个方法中你调用loadHeadersFromResource()去加载Headers.


2. 关键的几个类

    1)PreferenceActivity  

    2)PreferenceFragment 它只是一个Frament,里面有一个PreferenceScreen 和 PreferenceManage 实例

    3)PreferenceScreen   一个hierarchies的root

    4)PreferenceCategroy

    5)PreferenceManage 帮忙去创建和管理preference hierarchies, 里面有一个sharedpreference

    6)各种Preference子类:

CheckBoxPreferenceEditTextPreferenceListPreferenceMultiSelectListPreferencePreferenceCategoryPreferenceScreenSwitchPreference

     

    7)SharedPreference  Android中的一种存储机制,采用map的方式 


下面是PreferenceFragment 的介绍。

1. PreferenceScreen:设置页面,可嵌套形成二级设置页面,用Title参数设置标题(这个还可以作为单独的activity来启动设置界面,暂没有研究)。
2. PreferenceCategory:某一类相关的设置,可用Title参数设置标题,相当于一个分界线的东西一样,它的上面和下面分别属于不同类型的设置。
3. CheckBoxPreference:是一个CheckBox设置,只有两种值,true或false,可用Title参数设置标题,用summaryOn和summaryOff参数来设置控件选中和未选中时的提示,可以用defaultValue设置缺省值。
4. ListPreference:下拉框选择控件,用Title参数设置标题,用Summary参数设置说明,点击后出现下拉框,用dialogTitle设置下拉框的标题,下拉框内显示的内容和具体的值需要在res/values/array.xml中设置两个array来表示,entries和entryValues分别表示显示的值和代码中获取的真正的值。
5. EditTextPreference:输入框控件,点击后可输入字符串设置。用Title参数设置标题,Summary参数设置说明,dialogTitle参数设置输入框的标题。
6. RingtonePreference:铃声选择框,点击后可选择系统铃声。Title参数设置标题,Summary参数设置说明,dialogTitle参数设置铃声选择框的标题。
以上是xml描述,那么在程序中我们只需要新建一个继承自PreferenceActivity的 Activity,然后在主程序中调用就可以了。这个PreferenceActivity中的设置存储是完全自动的,你不需要再用代码去实现设置的存储,PreferenceActivity创建后会自动创建一个配置文件/data/data/your_package_name /shared_prefs/(your_package_name)_preferences.xml。

使用保存的参数值:
  PreferenceManager。管理 PreferenceActivity 中的设置,属于整个应用程序,但是只有一个。
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); 
        boolean check_test = sp.getBoolean("checkbox_key", false); 

PreferenceActivity中的内容改变时,Android系统会自动进行保存和持久化维护,我们只需要在要用的设置界面中数据的地方进行读取就可以了。同时Android还提供了OnPreferenceClickListenerOnPreferenceChangeListener两个与Preference相关的监听接口,当PreferenceActivity中的某一个Preference进行了点击或者改变的操作时,都会回调接口中的函数,这样可以第一个时间向其他Activity等通知系统设置进行了改变。

Creating a Preference Activity


Subclasses of PreferenceActivity should implement onBuildHeaders(List) to populate the header list with the desired items. Doing this implicitly switches the class into its new "headers + fragments" mode rather than the old style of just showing a single preferences list.
代码示例:
public class PreferenceWithHeaders extends PreferenceActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Add a button to the header list.
        if (hasHeaders()) {
            Button button = new Button(this);
            button.setText("Some action");
            setListFooter(button);
        }
    }

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

    /**
     * This fragment shows the preferences for the first header.
     */
    public static class Prefs1Fragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Make sure default values are applied.  In a real app, you would
            // want this in a shared function that is used to retrieve the
            // SharedPreferences wherever they are needed.
            PreferenceManager.setDefaultValues(getActivity(),
                    R.xml.advanced_preferences, false);

            // Load the preferences from an XML resource
            addPreferencesFromResource(R.xml.fragmented_preferences);
        }
    }

    /**
     * This fragment contains a second-level set of preference that you
     * can get to by tapping an item in the first preferences fragment.
     */
    public static class Prefs1FragmentInner extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Can retrieve arguments from preference XML.
            Log.i("args", "Arguments: " + getArguments());

            // Load the preferences from an XML resource
            addPreferencesFromResource(R.xml.fragmented_preferences_inner);
        }
    }

    /**
     * This fragment shows the preferences for the second header.
     */
    public static class Prefs2Fragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Can retrieve arguments from headers XML.
            Log.i("args", "Arguments: " + getArguments());

            // Load the preferences from an XML resource
            addPreferencesFromResource(R.xml.preference_dependencies);
        }
    }
}

The preference_headers resource describes the headers to be displayed and the fragments associated with them. It is:

<preference-headers
        xmlns:android="http://schemas.android.com/apk/res/android">

    <header android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs1Fragment"
            android:icon="@drawable/ic_settings_applications"
            android:title="Prefs 1"
            android:summary="An example of some preferences." />

    <header android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs2Fragment"
            android:icon="@drawable/ic_settings_display"
            android:title="Prefs 2"
            android:summary="Some other preferences you can see.">
        <!-- Arbitrary key/value pairs can be included with a header as
             arguments to its fragment. -->
        <extra android:name="someKey" android:value="someHeaderValue" />
    </header>

    <header android:icon="@drawable/ic_settings_display"
            android:title="Intent"
            android:summary="Launches an Intent.">
        <intent android:action="android.intent.action.VIEW"
                android:data="http://www.android.com" />
    </header>

</preference-headers>

The first header is shown by Prefs1Fragment, which populates itself from the following XML resource:

<PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
            android:title="@string/inline_preferences">

        <CheckBoxPreference
                android:key="checkbox_preference"
                android:title="@string/title_checkbox_preference"
                android:summary="@string/summary_checkbox_preference" />

    </PreferenceCategory>

    <PreferenceCategory
            android:title="@string/dialog_based_preferences">

        <EditTextPreference
                android:key="edittext_preference"
                android:title="@string/title_edittext_preference"
                android:summary="@string/summary_edittext_preference"
                android:dialogTitle="@string/dialog_title_edittext_preference" />

        <ListPreference
                android:key="list_preference"
                android:title="@string/title_list_preference"
                android:summary="@string/summary_list_preference"
                android:entries="@array/entries_list_preference"
                android:entryValues="@array/entryvalues_list_preference"
                android:dialogTitle="@string/dialog_title_list_preference" />

    </PreferenceCategory>

    <PreferenceCategory
            android:title="@string/launch_preferences">

        <!-- This PreferenceScreen tag sends the user to a new fragment of
             preferences.  If running in a large screen, they can be embedded
             inside of the overall preferences UI. -->
        <PreferenceScreen
                android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs1FragmentInner"
                android:title="@string/title_fragment_preference"
                android:summary="@string/summary_fragment_preference">
            <!-- Arbitrary key/value pairs can be included for fragment arguments -->
            <extra android:name="someKey" android:value="somePrefValue" />
        </PreferenceScreen>

        <!-- This PreferenceScreen tag sends the user to a completely different
             activity, switching out of the current preferences UI. -->
        <PreferenceScreen
                android:title="@string/title_intent_preference"
                android:summary="@string/summary_intent_preference">

            <intent android:action="android.intent.action.VIEW"
                    android:data="http://www.android.com" />

        </PreferenceScreen>

    </PreferenceCategory>

    <PreferenceCategory
            android:title="@string/preference_attributes">

        <CheckBoxPreference
                android:key="parent_checkbox_preference"
                android:title="@string/title_parent_preference"
                android:summary="@string/summary_parent_preference" />

        <!-- The visual style of a child is defined by this styled theme attribute. -->
        <CheckBoxPreference
                android:key="child_checkbox_preference"
                android:dependency="parent_checkbox_preference"
                android:layout="?android:attr/preferenceLayoutChild"
                android:title="@string/title_child_preference"
                android:summary="@string/summary_child_preference" />

    </PreferenceCategory>

</PreferenceScreen>



PreferenceFragment注意:
To inflate from XML, use the addPreferencesFromResource(int). The root element should be a PreferenceScreen. Subsequent elements can point to actual Preference subclasses
To specify an Intent to query Activities that each have preferences, use addPreferencesFromIntent(Intent). Each Activity can specify meta-data in the manifest (via the key METADATA_KEY_PREFERENCES) that points to an XML resource. 若在xml文件中定义则可忽略

Each Preference subclass can be declared with an XML element that matches the class name, such as<CheckBoxPreference>.

You must save the XML file in the res/xml/ directory. Although you can name the file anything you want, it's traditionally named preferences.xml. You usually need only one file, because branches in the hierarchy (that open their own list of settings) are declared using nested instances of PreferenceScreen.

Note: If you want to create a multi-pane layout for your settings, then you need separate XML files for each fragment.

The root node for the XML file must be a <PreferenceScreen> element. Within this element is where you add each Preference. Each child you add within the <PreferenceScreen> element appears as a single item in the list of settings.

public static class PrefsFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

For example:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <CheckBoxPreference
        android:key="pref_sync"
        android:title="@string/pref_sync"
        android:summary="@string/pref_sync_summ"
        android:defaultValue="true" />
    <ListPreference
        android:dependency="pref_sync"
        android:key="pref_syncConnectionType"
        android:title="@string/pref_syncConnectionType"
        android:dialogTitle="@string/pref_syncConnectionType"
        android:entries="@array/pref_syncConnectionTypes_entries"
        android:entryValues="@array/pref_syncConnectionTypes_values"
        android:defaultValue="@string/pref_syncConnectionTypes_default" />
</PreferenceScreen>
Using titles

If you want to provide dividers with headings between groups of settings (as shown in figure 2), place each group of Preference objects inside a PreferenceCategory.

For example:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory 
        android:title="@string/pref_sms_storage_title"
        android:key="pref_key_storage_settings">
        <CheckBoxPreference
            android:key="pref_key_auto_delete"
            android:summary="@string/pref_summary_auto_delete"
            android:title="@string/pref_title_auto_delete"
            android:defaultValue="false"... />
        <Preference 
            android:key="pref_key_sms_delete_limit"
            android:dependency="pref_key_auto_delete"
            android:summary="@string/pref_summary_delete_limit"
            android:title="@string/pref_title_sms_delete"... />
        <Preference 
            android:key="pref_key_mms_delete_limit"
            android:dependency="pref_key_auto_delete"
            android:summary="@string/pref_summary_delete_limit"
            android:title="@string/pref_title_mms_delete" ... />
    </PreferenceCategory>
    ...
</PreferenceScreen>

Using intents

In some cases, you might want a preference item to open a different activity instead of a settings screen, such as a web browser to view a web page. To invoke an Intent when the user selects a preference item, add an<intent> element as a child of the corresponding <Preference> element.

For example, here's how you can use a preference item to open a web page:

<Preference android:title="@string/prefs_web_page" >
    <intent android:action="android.intent.action.VIEW"
            android:data="http://www.example.com" />
</Preference>

Reading Preferences


By default, all your app's preferences are saved to a file that's accessible from anywhere within your application by calling the static method PreferenceManager.getDefaultSharedPreferences(). This returns theSharedPreferences object containing all the key-value pairs that are associated with the Preference objects used in your PreferenceActivity.

For example, here's how you can read one of the preference values from any other activity in your application:

SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
String syncConnPref = sharedPref.getString(SettingsActivity.KEY_PREF_SYNC_CONN, "");

Listening for preference changes

There are several reasons you might want to be notified as soon as the use changes one of the preferences. In order to receive a callback when a change happens to any one of the preferences, implement theSharedPreference.OnSharedPreferenceChangeListener interface and register the listener for theSharedPreferences object by calling registerOnSharedPreferenceChangeListener().

The interface has only one callback method, onSharedPreferenceChanged(), and you might find it easiest to implement the interface as a part of your activity. For example:

public class SettingsActivity extends PreferenceActivity
                              implements OnSharedPreferenceChangeListener {
    public static final String KEY_PREF_SYNC_CONN = "pref_syncConnectionType";
    ...

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals(KEY_PREF_SYNC_CONN)) {
            Preference connectionPref = findPreference(key);
            // Set summary to be the user-description for the selected value
            connectionPref.setSummary(sharedPreferences.getString(key, ""));
        }
    }
}

In this example, the method checks whether the changed setting is for a known preference key. It callsfindPreference() to get the Preference object that was changed so it can modify the item's summary to be a description of the user's selection. That is, when the setting is a ListPreference or other multiple choice setting, you should call setSummary() when the setting changes to display the current status (such as the Sleep setting shown in figure 5).

Note: As described in the Android Design document about Settings, we recommend that you update the summary for a ListPreference each time the user changes the preference in order to describe the current setting.

For proper lifecycle management in the activity, we recommend that you register and unregister yourSharedPreferences.OnSharedPreferenceChangeListener during the onResume() and onPause()callbacks, respectively:

@Override
protected void onResume() {
    super.onResume();
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}

@Override
protected void onPause() {
    super.onPause();
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}


高级主题,自定义Preference:

When you extend the Preference class, there are a few important things you need to do:

  • Specify the user interface that appears when the user selects the settings.
  • Save the setting's value when appropriate.
  • Initialize the Preference with the current (or default) value when it comes into view.
  • Provide the default value when requested by the system.
  • If the Preference provides its own UI (such as a dialog), save and restore the state to handle lifecycle changes (such as when the user rotates the screen).

The following sections describe how to accomplish each of these tasks.

Specifying the user interface


If you directly extend the Preference class, you need to implement onClick() to define the action that occurs when the user selects the item. However, most custom settings extend DialogPreference to show a dialog, which simplifies the procedure. When you extend DialogPreference, you must callsetDialogLayoutResourcs() during in the class constructor to specify the layout for the dialog.

For example, here's the constructor for a custom DialogPreference that declares the layout and specifies the text for the default positive and negative dialog buttons:

public class NumberPickerPreference extends DialogPreference {
    public NumberPickerPreference(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        setDialogLayoutResource(R.layout.numberpicker_dialog);
        setPositiveButtonText(android.R.string.ok);
        setNegativeButtonText(android.R.string.cancel);
        
        setDialogIcon(null);
    }
    ...
}

Saving the setting's value

You can save a value for the setting at any time by calling one of the Preference class's persist*() methods, such as persistInt() if the setting's value is an integer or persistBoolean() to save a boolean.

Note: Each Preference can save only one data type, so you must use the persist*() method appropriate for the data type used by your custom Preference.

When you choose to persist the setting can depend on which Preference class you extend. If you extendDialogPreference, then you should persist the value only when the dialog closes due to a positive result (the user selects the "OK" button).

When a DialogPreference closes, the system calls the onDialogClosed() method. The method includes a boolean argument that specifies whether the user result is "positive"—if the value is true, then the user selected the positive button and you should save the new value. For example:

@Override
protected void onDialogClosed(boolean positiveResult) {
    // When the user selects "OK", persist the new value
    if (positiveResult) {
        persistInt(mNewValue);
    }
}

Initializing the current value

When the system adds your Preference to the screen, it calls onSetInitialValue() to notify you whether the setting has a persisted value. If there is no persisted value, this call provides you the default value.

The onSetInitialValue() method passes a boolean, restorePersistedValue, to indicate whether a value has already been persisted for the setting. If it is true, then you should retrieve the persisted value by calling one of the Preference class's getPersisted*() methods, such as getPersistedInt() for an integer value. You'll usually want to retrieve the persisted value so you can properly update the UI to reflect the previously saved value.

If restorePersistedValue is false, then you should use the default value that is passed in the second argument.

@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    if (restorePersistedValue) {
        // Restore existing state
        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
    } else {
        // Set default state from the XML attribute
        mCurrentValue = (Integer) defaultValue;
        persistInt(mCurrentValue);
    }
}

Providing a default value

If the instance of your Preference class specifies a default value (with the android:defaultValue attribute), then the system calls onGetDefaultValue() when it instantiates the object in order to retrieve the value. You must implement this method in order for the system to save the default value in the SharedPreferences. For example:

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
    return a.getInteger(index, DEFAULT_VALUE);
}

Saving and restoring the Preference's state

Just like a View in a layout, your Preference subclass is responsible for saving and restoring its state in case the activity or fragment is restarted (such as when the user rotates the screen). To properly save and restore the state of your Preference class, you must implement the lifecycle callback methods onSaveInstanceState()and onRestoreInstanceState().

The state of your Preference is defined by an object that implements the Parcelable interface. The Android framework provides such an object for you as a starting point to define your state object: thePreference.BaseSavedState class.

private static class SavedState extends BaseSavedState {
    // Member that holds the setting's value
    // Change this data type to match the type saved by your Preference
    int value;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        // Get the current preference's value
        value = source.readInt();  // Change this to read the appropriate data type
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        // Write the preference's value
        dest.writeInt(value);  // Change this to write the appropriate data type
    }

    // Standard creator object using an instance of this class
    public static final Parcelable.Creator<SavedState> CREATOR =
            new Parcelable.Creator<SavedState>() {

        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

With the above implementation of Preference.BaseSavedState added to your app (usually as a subclass of your Preference subclass), you then need to implement the onSaveInstanceState() andonRestoreInstanceState() methods for your Preference subclass.

For example:

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    // Check whether this Preference is persistent (continually saved)
    if (isPersistent()) {
        // No need to save instance state since it's persistent, use superclass state
        return superState;
    }

    // Create instance of custom BaseSavedState
    final SavedState myState = new SavedState(superState);
    // Set the state's value with the class member that holds current setting value
    myState.value = mNewValue;
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    // Check whether we saved the state in onSaveInstanceState
    if (state == null || !state.getClass().equals(SavedState.class)) {
        // Didn't save the state, so call superclass
        super.onRestoreInstanceState(state);
        return;
    }

    // Cast state to custom BaseSavedState and pass to superclass
    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    
    // Set this Preference's widget to reflect the restored state
    mNumberPicker.setValue(myState.value);
}
private static class SavedState extends BaseSavedState {
    // Member that holds the setting's value
    // Change this data type to match the type saved by your Preference
    int value;

    public SavedState(Parcelable superState) {
        super(superState);
    }

    public SavedState(Parcel source) {
        super(source);
        // Get the current preference's value
        value = source.readInt();  // Change this to read the appropriate data type
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        // Write the preference's value
        dest.writeInt(value);  // Change this to write the appropriate data type
    }

    // Standard creator object using an instance of this class
    public static final Parcelable.Creator<SavedState> CREATOR =
            new Parcelable.Creator<SavedState>() {

        public SavedState createFromParcel(Parcel in) {
            return new SavedState(in);
        }

        public SavedState[] newArray(int size) {
            return new SavedState[size];
        }
    };
}

With the above implementation of Preference.BaseSavedState added to your app (usually as a subclass of your Preference subclass), you then need to implement the onSaveInstanceState() andonRestoreInstanceState() methods for your Preference subclass.




 主要参考文章:
blog.csdn.net/ichliebephone/article/details/5916320
android api文档
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值