Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android官方文檔之User Interface(Settings)

Android官方文檔之User Interface(Settings)

編輯:關於Android編程

設置頁面(Settings)用於修改、保存app的特性或行為 等。如某些新聞app可以在設置頁面設置其白天/夜間模式、清除緩存、是否允許推送通知等。如下圖所示。
如需為應用提供設置頁面,應使用Android SDK中的Preference這個API。本文將介紹如何使用這個API構建app的設置頁面,如需訪問官方原文,您可以點擊這個鏈接:《Settings》。


概覽(Overview)


從傳統上說,一般使用Layout布局資源為Activity、Fragment綁定可視化界面,而使用Preference及其子類也可以定義XML文件。Preference對象可以在一個設置頁面中作為一個模塊。每一個Preference都可以實現一個特殊的設置功能項。比如說,CheckBoxPreference類可以提供一個列表項,項中包含一個CheckBox控件。而ListPreference可創建一個包含列表項的對話框。


每一個Preference都存儲了一個鍵值對,該鍵值對默認使用SharedPreferences 存儲。當用戶修改了某些設置後,系統會更新SharedPreferences 的對應值。一般情況下,SharedPreferences 存儲的這些鍵值對無需您主動訪問,除非您需要通過某些鍵值對來修改app的某些行為。


SharedPreferences 可以存儲以下類型的值(由於SharedPreferences存儲的是XML格式的文件,所以只能存儲一些基礎類型的值、String及String數組):

Boolean

Float

Int

Long

String

String[] (String數組)


由於設置頁面使用的是Preferences布局,而並沒有使用傳統的Layout,所以相應的Activity或Fragment也應該是特殊的Activity或Fragment。

如果您的設備低於Android 3.0,那麼請使用PreferenceActivity類。

如果您的設備為Android 3.0及以上版本,那麼使用Activity 類就可以,而Fragment需使用PreferenceFragment


Preferences類(Preferences)


常見的Preferences類如下:

CheckBoxPreference:包含一個列表項,項中有一個CheckBox控件。

ListPreference:包含了若干個 radio buttons的對話框。

EditTextPreference:包含一個EditText 的對話框。


當然,內置的Preferences不能滿足所有需求,這時需要自定義Preferences類。


在XML中定義Preferences (Defining Preferences in XML)


所有Preferences 均有標簽與之對應如CheckBoxPreference對應標簽。


xml文件應存儲於res/xml/目錄下,通常命名為 preferences.xml,且只定義一個文件即可。
XML 文件的根節點必須是一個 標簽。每一個子標簽都表示一項:



    
    

標簽包含以下三個共同屬性:

android:key:大多情況下,該屬性不可缺省。它指定該Preference的鍵名,該鍵是一個String類型且必須唯一。當然,在、指定了標簽的Preference、以及指定了android:fragment屬性的Preference中可以不指定android:key屬性。

android:title:Preference標簽的標題。

android:defaultValue:缺省值。


當創建了超過10個Preference項時,您可能希望多創建一個Preference設置組( groups of settings )以便歸類顯示,並為該組起一個名字。如下圖所示:

這裡寫圖片描述


創建設置組(Creating setting groups)


您可以將任意個具有相似功能的Preference項設置為一個組。創建設置組有兩種方式:

使用標題(Using titles)

使用子屏幕(Using subscreens)


使用標題(Using titles)


如果您需要為Preferences項設置一個組同時為其設置一個標題,那麼您可以使用,該標簽表示一個Preferences組,為該標簽內設置Preferences子標簽即是組中的項。比如:


    
        
        
        
    
    ...

使用子屏幕(Using subscreens)


當點擊了某個Preference項後,可以跳轉到另一屏Preference組中(見下圖所示)——為實現這一功能,您需要在標簽中嵌套標簽,內標簽就表示另一屏Preference組。

這裡寫圖片描述

XML示例如下所示:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> ... ... ... Using intents


使用 intents(Using intents)


有些時候,當您點擊了某個Preference項時,啟動一個Activity而不是另一屏Preference組。為實現這個功能,您需要在Preference標簽中添加標簽以指定啟動Activity的action和 data等屬性:
如需啟動一個網頁,示例如下:


    

