lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Thiết đặt

Ứng dụng thường bao gồm những thiết đặt cho phép người dùng sửa đổi các tính năng và hành vi của ứng dụng. Ví dụ, một số ứng dụng cho phép người dùng quy định xem thông báo có được kích hoạt hay không hoặc quy định tần suất ứng dụng sẽ đồng bộ dữ liệu với đám mây.

Nếu muốn cung cấp thiết đặt cho ứng dụng của mình, bạn nên sử dụng các API Preference của Android để xây dựng một giao diện phù hợp với trải nghiệm người dùng trong các ứng dụng Android khác (bao gồm thiết đặt hệ thống). Tài liệu này mô tả cách xây dựng thiết đặt ứng dụng của bạn bằng cách sử dụng các API Preference.

Thiết kế Thiết đặt

Để biết thông tin về cách thiết kế thiết đặt của bạn, hãy đọc hướng dẫn thiết kế Thiết đặt.

Hình 1. Ảnh chụp màn hình từ thiết đặt của ứng dụng Messaging trên Android. Chọn một mục được định nghĩa bởi một Preference sẽ mở ra một giao diện để thay đổi thiết đặt.

Tổng quan

Thay vì sử dụng các đối tượng View để xây dựng giao diện người dùng, thiết đặt được xây dựng bằng cách sử dụng các lớp con khác nhau của lớp Preference mà bạn khai báo trong một tệp XML.

Đối tượng Preference là một khối dựng cho một thiết đặt đơn lẻ. Mỗi Preference xuất hiện như một mục trong một danh sách và cung cấp UI phù hợp để người dùng sửa đổi thiết đặt. Ví dụ, một CheckBoxPreference tạo một mục danh sách hiển thị một hộp kiểm, và một ListPreference tạo một mục mở ra một hộp thoại với danh sách lựa chọn.

Mỗi Preference mà bạn thêm có một cặp khóa-giá trị tương ứng mà hệ thống sử dụng để lưu thiết đặt trong một tệp SharedPreferences mặc định cho thiết đặt của ứng dụng của bạn. Khi người dùng thay đổi một thiết đặt, hệ thống sẽ cập nhật giá trị tương ứng trong tệp SharedPreferences cho bạn. Lần duy nhất mà bạn nên trực tiếp tương tác với tệp SharedPreferences được liên kết đó là khi bạn cần đọc giá trị để xác định xem hành vi ứng dụng của mình có được dựa trên thiết đặt của người dùng không.

Giá trị được lưu trong SharedPreferences cho từng thiết đặt có thể là một trong các kiểu dữ liệu sau:

  • Boolean
  • Float
  • Int
  • Long
  • String
  • String Set

Vì thiết đặt của ứng dụng của bạn được xây dựng bằng cách sử dụng các đối tượng Preference thay vì đối tượng View, bạn nên sử dụng một lớp con Activity hoặc Fragment chuyên dụng để hiển thị thiết đặt danh sách:

  • Nếu ứng dụng của bạn hỗ trợ các phiên bản Android cũ hơn 3.0 (API mức 10 và thấp hơn), bạn phải xây dựng hoạt động như một phần mở rộng của lớp PreferenceActivity.
  • Trên phiên bản Android 3.0 trở lên, thay vào đó, bạn nên sử dụng một Activity truyền thống nơi lưu giữ PreferenceFragment để hiển thị thiết đặt ứng dụng của bạn. Tuy nhiên, bạn cũng có thể sử dụng PreferenceActivity để tạo một bố trí hai bảng cho màn hình lớn khi bạn có nhiều nhóm thiết đặt.

Cách thiết đặt PreferenceActivity của bạn và các thực thể của PreferenceFragment được trình bày trong các phần về Tạo một Hoạt động Tùy chọnSử dụng Phân đoạn Tùy chọn.

Tùy chọn

Mọi thiết đặt cho ứng dụng của bạn đều được biểu diễn bởi một lớp con cụ thể của lớp Preference. Mỗi lớp con lại bao gồm một tập hợp các tính chất cốt lõi cho phép bạn quy định những thứ như tiêu đề cho thiết đặt và giá trị mặc định. Mỗi lớp con cũng cung cấp các tính chất và giao diện người dùng chuyên dụng của chính nó. Ví dụ, hình 1 mình họa một ảnh chụp màn hình từ thiết đặt của ứng dụng Messaging. Mỗi mục danh sách trong màn hình thiết đặt được hỗ trợ bởi một đối tượng Preference khác nhau.

Sau đây là một số tùy chọn phổ biến nhất:

CheckBoxPreference
Hiển thị một mục kèm một hộp kiểm cho thiết đặt hoặc được kích hoạt hoặc bị vô hiệu hóa. Giá trị được lưu là một boolean (true nếu nó được chọn).
ListPreference
Mở một hộp thoại kèm danh sách nút chọn một. Giá trị được lưu có thể là bất kỳ loại giá trị được hỗ trợ nào (liệt kê bên trên).
EditTextPreference
Mở một hộp thoại kèm một widget EditText. Giá trị được lưu là một String.

