閱讀921 返回首頁    go 技術社區[雲棲]


係統深入學習android-Android框架學習-5.設置(Settings)

應用程序通常包括允許用戶修改應用程序的特性和行為的設置功能。例如,一些應用程序允許用戶指定通知是否啟用或指定多久使用雲同步數據。如果你想要為你的應用程序提供設置,你應該使用Android的Preference APIs來構建統一的接口。本章的主角就是Preference,下麵先讓我們看一下圖5-1:

:

圖5-1 這是android短信息應用程序的設置界麵截圖。它使用就是就是Preference

5.1 概述

相比使用View對象來構建用戶接main,設置是構建Preference的子類。一個Preference對象是構建一個單一設置的一個部分。每一個Preference作為一個item在list並為用戶修改設置提供了適當的界麵。例如,一個CheckBoxPreference創建一個用於顯示checkbox的list item,ListPreference創建一個選擇列表來顯示一個對話框的item。每一個Preference其實都以鍵值對的形式保存在你應用程序的SharedPreferences文件中。當用戶改變設置時,係統會更新SharedPreferences文件中的鍵值對。我們隻需要讀取文件中的設置數據即可。SharedPreferences支持以下數據類型的保存:

Boolean

Float

Int

Long

String

String Set

因為你的應用程序設置界麵是使用Preference對象構建的而不是view,你需要使用Activity或Fragment的子類來顯示設置列表:

◆    如果你的應用程序支持android 3.0以下版本,你必須使用PreferenceActivity類來構建。

◆    如果高於或等於android 3.0版本,你可以使用PreferenceFragment。當然你屏幕如果足夠大的話你還是可以使用PreferenceActivity創建雙麵板布局來顯示多組設置

5.1.1 Preference

你應用中的每一個設置都代表一個Preference對象。每一個Preference的子類包含一組核心的屬性,如允許你指定設置的標題和默認值這樣的屬性。每一個子類也提供自己的屬性和用戶界麵。就想上麵圖5-1那樣,每一個設置都是List View中的一個item,也是一個Preference對象。常見的Preference如下:

CheckBoxPreference

用checkbox顯示一個item的設置是否為打開或關閉。他保存的是boolean值,true表示選中

ListPreference

以單選按鈕列表的形式打開一個對話框,保存的值能支持任意類型

EditTextPreference

使用EditText打開一個對話框。保存的值為一個String。

5.2 在XML中定義Preferences

雖然你可以在運行時實例化新的Preference對象,但你也可以在XML中用Preference層級對象來定義。使用XML定義設置是首選,因為XML文件結構更容易閱讀的並且更新也很簡單。此外,你的應用程序的設置通常是預先確定的,但你仍然可以在運行時修改它們。每一個Preference子類都能使用XML節點來匹配聲明。如<CheckBoxPreference>。你必須在項目的res/xml目錄下保存這種XML文件。盡管你可以任意命名你的文件名字,但建議使用preferences.xml,方便以後識別自己寫的東西。注意如果你想要為你的設置創建多麵板布局,那你需要為每一個fragment創建單獨的XML文件。

根節點的XML文件必須是一個< PreferenceScreen >元素。在這個元素中你可以添加每個Preference。每個你添加在< PreferenceScreen >元素下的子節點顯示為單一列表項的設置。如代碼清單5-1所示:

複製代碼
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="https://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>
複製代碼

 

代碼清單5-1

在上麵的例子中有一個CheckBoxPreference和一個ListPreference。這兩個items包含以下三個屬性:

◆android:key

這個屬性是必須的,對於一個preferences 來說是一個持久的數據值。當在SharedPreferences中保存這個Setting值時這個指定唯一的key(一個字符串)是被係統使用的。但某些特殊情況,如preferences是一個PreferenceCategory或 PreferenceScreen,或者是一個XML中<Intent>調用時,又或者是一個Fragment顯示時(用android:fragment屬性),以上這些特殊情況下,這個key就不是必須的了。

◆android:title

為設置提供了一個用戶可見的名稱。

◆android:defaultValue

這指定初始值,係統應該建立在SharedPreferences文件。你應該為所有設置提供一個默認值。