標簽同時支持顯式啟動和隱式啟動,其中用於隱式啟動的屬性為:

android:action:相當於代碼中的setAction();

android:data:相當於代碼中的setData();

android:mimeType:相當於代碼中的setType();

顯式啟動的屬性為:

android:targetClass:啟動目標組件類名的字節碼,相當於代碼中的setComponent();

android:targetPackage:啟動目標組件所在包的包名,相當於setComponent();


創建Preference Activity(Creating a Preference Activity)


為了兼容Android3.0之前的版本,您的Activity需要繼承PreferenceActivity(若只兼容Android 3.0及以上版本,只需繼承Activity即可)。


為了綁定創建好的Preferences的XML視圖文件,需要onCreate()方法中調用addPreferencesFromResource()方法並將xml文件傳入:

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

使用Preference Fragment(Using Preference Fragments)


如需將Preference綁定至Fragment,您的Fragment應繼承自PreferenceFragment (支持Android 3.0及以後版本),並且可以將這個Fragment應用於任何Activity上(不一定非得是PreferenceActivity)。


與Activity一樣,您只需在PreferenceFragment 的onCreate()方法中調用addPreferencesFromResource()方法即可:

public static class SettingsFragment extends PreferenceFragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

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

再將這個Fragment加入到Activity視圖中:

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }
}

!請注意:Fragment並不是一個Context,如需在Fragment中使用Context對象,應調用Fragment的getActivity()獲得其宿主Activity實例的引用,但只有在Fragment的onAttach()方法執行完畢以後,getActivity()才能獲取Activity實例的引用,在這之前或onDetach()回調後,getActivity()均返回null。(However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null)


設置缺省值(Setting Default Values)


某些Preferences項非常重要,這需要您為它設置缺省值,以便在第一次打開app時使用這個缺省值。


配置缺省值的方式是在XML文件中設置android:defaultValue屬性。屬性值的類型對應Preference的類型,如下所示:






接著在onCreate()中、或其他應用的任何Activity可能啟動本app的第一時間,調用方法:

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

在onCreate()方法中調用該方法可以確保您的應用在啟動時正確設置缺省值。而程序可能需要這些缺省值來確定某些行為。(如是否在蜂窩網絡中下載數據)


方法有三個參數:

Context;

preference的XML ID;

若設為false,表示僅在應用第一次調用該方法時,系統才會指定默認值,若設為true,表示每次啟動activity時,默認值都會覆蓋掉原來設置的值。


使用Preference標頭(Using Preference Headers)


在少數情況下,您需要為自己的app設置子Preference,就像下面兩圖展示的一樣。而在Android 3.0版本及以後,Android推出了“headers”的概念,這使得您不用為了分屏顯示設置項而嵌套Preference。

這裡寫圖片描述

這裡寫圖片描述


為了為設置創建標頭(headers),您應當:

將每一屏的設置布局分別綁定至一個PreferenceFragment上,也就是說,每一屏Preference設置頁均有各自的XML布局文件。

創建一個XML標頭文件,用於羅列出各頁面的設置組,同時聲明各自設置頁面是由哪個fragment包含的。

繼承PreferenceActivity 類,為其布局添加放置Fragment的位置。

4.實現onBuildHeaders() 回調方法,來制定標頭文件。


創建標頭文件的示例(Creating the headers file)


標頭XML文件的根標簽是 ,每一個子標簽

 
包含了一個fragment:


其中有一個標簽,該標簽有一個鍵屬性(android:name)和一個值(android:value)屬性,鍵值對用於向fragment傳遞一個bundle參數,可以通過getArguments()方法獲得。下面的例子便是通過“settings”鍵獲得了對應的屬性值,並通過該屬性值加載不同的fragment:

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);
        }
    }
}

顯示標頭(Displaying the headers)


為了顯示標頭,需要在回調Activity的onBuildHeaders()方法,並調用loadHeadersFromResource()方法,比如:

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

每當用戶點擊某個設置項時,系統會打開相應的fragment。

若使用了標頭,則無需再PreferenceActivity 中回調onCreate()方法。


讀取Preferences(Reading Preferences)


