Ứ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ụngPreferenceActivity
để 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ọn và Sử 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ộtString
.
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ặcPreferenceScreen
, hoặc tùy chọn quy định mộtIntent
để gọi ra (bằng phần tử<intent>
) hoặcFragment
để hiển thị (bằng thuộc tínhandroid: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ử <PreferenceScreen>
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ử <intent>
làm con của phần tử <Preference>
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ặcKEY_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:
- 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. - 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.
- Mở rộng lớp
PreferenceActivity
để lưu trữ các thiết đặt của bạn. - 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ử <header>
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ử <header>
đơn lẻ bên trong một phần tử <preference-headers>
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ử <extras>
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 <extra>
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 <preference-headers>
đã đượ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 Android 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 <intent>
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 restorePersistedValue
là false
, 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 restorePersistedValue
là true
.
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()
và 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()
và 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); }