關於其他更多屬性,請直接查看Preference文檔。

 

圖 5-2 根據title的設置分類
1. 通過指定<PreferenceCategory>節點的分類 
2. 通過使用android:title指定title分類.

當你的列表設置超過大約10項,您可能想通過添加標題定義分組設置或在一個單獨的屏幕顯示這些組。

5.2.1創建設置組(groups)

如果你列出10個或更多的設置,用戶可能會有些頭疼。這樣我們就可以使用分組。以下有兩種分組方法:

◆使用titles

◆使用subscreens

1. 使用titles

如果你想要根據標題來提供分界線,請使用PreferenceCategory如代碼清單5-2所示:

複製代碼
<PreferenceScreen xmlns:android="https://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>
複製代碼

 

代碼清單5-2

2. 使用subscreens

 

如果你想要放置設置組到一個subscreen中,請使用PreferenceScreen如圖5-3和代碼清單5-3:

 

圖 5-3

複製代碼
<PreferenceScreen  xmlns:android="https://schemas.android.com/apk/res/android">
    <!--打開一個subscreen -->
    <PreferenceScreen
        android:key="button_voicemail_category_key"
        android:title="@string/voicemail"
        android:persistent="false">
        <ListPreference
            android:key="button_voicemail_provider_key"
            android:title="@string/voicemail_provider" ... />
        <!--打開另一個嵌套的subscreen -->
        <PreferenceScreen
            android:key="button_voicemail_setting_key"
            android:title="@string/voicemail_settings"
            android:persistent="false">
            ...
        </PreferenceScreen>
        <RingtonePreference
            android:key="button_voicemail_ringtone_key"
            android:title="@string/voicemail_ringtone_title"
            android:ringtoneType="notification" ... />
        ...
    </PreferenceScreen>
    ...
</PreferenceScreen>
複製代碼

 

代碼清單5-3

5.2.2使用Intents

某些情況下, 你可能想要一個preference item來打開不同的activity而不是設置屏幕,就像一個web瀏覽器來查看一個web頁麵。當用戶選擇一個preference item時可以調用Intent來啟動。方法就是添加一個<intent>節點到<Preference>節點中。如代碼清單5-4所示:

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

 

代碼清單5-4

 

你能使用以下屬性創建隱式和顯式的intents:

android:action

如同setAction()方法一樣設置action

android:data

如同setData()方法一樣設置data

◆android:mimeType

如同setType()方法一樣設置MIME類型

◆android:targetClass

如同setComponent()方法一樣設置組件類名

◆android:targetPackage

如同setComponent()方法一樣設置組件包名

5.3 創建一個Preference Activity

為了在Acitivity中顯示你的設置,你可以繼承PreferenceActivity類。這是擴展於傳統Activity的一個類,它基於Preference對象層級來顯示一個設置列表。當用戶做出一個改變時PreferenceActivity能自動保存與每一個Preference相關的設置。注意:如果在3.0或以上係統版本中,你應該使用PreferenceFragment。最重要的是要記住,你在onCreate()回調期間沒有加載一個視圖的布局。而是調用addPreferencesFromResource()來添加你定義的XML文件。例如代碼清單5-5所示:

複製代碼
public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
}
複製代碼

 

代碼清單5-5

隻要用戶修改preference,係統將改變保存到一個默認的SharedPreferences文件。

5.4 使用Preference Fragments

如果你在android3.0或更高版本上開發,你應該使用PreferenceFragment來顯示Preference  對象列表。你不應該在使用PreferenceActivity了。因為Fragments提供更為靈活的應用程序結構如代碼清單5-6所示:

複製代碼
public static class SettingsFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
    }
    ...
}
複製代碼

 

代碼清單5-6

然後你能吧這個fragment添加到Activity,如代碼清單5-7所示:

複製代碼
public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }
}
複製代碼

 

代碼清單5-7

注意:一個PreferenceFragment沒有它自己的Context對象。如果你需要一個Content對象,你能調用getActivity()方法。然而,當fragment沒有附加到activity中或者activity聲明周期結束時分離後,你使用getActivity()返回的將是null。