在默認情況下,您的app中所有的Preference設置均保存在一個文件中,該文件可以在程序的任何位置調用PreferenceManager.getDefaultSharedPreferences()方法獲得這個文件,該方法返回一個 SharedPreferences 對象的引用,該對象包含了所有在PreferenceActivity中設置過的鍵值對:

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

該示例演示了在同一應用程序的其他Activity中獲得PreferenceActivity中的設置值。


監聽preference 設置項的變化(Listening for preference changes)


為了監聽preference 設置項的變化,需要實現SharedPreference.OnSharedPreferenceChangeListener接口,並調用 registerOnSharedPreferenceChangeListener()方法為SharedPreferences 注冊監聽器。

在接口SharedPreference.OnSharedPreferenceChangeListener中只包含一個onSharedPreferenceChanged()方法:

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, ""));
        }
    }
}

調用findPreference()方法傳入一個key,系統可以返回包含了該key所對應的Preference實例,該實例的選項值是修改了設置以後的值,這樣就可以用來做相應的修改操作。

建議您在 onResume() 和 onPause() 回調期間分別注冊和注銷 SharedPreferences.OnSharedPreferenceChangeListener:


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

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

首選項管理器不會在您調用 registerOnSharedPreferenceChangeListener() 時存儲對偵聽器的強引用。但是,您必須存儲對偵聽器的強引用,否則它將很容易被當作垃圾回收。 所以,建議您將偵聽器的引用保存在只要您需要偵聽器就會存在的對象的實例數據中。

例如,在以下代碼中,調用方未保留對偵聽器的引用。 因此,偵聽器將容易被當作垃圾回收,並在將來某個不確定的時間失敗:

prefs.registerOnSharedPreferenceChangeListener(
  // Bad! The listener is subject to garbage collection!
  new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // listener implementation
  }
});

所以,請將對偵聽器的引用存儲在只要需要偵聽器就會存在的對象的實例數據字段中:

SharedPreferences.OnSharedPreferenceChangeListener listener =
    new SharedPreferences.OnSharedPreferenceChangeListener() {
  public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    // listener implementation
  }
};
prefs.registerOnSharedPreferenceChangeListener(listener);

管理網絡使用情況(Managing Network Usage)


從Android 4.0開始,用戶可以通過設置頁面了解自己的應用在前台和後台使用網絡的情況。用戶可以根據這些信息禁用掉app在後台使用網絡。為了避免用戶禁止您的應用從後台訪問數據,應該有效地使用數據連接,並允許用戶通過應用設置優化應用的數據使用。

比如您可以允許用戶控制應用同步數據的頻率、控制應用是否僅在有 Wi-Fi 時才執行上傳/下載操作、以及以及控制應用能否在漫游時使用數據 等(how often your app syncs data, whether your app performs uploads/downloads only when on Wi-Fi,whether your app uses data while roaming, etc)。

在PreferenceActivity 中添加必要的首選項來控制應用的數據使用習慣後,應在清單文件中為 ACTION_MANAGE_NETWORK_USAGE 添加 Intent 過濾器。例如:


    
       
       
    

當用戶從系統的“設置”應用檢查應用所使用的數據量時,可以使用“查看應用設置”按鈕啟動 PreferenceActivity,這樣,用戶就能夠優化應用使用的數據量。


創建定制的Preference選項(Building a Custom Preference)


可以通過擴展 Preference 類或其他子類之一來創建自定義首選項。

擴展 Preference 類時,您需要執行以下幾項重要操作:

指定在用戶選擇設置時顯示的用戶界面(Specify the user interface that appears when the user selects the settings);

適時保存設置的值(Save the setting’s value when appropriate);

使用顯示的當前(默認)值初始化 Preference(Initialize the Preference with the current (or default) value when it comes into view);

在系統請求時提供默認值(在系統請求時提供默認值);

如果 Preference 提供自己的 UI(例如對話框),請保存並恢復狀態以處理生命周期變更(例如,用戶旋轉屏幕)。(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))


這面將一一展示每一步驟:

指定用戶界面(Specifying the user interface)