Xem lớp Preference để biết danh sách tất cả các lớp con khác và tính chất tương ứng của chúng.

Dĩ nhiên, các lớp tích hợp không đáp ứng mọi nhu cầu và ứng dụng của bạn có thể yêu cầu lớp con chuyên dụng hơn. Ví dụ, nền tảng này hiện chưa cung cấp một lớp Preference cho việc chọn một số hay ngày. Vì thế, bạn có thể cần phải định nghĩa lớp con Preference của chính mình. Để được trợ giúp khi làm vậy, hãy xem phần về Xây dựng Thiết đặt Tùy chỉnh.

Định nghĩa Tùy chọn trong XML

Mặc dù bạn có thể khởi tạo các đối tượng Preference mới vào thời gian chạy, bạn nên định nghĩa danh sách các thiết đặt của mình trong XML kèm một phân cấp của các đối tượng Preference . Việc sử dụng một tệp XML để định nghĩa bộ sưu tập thiết đặt của bạn sẽ được ưu tiên vì tệp cung cấp một cấu trúc dễ đọc, cập nhật đơn giản. Bên cạnh đó, các thiết đặt ứng dụng của bạn thường được xác định trước, mặc dù bạn vẫn có thể sửa đổi bộ sưu tập vào thời gian chạy.

Mỗi lớp con Preference có thể được khai báo bằng một phần tử XML mà khớp với tên lớp đó, chẳng hạn như <CheckBoxPreference>.

Bạn phải lưu tệp XML trong thư mục res/xml/. Mặc dù bạn có thể đặt tên tệp là bất cứ thứ gì mình muốn, nó thường được đặt tên làpreferences.xml. Bạn thường chỉ cần một tệp, bởi các nhánh trong phân cấp (mà mở danh sách thiết đặt của riêng chúng) sẽ được khai báo bằng cách sử dụng các thực thể lồng nhau của PreferenceScreen.

Lưu ý: Nếu bạn muốn tạo một bố trí đa bảng cho thiết đặt của mình, vậy bạn nên tách riêng các tệp XML cho từng phân đoạn.

Node gốc cho tệp XML phải là một phần tử <PreferenceScreen>. Trong phần tử này là nơi bạn thêm từng Preference. Từng phần tử con mà bạn thêm vào trong phần tử <PreferenceScreen> sẽ xuất hiện như một mục đơn lẻ trong danh sách thiết đặt.

Ví dụ:

<?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>

Trong ví dụ này, có một CheckBoxPreference và một ListPreference. Cả hai mục đều bao gồm ba thuộc tính sau:

android:key
Thuộc tính này được yêu cầu cho các tùy chọn duy trì một giá trị dữ liệu. Nó quy định khóa (xâu) duy nhất mà hệ thống sử dụng khi lưu giá trị của thiết đặt này trong SharedPreferences.

Các thực thể duy nhất mà thuộc tính này không được yêu cầu là khi tùy chọn là một PreferenceCategory hoặc PreferenceScreen, hoặc tùy chọn quy định một Intent để gọi ra (bằng phần tử &lt;intent&gt;) hoặc Fragment để hiển thị (bằng thuộc tính android:fragment).

android:title
Thuộc tính này cung cấp một tên hiển thị với người dùng cho thiết đặt.
android:defaultValue
Nó quy định giá trị ban đầu mà hệ thống nên đặt trong tệp SharedPreferences. Bạn nên cung cấp một giá trị mặc định cho tất cả thiết đặt.

Để biết thông tin về tất cả thuộc tính được hỗ trợ khác, hãy xem tài liệu Preference (và lớp con tương ứng).

Hình 2. Thiết đặt thể loại có tiêu đề.
1. Thể loại được quy định bởi phần tử <PreferenceCategory>.
2. Tiêu đề được quy định bằng thuộc tính android:title.

Khi danh sách thiết đặt của bạn vượt quá khoảng 10 mục, bạn có thể muốn thêm tiêu đề để định nghĩa các nhóm thiết đặt hoặc hiển thị các nhóm đó trong một màn hình riêng. Những tùy chọn này được mô tả trong các phần sau.

Tạo nhóm thiết đặt

Nếu bạn trình bày một danh sách từ 10 thiết đặt trở lên, người dùng có thể gặp khó khăn trong việc dò tìm, hiểu và xử lý chúng. Bạn có thể khắc phục điều này bằng cách chia một số hoặc tất cả thiết đặt thành các nhóm, qua đó biến một danh sách dài thành nhiều danh sách ngắn hơn. Một nhóm các thiết đặt có liên quan có thể được trình bày bằng một trong hai cách:

Bạn có thể sử dụng một hoặc cả hai kỹ thuật tạo nhóm này để sắp xếp các thiết đặt cho ứng dụng của mình. Khi quyết định sử dụng cái nào và làm thế nào để chia các thiết đặt của mình, bạn nên tuân theo các hướng dẫn trong tài liệu hướng dẫn Thiết đặt của Thiết kế Android.

Sử dụng tiêu đề

Nếu bạn muốn cung cấp các thanh chia có tiêu đề giữa các nhóm thiết đặt (như minh họa trong hình 2), hãy đặt từng nhóm đối tượng Preference vào bên trong một PreferenceCategory.

Ví dụ:

<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>

Sử dụng màn hình con

Nếu bạn muốn đặt các nhóm thiết đặt vào một màn hình con (như minh họa trong hình 3), hãy đặt nhóm các đối tượng Preference vào bên trong một PreferenceScreen.

Hình 3. Màn hình con thiết đặt. Phần tử &lt;PreferenceScreen&gt; sẽ tạo một mục mà, khi được chọn, nó sẽ mở ra một danh sách riêng để hiển thị các thiết đặt lồng nhau.

Ví dụ:

<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- opens a subscreen of settings -->
    <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" ... />
        <!-- opens another nested 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>

Sử dụng ý định

Trong một số trường hợp, bạn có thể muốn một mục tùy chọn mở một hoạt động khác thay vì một màn hình thiết đặt, chẳng hạn như một trình duyệt web để xem một trang web. Để gọi ra một Intent khi người dùng chọn một mục tùy chọn, hãy thêm một phần tử &lt;intent&gt; làm con của phần tử &lt;Preference&gt; tương ứng.

Ví dụ, sau đây là cách bạn có thể sử dụng một mục tùy chọn để mở một trang web:

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

Bạn có thể tạo cả ý định biểu thị và không biểu thị bằng cách sử dụng các thuộc tính sau:

android:action
Hành động cần gán, theo mỗi phương pháp setAction() .
android:data
Dữ liệu cần gán, theo mỗi phương pháp setData().
android:mimeType
Kiểu MIME cần gán, theo mỗi phương pháp setType() .
android:targetClass
Phần lớp của tên thành phần, theo mỗi phương pháp setComponent().
android:targetPackage
Phần gói của tên thành phần, theo mỗi phương pháp setComponent().

Tạo một Hoạt động Tùy chọn

Để hiển thị thiết đặt của bạn trong một hoạt động, hãy mở rộng lớp PreferenceActivity. Đây là phần mở rộng của lớp Activity truyền thống mà hiển thị một danh sách các thiết đặt dựa trên một phân cấp của các đối tượng Preference. PreferenceActivity sẽ tự động duy trì các thiết đặt liên kết với từng Preference khi người dùng thực hiện một thay đổi.

Lưu ý: Nếu bạn đang phát triển ứng dụng của mình cho phiên bản Android 3.0 và cao hơn, thay vào đó bạn nên sử dụng PreferenceFragment. Đi đến phần tiếp theo về Sử dụng Phân đoạn Tùy chọn.

Điều quan trọng nhất cần nhớ đó là bạn không được tải một bố trí dạng xem trong khi gọi lại onCreate(). Thay vào đó, bạn hãy gọi addPreferencesFromResource() để thêm tùy chọn mà bạn đã khai báo trong một tệp XML vào hoạt động. Ví dụ, sau đây là đoạn mã tối thiểu cần thiết cho một PreferenceActivity chức năng:

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

Đây là đoạn mã vừa đủ cho một số ứng dụng bởi ngay khi người dùng sửa đổi một tùy chọn, hệ thống sẽ lưu thay đổi đối với tệp SharedPreferences mặc định mà các thành phần ứng dụng khác của bạn có thể đọc khi bạn cần kiểm tra thiết đặt của người dùng. Tuy nhiên, nhiều ứng dụng lại yêu cầu thêm mã để theo dõi những thay đổi xảy ra với các tùy chọn đó. Để biết thông tin về việc theo dõi thay đổi trong tệp SharedPreferences, hãy xem phần về Đọc Tùy chọn.

Sử dụng Phân đoạn Tùy chọn

Nếu bạn đang phát triển cho phiên bản Android 3.0 (API mức 11) trở lên, bạn nên sử dụng một PreferenceFragment để hiển thị danh sách các đối tượng Preference của bạn. Bạn có thể thêm một PreferenceFragment vào bất kỳ hoạt động nào—bạn không cần sử dụng PreferenceActivity.

Phân đoạn cung cấp một kiến trúc linh hoạt hơn cho ứng dụng của bạn, so với việc sử dụng chỉ các hoạt động, dù loại hoạt động mà bạn đang xây dựng là gì. Như vậy, chúng tôi gợi ý bạn sử dụng PreferenceFragment để kiểm soát hiển thị các thiết đặt của mình thay cho PreferenceActivity khi có thể.

Việc triển khai PreferenceFragment có thể chỉ đơn giản như định nghĩa phương pháp onCreate() để tải một tệp tùy chọn bằng addPreferencesFromResource(). Ví dụ:

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

Khi đó, bạn có thể thêm phân đoạn này vào một Activity giống như cách mà bạn sẽ làm với bất kỳ Fragment nào khác. Ví dụ:

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

Lưu ý: PreferenceFragment không có một đối tượng Context của chính nó. Nếu bạn cần một đối tượng Context , bạn có thể gọi getActivity(). Tuy nhiên, hãy chắc chắn là chỉ gọi getActivity() khi phân đoạn đó được gắn kèm với một hoạt động. Khi phân đoạn chưa được gắn kèm, hoặc bị bỏ gắn kèm trong khi kết thúc vòng đời của nó, getActivity() sẽ trả về rỗng.

Thiết đặt Giá trị Mặc định

Tùy chọn mà bạn tạo có thể định nghĩa một số hành vi quan trọng cho ứng dụng của bạn, vì thế bạn cần phải khởi tạo tệp SharedPreferences kèm theo với các giá trị mặc định cho từng Preference khi người dùng lần đầu mở ứng dụng của bạn.

Điều đầu tiên bạn phải làm đó là quy định một giá trị mặc định cho từng đối tượng Preference trong tệp XML của bạn bằng cách sử dụng thuộc tính android:defaultValue. Giá trị đó có thể là bất kỳ kiểu dữ liệu nào mà phù hợp với đối tượng Preference tương ứng. Ví dụ:

<!-- default value is a boolean -->
<CheckBoxPreference
    android:defaultValue="true"
    ... />

<!-- default value is a string -->
<ListPreference
    android:defaultValue="@string/pref_syncConnectionTypes_default"
    ... />

Khi đó, từ phương pháp onCreate() trong hoạt động chính —của ứng dụng của bạn và trong bất kỳ hoạt động nào khác mà thông qua đó người dùng có thể vào ứng dụng của bạn lần đầu tiên—hãy gọi setDefaultValues():

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

Việc gọi này trong khi onCreate() sẽ đảm bảo rằng ứng dụng của bạn được khởi tạo phù hợp với các thiết đặt mặc định mà ứng dụng của bạn có thể cần đọc để xác định một số hành vi (chẳng hạn như có tải xuống dữ liệu trong khi đang trên mạng di động hay không).

Phương pháp này dùng ba tham đối:

  • Context ứng dụng của bạn.
  • ID tài nguyên cho tệp XML tùy chọn mà bạn muốn đặt các giá trị mặc định cho.
  • Một boolean cho biết các giá trị mặc định có nên được đặt nhiều hơn một lần hay không.

    Khi tham đối này là false, hệ thống sẽ đặt các giá trị mặc định chỉ khi phương pháp này chưa từng được gọi trước đây (hoặc KEY_HAS_SET_DEFAULT_VALUES trong tệp tùy chọn được chia sẻ giá trị mặc định là sai).

Miễn là bạn đặt tham đối thứ ba này thành false, bạn có thể gọi phương pháp này một cách an toàn mỗi khi hoạt động của bạn bắt đầu mà không khống chế các tùy chọn đã lưu của người dùng bằng cách đặt lại chúng thành mặc định. Tuy nhiên, nếu bạn đặt nó thành true, bạn sẽ khống chế mọi giá trị trước đó bằng các giá trị mặc định.

Sử dụng Tiêu đề Tùy chọn

Trong vài trường hợp hiếm gặp, bạn có thể muốn thiết kế các thiết đặt của mình sao cho màn hình thứ nhất chỉ hiển thị một danh sách các màn hình con (chẳng hạn như trong ứng dụng Thiết đặt của hệ thống, như minh họa trong các hình 4 và 5). Khi phát triển thiết kế như vậy cho phiên bản Android 3.0 trở lên, bạn nên sử dụng tính năng "tiêu đề" mới trong Android 3.0, thay vì xây dựng màn hình con với các phần tử PreferenceScreen lồng nhau.

Để xây dựng thiết đặt có tiêu đề của mình, bạn cần:

  1. Tách riêng từng nhóm thiết đặt thành các thực thể riêng của PreferenceFragment. Cụ thể, mỗi nhóm thiết đặt cần một tệp XML riêng.
  2. Tạo một tệp tiêu đề XML liệt kê từng nhóm thiết đặt và khai báo phân đoạn nào chứa danh sách thiết đặt tương ứng.
  3. Mở rộng lớp PreferenceActivity để lưu trữ các thiết đặt của bạn.
  4. Triển khai lệnh gọi lại onBuildHeaders() để quy định tệp tiêu đề.

Một lợi ích tuyệt vời đối với việc sử dụng thiết kế này đó là PreferenceActivity tự động trình bày bố trí hai bảng như minh họa trong hình 4 khi chạy trên màn hình lớn.

Ngay cả khi ứng dụng của bạn hỗ trợ các phiên bản Android cũ hơn 3.0, bạn có thể xây dựng ứng dụng của mình để sử dụng PreferenceFragment cho một trình chiếu hai bảng trên các thiết bị mới hơn, trong khi vẫn hỗ trợ phân cấp đa màn hình truyền thống trên các thiết bị cũ hơn (xem phần nói về Hỗ trợ các phiên bản cũ hơn với tiêu đề tùy chọn).

Hình 4. Bố trí có hai bảng với tiêu đề.
1. Tiêu đề được định nghĩa trong một tệp tiêu đề XML.
2. Mỗi nhóm thiết đặt được định nghĩa bởi một PreferenceFragment, được quy định bởi một phần tử &lt;header&gt; trong tệp tiêu đề .

Hình 5. Thiết bị cầm tay với các tiêu đề thiết đặt. Khi một mục được chọn, PreferenceFragment được liên kết sẽ thay thế tiêu đề.

Tạo tệp tiêu đề

Mỗi nhóm thiết đặt trong danh sách tiêu đề của bạn được quy định bởi một phần tử &lt;header&gt; đơn lẻ bên trong một phần tử &lt;preference-headers&gt; gốc. Ví dụ:

<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://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" >
        <!-- key/value pairs can be included as arguments for the fragment. -->
        <extra android:name="someKey" android:value="someHeaderValue" />
    </header>
</preference-headers>

Với thuộc tính android:fragment, mỗi tiêu đề sẽ khai báo một thực thể của PreferenceFragment mà sẽ mở khi người dùng chọn tiêu đề đó.

Phần tử &lt;extras&gt; cho phép bạn chuyển các cặp khóa-giá trị sang phân đoạn trong một Bundle. Phân đoạn có thể truy xuất các tham đối bằng cách gọi getArguments(). Bạn có thể chuyển các tham đối tới phân đoạn vì nhiều lý do khác nhau, nhưng một lý do chính đáng đó là để sử dụng lại cùng lớp con của PreferenceFragment cho mỗi nhóm và sử dụng tham đối để quy định tệp XML tùy chọn nào mà phân đoạn cần tải.

Ví dụ, sau đây là một phân đoạn mà có thể được tái sử dụng cho nhiều nhóm thiết đặt, khi từng tiêu đề định nghĩa một tham đối &lt;extra&gt; với khóa "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);
        }
    }
}

Hiển thị tiêu đề

Để hiển thị tiêu đề tùy chọn, bạn phải triển khai phương pháp gọi lại onBuildHeaders() và gọi loadHeadersFromResource(). Ví dụ:

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

Khi người dùng chọn một mục từ danh sách tiêu đề, hệ thống sẽ mở PreferenceFragment kèm theo.

Lưu ý: Khi sử dụng tiêu đề tùy chọn, lớp con PreferenceActivity của bạn không cần triển khai phương pháp onCreate(), vì tác vụ cần thiết duy nhất cho hoạt động đó là tải tiêu đề.

Hỗ trợ các phiên bản cũ hơn với tiêu đề tùy chọn

Nếu ứng dụng của bạn hỗ trợ các phiên bản Android cũ hơn 3.0, bạn vẫn có thể sử dụng tiêu đề để cung cấp một bố trí hai bảng khi chạy trên Android 3.0 trở lên. Tất cả những việc bạn cần làm đó là tạo một tệp XML tùy chọn bổ sung có sử dụng phần tử cơ bản <Preference> đóng vai trò như mục tiêu đề (để dùng cho các phiên bản Android cũ hơn).

Tuy nhiên, thay vì mở một PreferenceScreen mới, từng phần tử <Preference> sẽ gửi một Intent tới PreferenceActivity mà quy định tệp XML tùy chọn cần tải.

Ví dụ, sau đây là một tệp XML cho các tiêu đề tùy chọn được sử dụng trên Android 3.0 trở lên (res/xml/preference_headers.xml):

<preference-headers xmlns:android="http://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>

Và sau đây là một tệp tùy chọn cung cấp cùng các tiêu đề cho các phiên bản cũ hơn Android 3.0 (res/xml/preference_headers_legacy.xml):

<PreferenceScreen xmlns:android="http://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>

Vì hỗ trợ dành cho &lt;preference-headers&gt; đã được thêm trong Android 3.0, hệ thống sẽ gọi onBuildHeaders() trong PreferenceActivity của bạn chỉ khi đang chạy trên phiên bản Androd 3.0 hoặc cao hơn. Để tải tệp tiêu đề "kế thừa" (preference_headers_legacy.xml), bạn phải kiểm tra phiên bản Android và, nếu phiên bản cũ hơn Android 3.0 (HONEYCOMB), hãy gọi addPreferencesFromResource() để tải tệp tiêu đề kế thừa. Ví dụ:

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

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}

// Called only on Honeycomb and later
@Override
public void onBuildHeaders(List<Header> target) {
   loadHeadersFromResource(R.xml.preference_headers, target);
}

Việc duy nhất còn lại cần làm đó là xử lý Intent mà được chuyển vào hoạt động để nhận biết tệp tùy chọn nào cần tải. Vì vậy, hãy truy xuất hành động của ý định và so sánh nó với các xâu hành động đã biết mà bạn đã sử dụng trong tag &lt;intent&gt; của XML tùy chọn:

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) {
        // Load the legacy preferences headers
        addPreferencesFromResource(R.xml.preference_headers_legacy);
    }
}

Lưu ý rằng các lệnh gọi liên tiếp đến addPreferencesFromResource() sẽ xếp chồng tất cả tùy chọn trong một danh sách duy nhất, vì thế hãy chắc chắn rằng nó chỉ được gọi một lần bằng cách liên kết các điều kiện với mệnh đề else-if.

Đọc Tùy chọn

Theo mặc định, tất cả tùy chọn của ứng dụng của bạn đều được lưu vào một tệp có thể truy cập từ bất kỳ nơi nào trong ứng dụng của bạn bằng cách gọi phương pháp tĩnh PreferenceManager.getDefaultSharedPreferences(). Điều này sẽ trả về đối tượng SharedPreferences chứa tất cả cặp khóa-giá trị liên kết với các đối tượng Preference được sử dụng trong PreferenceActivity.

Ví dụ, sau đây là cách bạn có thể đọc một trong các giá trị tùy chọn từ bất kỳ hoạt động nào khác trong ứng dụng của mình:

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

Theo dõi thay đổi tùy chọn

Có một vài lý do khiến bạn có thể muốn được thông báo càng sớm càng tốt nếu người dùng thay đổi một trong các tùy chọn. Để nhận một phương pháp gọi lại khi thay đổi xảy ra với bất kỳ tùy chọn nào, hãy triển khai giao diện SharedPreference.OnSharedPreferenceChangeListener và đăng ký đối tượng theo dõi cho đối tượng SharedPreferences bằng cách gọi registerOnSharedPreferenceChangeListener().

Giao diện này chỉ có một phương pháp gọi lại, onSharedPreferenceChanged(), và bạn có thể thấy đây là cách dễ nhất để triển khai giao diện như một phần hoạt động của mình. Ví dụ:

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

Trong ví dụ này, phương pháp sẽ kiểm tra xem thiết đặt bị thay đổi có áp dụng cho một khóa tùy chọn đã biết không. Nó sẽ gọi findPreference() để nhận đối tượng Preference đã bị thay đổi để nó có thể sửa đổi tóm tắt của mục đó thành mô tả lựa chọn của người dùng. Cụ thể, khi thiết đặt là một ListPreference hoặc thiết đặt nhiều lựa chọn khác, bạn nên gọi setSummary() khi thiết đặt thay đổi để hiển thị trạng thái hiện tại (chẳng hạn như thiết đặt Ngủ như minh họa trong hình 5).

Lưu ý: Như đã mô tả trong tài liệu Thiết kế Android về Thiết đặt, chúng tôi khuyên bạn nên cập nhật tóm tắt cho ListPreference mỗi khi người dùng thay đổi tùy chọn để mô tả thiết đặt hiện tại.

Để quản lý vòng đời trong hoạt động cho phù hợp, chúng tôi khuyên rằng bạn nên đăng ký và bỏ đăng ký SharedPreferences.OnSharedPreferenceChangeListener của mình tương ứng trong onResume() và các lệnh gọi lại onPause():

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

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

Chú ý: Khi bạn gọi registerOnSharedPreferenceChangeListener(), trình quản lý tùy chọn hiện không lưu trữ một tham chiếu mạnh tới đối tượng theo dõi. Bạn phải lưu trữ một tham chiếu mạnh tới đối tượng theo dõi, nếu không nó sẽ dễ bị thu thập thông tin rác. Chúng tôi khuyên bạn nên giữ một tham chiếu tới đối tượng theo dõi trong dữ liệu thực thể của một đối tượng mà sẽ tồn tại miễn là bạn còn cần đối tượng theo dõi đó.

Ví dụ, trong đoạn mã sau, hàm gọi không giữ tham chiếu tới đối tượng theo dõi. Kết quả là đối tượng theo dõi sẽ bị thu thập thông tin rác, và nó sẽ bị lỗi tại một thời điểm không xác định trong tương lai:

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

Thay vào đó, hãy lưu một tham chiếu tới đối tượng theo dõi trong một trường dữ liệu thực thể của một đối tượng mà sẽ tồn tại miễn là còn cần đối tượng theo dõi đó:

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

Quản lý Sử dụng Mạng

Bắt đầu với Android 4.0, ứng dụng Thiết đặt của hệ thống sẽ cho phép người dùng xem ứng dụng của họ đang sử dụng bao nhiêu dữ liệu mạng khi đang ở tiền cảnh và dưới nền. Khi đó, người dùng có thể vô hiệu hóa việc sử dụng dữ liệu chạy ngầm cho từng ứng dụng. Để tránh việc người dùng vô hiệu hóa truy cập dữ liệu của ứng dụng của bạn từ dưới nền, bạn nên sử dụng kết nối dữ liệu một cách hiệu quả và cho phép người dùng tinh chỉnh mức sử dụng dữ liệu cho ứng dụng của bạn thông qua thiết đặt ứng dụng.

Ví dụ, bạn có thể cho phép người dùng kiểm soát tần suất ứng dụng của bạn đồng bộ dữ liệu, ứng dụng của bạn chỉ được thực hiện tải lên/tải xuống khi trên Wi-Fi, ứng dụng của bạn sử dụng dữ liệu trong khi đang chuyển vùng dữ liệu, v.v... hay không. Với những kiểm soát này, người dùng sẽ ít có khả năng vô hiệu hóa truy cập dữ liệu của ứng dụng của bạn hơn nhiều khi họ đạt gần mức giới hạn đặt ra trong Thiết đặt hệ thống, vì thay vào đó, họ có thể kiểm soát chính xác lượng dữ liệu mà ứng dụng của bạn sử dụng.

Sau khi bạn đã thêm các tùy chọn cần thiết trong PreferenceActivity của mình để kiểm soát các thói quen dữ liệu của ứng dụng của bạn, bạn nên thêm một bộ lọc ý định cho ACTION_MANAGE_NETWORK_USAGE trong tệp bản kê khai của mình. Ví dụ:

<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>

Bộ lọc ý định này cho hệ thống biết rằng đây là hoạt động kiểm soát mức sử dụng dữ liệu của ứng dụng của bạn. Vì thế, khi người dùng kiểm tra lượng dữ liệu mà ứng dụng của bạn đang dùng từ ứng dụng Thiết đặt của hệ thống, sẽ có một nút Xem thiết đặt ứng dụng khởi chạy PreferenceActivity của bạn, vì thế người dùng có thể tinh chỉnh lượng dữ liệu mà ứng dụng của bạn dùng.

Xây dựng một Thiết đặt Tùy chỉnh

Khuôn khổ Android bao gồm nhiều lớp con Preference mà cho phép bạn xây dựng một UI cho một vài loại thiết đặt khác nhau. Tuy nhiên, bạn có thể khám phá thiết đặt mình cần mà chưa có giải pháp tích hợp sẵn, chẳng hạn như một bộ chọn số hay bộ chọn ngày. Trong trường hợp như vậy, bạn sẽ cần tạo một tùy chọn tùy chỉnh bằng cách mở rộng lớp Preference hoặc một trong các lớp con khác.

Khi bạn mở rộng lớp Preference, có một vài điều quan trọng mà bạn cần làm:

  • Quy định giao diện người dùng sẽ xuất hiện khi người dùng chọn thiết đặt.
  • Lưu giá trị của thiết đặt khi phù hợp.
  • Khởi tạo Preference bằng giá trị hiện tại (hoặc mặc định) khi xét tới dạng xem.
  • Cung cấp giá trị mặc định khi hệ thống yêu cầu.
  • Nếu Preference cung cấp UI của chính mình (chẳng hạn như một hộp thoại), hãy lưu và khôi phục trạng thái để xử lý các thay đổi trong vòng đời (chẳng hạn như khi người dùng xoay màn hình).

Các phần sau mô tả cách hoàn thành từng tác vụ này.

Quy định một giao diện người dùng

Nếu bạn trực tiếp mở rộng lớp Preference, bạn cần triển khai onClick() để định nghĩa hành động xảy ra khi người dùng chọn mục. Tuy nhiên, hầu hết các thiết đặt tùy chỉnh sẽ mở rộng DialogPreference để hiển thị một hộp thoại, điều này làm đơn giản hóa quy trình. Khi bạn mở rộng DialogPreference, bạn phải gọi setDialogLayoutResourcs() trong khi đang ở trong hàm dựng lớp để quy định bố trí cho hộp thoại.

Ví dụ, sau đây là hàm dựng cho một DialogPreference tùy chỉnh mà khai báo bố trí và quy định văn bản cho các nút hộp thoại tích cực và tiêu cực mặc định:

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

Lưu giá trị của thiết đặt

Bạn có thể lưu một giá trị cho thiết đặt vào bất cứ lúc nào bằng cách gọi một trong các phương pháp của lớp Preference, persist*(), chẳng hạn như persistInt() nếu giá trị của thiết đặt là một số nguyên hoặc persistBoolean() để lưu một boolean.

Lưu ý: Mỗi Preference chỉ có thể lưu một kiểu dữ liệu, vì thế bạn phải sử dụng phương pháp persist*() phù hợp cho kiểu dữ liệu được sử dụng bởi Preference tùy chỉnh của mình.

Thời điểm bạn chọn duy trì thiết đặt có thể phụ thuộc vào lớp Preference nào mà bạn mở rộng. Nếu mở rộng DialogPreference, khi đó bạn nên duy trì giá trị đó chỉ khi hộp thoại đóng lại do kết quả tích cực (người dùng chọn nút "OK").

Khi DialogPreference đóng lại, hệ thống sẽ gọi phương pháp onDialogClosed(). Phương pháp bao gồm một tham đối boolean quy định xem người dùng có trả về kết quả "tích cực" hay không—nếu kết quả là true, khi đó, người dùng đã chọn nút tích cực và bạn nên lưu giá trị mới này. Ví dụ:

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

Trong ví dụ này, mNewValue là một thành viên lớp lưu giữ giá trị hiện tại của thiết đặt. Việc gọi persistInt() sẽ lưu giá trị vào tệp SharedPreferences (tự động sử dụng khóa mà được quy định trong tệp XML cho Preference này).

Khởi tạo giá trị hiện tại

Khi hệ thống thêm Preference của bạn vào màn hình, nó gọi onSetInitialValue() để thông báo với bạn xem thiết đặt có giá trị được duy trì hay không. Nếu không có giá trị được duy trì, lệnh gọi này sẽ cung cấp cho bạn giá trị mặc định.

Phương pháp onSetInitialValue() chuyển một boolean, restorePersistedValue, để cho biết liệu giá trị đã được duy trì cho thiết đặt hay không. Nếu nó là true, khi đó bạn nên truy xuất giá trị được duy trì bằng cách gọi một trong các phương pháp của lớp Preference, getPersisted*(), chẳng hạn như getPersistedInt() đối với một giá trị số nguyên. Bạn sẽ thường muốn truy xuất giá trị được duy trì sao cho bạn có thể cập nhật UI cho phù hợp để phản ánh giá trị đã lưu trước đó.

Nếu restorePersistedValuefalse, vậy bạn nên sử dụng giá trị mặc định được chuyển trong tham đối thứ hai.

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

Mỗi phương pháp getPersisted*() sẽ lấy một tham đối quy định giá trị mặc định sẽ sử dụng trong trường hợp thực sự không có giá trị được duy trì hoặc khóa không tồn tại. Trong ví dụ trên, một hằng số cục bộ được sử dụng để quy định giá trị mặc định trong trường hợp getPersistedInt() không thể trả về một giá trị được duy trì.

Chú ý: Bạn không thể sử dụng defaultValue làm giá trị mặc định trong phương pháp getPersisted*(), bởi giá trị của nó luôn rỗng khi restorePersistedValuetrue.

Cung cấp một giá trị mặc định

Nếu trường hợp lớp Preference của bạn quy định một giá trị mặc định (với thuộc tính android:defaultValue), khi đó hệ thống sẽ gọi onGetDefaultValue() khi nó khởi tạo đối tượng để truy xuất giá trị. Bạn phải triển khai phương pháp này để hệ thống lưu giá trị mặc định trong SharedPreferences. Ví dụ:

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

Các tham đối của phương pháp cung cấp mọi thứ bạn cần: mảng thuộc tính và vị trí chỉ mục của android:defaultValue mà bạn phải truy xuất. Lý do bạn phải triển khai phương pháp này nhằm trích xuất giá trị mặc định từ thuộc tính đó là bởi bạn phải quy định một giá trị mặc định cục bộ cho thuộc tính trong trường hợp giá trị không được định nghĩa.

Lưu và khôi phục trạng thái của Tùy chọn

Giống như View trong một bố trí, lớp con Preference của bạn chịu trách nhiệm lưu và khôi phục trạng thái của nó trong trường hợp hoạt động hoặc phân đoạn được khởi động lại (chẳng hạn như khi người dùng xoay màn hình). Để lưu và khôi phục trạng thái của lớp Preference của bạn cho đúng, bạn phải triển khai các phương pháp gọi lại vòng đời onSaveInstanceState()onRestoreInstanceState().

Trạng thái của Preference của bạn được định nghĩa bởi một đối tượng mà triển khai giao diện Parcelable. Khuôn khổ Android sẽ cung cấp một đối tượng như vậy cho bạn như một điểm bắt đầu để định nghĩa đối tượng trạng thái của bạn: lớp Preference.BaseSavedState.

Để định nghĩa cách thức lớp Preference của bạn lưu trạng thái của nó hãy mở rộng lớp Preference.BaseSavedState. Bạn cần khống chế chỉ một vài phương pháp và định nghĩa đối tượng CREATOR .

Đối với hầu hết ứng dụng, bạn có thể sao chép triển khai sau và chỉ cần thay đổi các dòng xử lý value nếu lớp con Preference của bạn lưu một kiểu dữ liệu khác số nguyên.

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

Với triển khai Preference.BaseSavedState bên trên được thêm vào ứng dụng của bạn (thường dưới dạng một lớp con của lớp con Preference của bạn), khi đó bạn cần triển khai các phương pháp onSaveInstanceState()onRestoreInstanceState() cho lớp con Preference của mình.

Ví dụ:

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