5.5 Setting的默認值

    你創建preferences可能是為你的應用程序定義一些重要的行為,所以當用戶第一次打開你的應用程序時,為每一個Preference相關的SharedPreferences 文件初始化默認值是必要的。首先你必須為每一個Preference對象指定一個默認值,你可以在XML文件中使用android:defaultValue屬性。例如代碼清單5-8所示:

複製代碼
<CheckBoxPreference
    android:defaultValue="true"
    ... />
 
<ListPreference
    android:defaultValue="@string/pref_syncConnectionTypes_default"
    ... />
複製代碼

 

代碼清單5-8

然後,在Main Activity裏的onCreate()方法中調用一次setDefaultValues(),如代碼清單5-9所示:

PreferenceManager.setDefaultValues(this, R.xml.advanced_preferences, false);

 

代碼清單5-9

在onCreate()方法的最開始就可以執行此方法,因為可能你的界麵需要依據默認值來設置一個屬性。這個方法中有三個參數:

1.應用程序的Context

2.Preference XML資源ID

3.這個boolean表示是否多次設置默認值,當然大部分情況下默認值一般我們隻需要設置一次,就傳false即可

5.6 使用Preference Headers

在少數情況下,如用首次屏幕顯示的時候,你可能想要讓用戶先設置一些配置屬性。在android3.0或更高版本係統下,你可以使用新的“headers”功能來代替以前的subscreens的嵌套。使用headers步驟如下:

1. 單獨的每組設置作為獨立PreferenceFragment的實例。即,每組設置需要一個單獨的XML文件。

2. 創建一個XML頭文件,其中列出了每個設置組和聲明這fragment包含相應的設置列表。

3. 擴展PreferenceActivity類來托管您的設置。

4. 實現onBuildHeaders()回調用來指定頭文件。

一個很棒的好處是,PreferenceActivity使用這個設計自動給出了雙欄布局如圖5-4大屏幕上運行時。

即使你的應用程序支持Android 3.0以上的版本,你也可以使用PreferenceFragment來構建應用程序用於較新的設備

 

圖 5-4 使用headers的雙麵板布局

1. headers使用一個xml heanders文件定義

2.每一組設置通過PreferenceFragment來定義,並且在<header>節點中指定

 

圖 5-5 這是一個手機設備,當一個item選中時候,會調用PreferenceFragment

5.6.1創建headers文件

每一組設置你都可以在<preference-headers>跟節點中指定一個<header>節點,如代碼清單5-10所示:

複製代碼
<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="https://schemas.android.com/apk/res/android">
    <header 
        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentOne"
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one" />
    <header 
        android:fragment="com.example.prefs.SettingsActivity$SettingsFragmentTwo"
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" >
        <!—這個鍵值對可以當成一個fragment的參數-->
        <extra android:name="someKey" android:value="someHeaderValue" />
    </header>
</preference-headers>
複製代碼

 

代碼清單5-10

使用android:fragment屬性,每一個header聲明一個PreferenceFragment實例,當用戶選擇這個header時就會打開這個PreferenceFragment。<extras>節點允許你通過鍵值對的形式傳參,一般是使用Bundle。Fragment通過調用getArguments()來得到參數。關於參數的用途比較常見的就是為每一個組重用相同的PreferenceFragment子類並且使用參數還是製定你將要載入哪一個preferences XML文件。例如,下麵是一個fragment,它被多個設置組重用,下麵代碼清單5-11中在XML中使用了<extra>節點,key為“settings”:

複製代碼
public static class SettingsFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        String settings = getArguments().getString("settings");
        if ("notifications".equals(settings)) {
            addPreferencesFromResource(R.xml.settings_wifi);
        } else if ("sync".equals(settings)) {
            addPreferencesFromResource(R.xml.settings_sync);
        }
    }
}
複製代碼

 

代碼清單5-11

5.6.2顯示headers

為了顯示preference headers, 你必須實現onBuildHeaders()回調方法並且調用loadHeadersFromResource()如代碼清單5-12所示:

public class SettingsActivity extends PreferenceActivity {
    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.preference_headers, target);
    }
}

 

代碼清單5-12

當用戶從headers列表中選擇一個item時,係統會打開相關的PreferenceFragment。注意當使用preference headers時候,你的PreferenceActivity 子類不需要onCreate()方法,因為這個activity的任務僅僅是載入headers而已。

5.6.3老版本中支持Preference header

如果你的應用程序既支持3.0以下的版本,也支持3.0以上的版本,要麼3.0以上我們使用headers能提供雙麵板布局。低於3.0的版本我們就可以添加額外的preferences XML但裏麵不是使用<header>而是使用<Preference>節點了。但每一個<Preference>都發送一個intent到PreferenceActivity。例如讓我們先看下3.0或以上版本的代碼清單5-13中(res/xml/preference_headers.xml):

複製代碼
<preference-headers xmlns:android="https://schemas.android.com/apk/res/android">
    <header 
        android:fragment="com.example.prefs.SettingsFragmentOne"
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one" />
    <header 
        android:fragment="com.example.prefs.SettingsFragmentTwo"
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" />
</preference-headers>
複製代碼

 

代碼清單5-13

然後讓我們再看下3.0以下版本的代碼清單5-14中(res/xml/preference_headers_legacy.xml):

複製代碼
<PreferenceScreen xmlns:android="https://schemas.android.com/apk/res/android">
    <Preference 
        android:title="@string/prefs_category_one"
        android:summary="@string/prefs_summ_category_one"  >
        <intent 
            android:targetPackage="com.example.prefs"
            android:targetClass="com.example.prefs.SettingsActivity"
            android:action="com.example.prefs.PREFS_ONE" />
    </Preference>
    <Preference 
        android:title="@string/prefs_category_two"
        android:summary="@string/prefs_summ_category_two" >
        <intent 
            android:targetPackage="com.example.prefs"
            android:targetClass="com.example.prefs.SettingsActivity"
            android:action="com.example.prefs.PREFS_TWO" />
    </Preference>
</PreferenceScreen>
複製代碼

 

代碼清單5-14

因為android3.0中支持<preference-headers>,僅在android3.0或更高版本中係統會在PreferenceActivity中調用onBuildHeaders()方法。當然如果用戶的係統不是3.0(HONEYCOMB)的你就必須調用preference_headers_legacy.xml,然後調用addPreferencesFromResource()來載入xml文件。例如代碼清單5-15所示:

複製代碼
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
 
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        //低於3.0版本載入legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}
 
// 高於或等於3.0版本會調用此方法
@Override
public void onBuildHeaders(List<Header> target) {
   loadHeadersFromResource(R.xml.preference_headers, target);
}
複製代碼

 

代碼清單5-15

剩下的3.0以下版本就是通過處理Intent來識別哪個preference文件要被加載到Activity中來。所以需要檢索intent的action並與在preference XML<intent>下已知action字符串比較,如代碼清單5-16所示:

複製代碼
final static String ACTION_PREFS_ONE = "com.example.prefs.PREFS_ONE";
...
 
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    String action = getIntent().getAction();
    if (action != null && action.equals(ACTION_PREFS_ONE)) {
        addPreferencesFromResource(R.xml.preferences);
    }
    ...
 
    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // 載入3.0以下版本的legacy preferences文件
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}
複製代碼

 

代碼清單5-16

注意連續調用addPreferencesFromResource()會堆疊所有的preferences到一個單獨的列表中,所以確保他隻調用一次,把它寫到else-if的條件分支下。

5.7 讀取Preferences

默認的,所有你應用中的preferences會保存到一個文件中,你可以調用靜態方法PreferenceManager.getDefaultSharedPreferences()來獲得你保存的preferences。它將返回一個SharedPreferences對象包含所有你在PreferenceActivity中使用的Preference對象的鍵值對。例如,下麵代碼清單5-17教你如何讀取:

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

 

代碼清單5-17

5.7.1監聽Preference的改變

有些情況下你可能想隻要一個preferences改變你就想得到通知。當任意一個preferences發生改變時,為了取得一個回調,我們可以實現SharedPreference.OnSharedPreferenceChangeListener這個接口並通過SharedPreferences.registerOnSharedPreferenceChangeListener()來注冊監聽。這個接口隻有一個回調方法,就是onSharedPreferenceChanged()你很容易就找到。如代碼清單5-18所示:

複製代碼
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);
            // 為選中的值設置用戶描述摘要。
            connectionPref.setSummary(sharedPreferences.getString(key, ""));
        }
    }
}
複製代碼

 

代碼清單5-18

這個例子中,onSharedPreferenceChanged()方法會檢測改變的設置是否為一個已知的preference key。如果是就會調用findPreference()來獲得Preference對象,並且這是改變後的對象你可以做你想做的事情,這裏我們設置了一個摘要用於當用戶選中時給出提示信息。其實這是一個比較好的方法,特別是多個被選中時,你可以通過現有的API讓用戶知道他們做了些什麼並得到反饋。還有請注意記得在Activity聲明周期中的onPause()和 onResume()方法中注冊於注銷你的監聽,如代碼清單5-19所示:

複製代碼
@Override
protected void onResume() {
    super.onResume();
    getPreferenceScreen().getSharedPreferences()
            .registerOnSharedPreferenceChangeListener(this);
}
 
@Override
protected void onPause() {
    super.onPause();
    getPreferenceScreen().getSharedPreferences()
            .unregisterOnSharedPreferenceChangeListener(this);
}
複製代碼

 

代碼清單5-19

5.8 管理網絡的使用

從Android4.0開始,係統的設置應用程序允許用戶能看到他們的應用程序在前台和後台使用了多少網絡數據。用戶對於個別Apps能關閉使用後台數據。為了避免用戶關閉你的程序從後台訪問數據的功能,你應該使用數據連接有效並允許用戶通過你應用程序的設置來完善你應用程序的數據使用。例如你可能允許用戶控製你的APP多久同步一次數據,是否你的app僅在Wifi情況下才更新和下載,漫遊情況下如何處理等。這樣的好處是給用戶更精準的控製你的程序使用多少數據,有這樣的精準控製,用戶就不會在係統設置中直接把你的應用訪問數據的功能給關掉。一旦你在PreferenceActivity 中添加了必要的preferences來控製你App的數據,並養成了這種寫程序的習慣,那接下來我很樂意給你說明一下,你應該在manifest文件中添加一個intent filter名字為ACTION_MANAGE_NETWORK_USAGE,如代碼清單5-20所示:

<activity android:name="SettingsActivity" ... >
    <intent-filter>
       <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
       <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

 

代碼清單5-20

這個intent filter表明了這個activity告訴係統我能控製這個應用程序的數據使用。因此,當用戶在設置應用中檢查你app使用了多少數據時,一個App設置按鈕便可用了,你點擊它會啟動PreferenceActivity然後讓用戶在精確控製你的app數據使用情況。

5.9 構建自定義的Preference

Android框架包含各種各樣的Preference子類允許你構建自己的UI。然而你可能發現一個設置沒有好的內置方案,如一個number picker或date picker。在這種情況下你需要創建自定義的preference,你需要繼承Preference類。當然擴展Preference類後有一些重要的事情要做:

◆當用戶選擇設置的時候指定用戶接口

◆在適當的情況下保存setting的值

◆當進入我們的View時,使用當前值或默認值初始化Preference

◆當被係統請求時,提供默認值

◆如果Preference提供它自己的UI(如一個對話框),保存和恢複狀態並處理生命周期的改變 。

5.9.1指定用戶界麵

如果你直接擴展Preference類,當用戶選擇一個item時,你需要實現onClick()用來定義action。其實大部分情況下就是直接繼承的DialogPreference顯示對話框的形式,這樣簡化的程序。如果你繼承了DialogPreference,你必須在類的構造函數中調用setDialogLayoutResourcs()來指定布局。如代碼清單5-21所示:

複製代碼
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);
    }
    ...
}
複製代碼

 

代碼清單5-21

5.9.2保存設置的值

你可以在任意時刻調用Preference類的persist*()方法來保存一個值,如設置的值為int,那麼就使用persistInt()。這個方法用在對話框關閉的時候調用比較好,它會給用戶一個提示。當點擊positive按鈕時,你就可以保存新的值。如代碼清單5-22所示:

複製代碼
@Override
protected void onDialogClosed(boolean positiveResult) {
    // 當用戶選擇OK時,保存新的值
    if (positiveResult) {
        persistInt(mNewValue);
    }
}
複製代碼

 

代碼清單5-22

在上麵這個例子中,mNewValue是一個類成員變量,並且是int型的。

5.9.3初始化當前值

當係統添加你的Preference 到屏幕時,它會調用onSetInitialValue() 來通知你的值是否是已經存在的值。如果不存在,這個調用會提供一個默認值。onSetInitialValue()方法通過一個boolean值來表明一個值是否已經被存儲了。如果為true,你應該把存儲的值給取出來,你可以使用getPersistedInt()這樣類似的方法取值。如果restorePersistedValue這個參數的值為false,那麼你就可以使用第二個默認值參數了,如代碼清單5-23所示:

複製代碼
@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    if (restorePersistedValue) {
        // 恢複狀態
        mCurrentValue = this.getPersistedInt(DEFAULT_VALUE);
    } else {
        // 從xml屬性中設置默認狀態
        mCurrentValue = (Integer) defaultValue;
        persistInt(mCurrentValue);
    }
}
複製代碼

 

代碼清單5-23

請注意這裏當restorePersistedValue為true時不能使用參數自帶的defaultValue,因為它的值為null,隻有當restorePersistedValue為false時才能使用。

5.9.4提供一個默認值

如果Preference的實例指定一個默認值(使用android:defaultValue屬性),那麼當Preference實例化對象時為了取得默認值,係統會調用onGetDefaultValue()方法。你必須實現這個方法,這樣在係統保存默認值到SharedPreferences的時候才能正確處理。例如代碼清單5-24所示:

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

 

代碼清單5-24

方法參數提供你需要的一切:數組屬性和你需要檢索的android:defaultValue的索引位置,原因你必須實現這個方法來提取默認值的屬性,因為您必須指定一個本地屬性的默認值,以防值是未定義的。

5.9.5保存和恢複Preference的狀態

就像一個在布局中的View,你的Preference子類負責保存和恢複它的狀態,以防止activity和fragment被重新啟動。妥善保存和恢複你Preference類的狀態,你必須實現生命周期中的onSaveInstanceState()和onRestoreInstanceState()回調。Preference的狀態可以通過Parcelable接口實現。Android框架提供這樣一個對象,你可以作為入口點定義你對象的狀態,比如Preference.BaseSavedState類。定義你Preference保存狀態,你應該繼承Preference.BaseSavedState類。你需要重寫一些方法來定義CREATOR對象。對於大部分應用程序,你可以直接複製一下實現並做一些簡單的改變即可,如代碼清單5-25所示:

複製代碼
private static class SavedState extends BaseSavedState {
    int value;
 
    public SavedState(Parcelable superState) {
        super(superState);
    }
 
    public SavedState(Parcel source) {
        super(source);
        // 獲得當前preference的值
        value = source.readInt();  
    }
 
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        // 寫入preference的值
        dest.writeInt(value);  
    }
 
    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];
        }
    };
}
複製代碼

 

代碼清單5-25

下麵是具體運用在onSaveInstanceState()和andonRestoreInstanceState()中的過程,如代碼清單5-26所示:

複製代碼
@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    // 檢查Preference是否被保存過
    if (isPersistent()) {
        //不需要保存實例狀態,因為它是持久化的,使用父類狀態
        return superState;
    }
 
    // 創建自定義的BaseSavedState實例
    final SavedState myState = new SavedState(superState);
    // 使用類成員變量賦值
    myState.value = mNewValue;
    return myState;
}
 
@Override
protected void onRestoreInstanceState(Parcelable state) {
    //檢查我們在onSaveInstanceState中是否保存過狀態
    if (state == null || !state.getClass().equals(SavedState.class)) {
        // 沒有保存狀態,調用父類的方法
        super.onRestoreInstanceState(state);
        return;
    }
 
    // 強製轉換到自定義的BaseSavedState
    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    
    // 應用它的值到UI,以恢複UI狀態
    mNumberPicker.setValue(myState.value);
}
複製代碼

最後更新:2017-04-03 12:55:41

  上一篇:go Android的語言設置
  下一篇:go SQL Server DATEADD() 函數