如果您要直接擴展 Preference 類,則需要實現 onClick() 來定義在用戶選擇該項時發生的操作,不過,大多數自定義設置都會擴展 DialogPreference 以顯示對話框,從而簡化這一過程。擴展 DialogPreference 時,必須在類構造函數中調用 setDialogLayoutResourcs() 來指定對話框的布局(you must call setDialogLayoutResourcs() during in the class constructor to specify the layout for the dialog)。

自定義 DialogPreference 可以使用下面的構造函數來聲明布局並為默認的肯定和否定對話框按鈕指定文本:

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)


如果設置的值為整型數或是用於保存布爾值的 persistBoolean(),則可通過調用 Preference 類的一個 persist*() 方法(如 persistInt())隨時保存該值。

每個 Preference 均只能保存一種數據類型,因此您必須使用適合自定義 Preference 所用數據類型的 persist*() 方法。

至於何時選擇保留設置,則可能取決於要擴展的 Preference 類。如果擴展 DialogPreference,則只能在用戶選擇“確定”按鈕時保留該值。

當 DialogPreference 關閉時,系統會調用 onDialogClosed() 方法。該方法包括一個布爾參數,用於指定用戶結果是否為“肯定”;如果值為 true,則表示用戶選擇的是肯定按鈕且您應該保存新值。 例如:

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

mNewValue 是一個類成員,可存放設置的當前值。調用 persistInt() 會將該值保存到 SharedPreferences 文件。

初始化當前值(Initializing the current value)


系統將 Preference 添加到屏幕時,會調用 onSetInitialValue() 來通知您設置是否具有保留值。如果沒有保留值,則此調用將為您提供默認值。

onSetInitialValue() 方法傳遞一個布爾值 (restorePersistedValue),以指示是否已為該設置保留值。 如果值為 true,則應通過調用 Preference 類的一個 getPersisted*() 方法(如整型值對應的 getPersistedInt())來檢索保留值。通常,您會需要檢索保留值,以便能夠正確更新 UI 來反映之前保存的值。

如果 restorePersistedValue 為 false,則應使用在第二個參數中傳遞的默認值。

@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);
    }
}

每種 getPersisted*() 方法均采用一個參數,用於指定在實際上沒有保留值或該鍵不存在時所要使用的默認值。在上述示例中,當 getPersistedInt() 不能返回保留值時,局部常量用於指定默認值。

您不能使用 defaultValue 作為 getPersisted*() 方法中的默認值,因為當 restorePersistedValue 為 true 時,其值始終為 null。

提供默認值(Providing a default value)


如果 Preference 類的實例指定一個默認值(使用 android:defaultValue 屬性),則在實例化對象以檢索該值時,系統會調用 onGetDefaultValue()。您必須實現此方法,系統才能將默認值保存在 SharedPreferences 中。 例如:

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

方法參數可提供您所需的一切:屬性的數組和 android:defaultValue(必須檢索的值)的索引位置。之所以必須實現此方法以從該屬性中提取默認值,是因為您必須為此屬性指定在未定義屬性值時所要使用的局部默認值。

保存和恢復首選項的狀態(Saving and restoring the Preference’s state)


正如布局中的 View 一樣,在重啟 Activity 或片段時(例如,用戶旋轉屏幕),Preference 子類也負責保存並恢復其狀態。要正確保存並恢復 Preference 類的狀態,您必須實現生命周期回調方法 onSaveInstanceState() 和 onRestoreInstanceState()。

Preference 的狀態由實現 Parcelable 接口的對象定義。Android 框架為您提供此類對象,作為定義狀態對象(Preference.BaseSavedState 類)的起點。

要定義 Preference 類保存其狀態的方式,您應該擴展 Preference.BaseSavedState 類。您只需重寫幾種方法並定義 CREATOR 對象。

對於大多數應用,如果 Preference 子類保存除整型數以外的其他數據類型,則可復制下列實現並直接更改處理 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 CREATOR =
            new Parcelable.Creator() {

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

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

如果將上述 Preference.BaseSavedState 實現添加到您的應用(通常,作為 Preference 子類的子類),則需要為 Preference 子類實現 onSaveInstanceState() 和 onRestoreInstanceState() 方法。

例如:

@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);
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved