Preferences
Overview
PreferenceActivity
PreferenceActivity を用いることで、設定画面 XML 定義のみで、SharedPrefecences への読み書きを行なうことができる。
res/xml/prefs_settings.xml
:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:key="pref_general"
android:persistent="false"
android:title="General">
<CheckBoxPreference
android:key="pref_enabled"
android:title="Enabled" />
<EditTextPreference
android:key="pref_username"
android:title="Username" />
</PreferenceCategory>
</PreferenceScreen>
android:key
の値をキーとして SharedPreferences に読み書きされる。- 保存しないキーには
android:persistent="false"
を指定する。
PreferenceActivity#onCreate
内で、PreferenceActivity#addPreferencesFromResource
を用いて、XML リソースを読み込むだけでよい。
public class SettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs_settings);
}
}
全ての Android バージョンに対応しているが、サポートライブラリ v4-appcompat 用の PreferenceActivity は提供されていない。PreferenceActivity は、内部的にビュー全体を組み立てるため、v7-appcompat のテーマ Theme.Appcompat
等で利用した場合には、アクションバーが表示されない。
PreferenceFragment
Android 3.0 以降であれば PreferenceFragment を用いて Preference UI を利用することができる。
public class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.prefs_settings);
}
}
v4-appcompat の FragmentActivity では、android.support.v4.app.(Fragment|FragmentManager)
を使う必要があるが、サポートライブラリ v4-appcompat 用の PreferenceFragment は提供されていない。
Material Design のために、v7-appcompat の ActionBarActivity を使っている等の理由で、Android 3.0 以降のみのサポートであれば、あえて FragmentManager を使うこともできるが、FragmentManager#addToBackStack
が正しく動作しない。
// The back stack doesn't work in v4 FragmentActivity.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.addToBackStack(null)
.commit();
Activity#onBackPressed
で強制的に FragmentManager#popBackStack
を呼ぶ方法があるが、PreferenceFragment を使う場合は、Appcompat は使わないほうがよいだろう。
@Override
public void onBackPressed() {
FragmentManager manager = getFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
manager.popBackStack();
return;
}
super.onBackPressed();
}
Nested Preference Screen
Preference UI を複数階層に置きたい場合は、PreferenceScreen 要素を入れ子にすればよい。PreferenceScreen のタイトルがリンクになり、子の PreferenceScreen ごとに UI が作成される。
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="pref_parent"
android:title="Parent" />
<PreferenceScreen
android:persistent="false"
android:title="Children" />
<EditTextPreference
android:key="pref_child"
android:title="Child" />
</PreferenceScreen>
</PreferenceScreen>
Intent を用いて、任意のアクティビティを起動することもできるので、extra 値に設定カテゴリのキーを指定して、リソースを切り替える方法もある。カテゴリ階層ごとに Activity を作ってもよい。
res/xml/pref_settings.xml
:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="pref_parent"
android:title="Parent" />
<Preference
android:persistent="false"
android:title="Children">
<intent
android:targetClass="net.example.android.SettingsActivity"
android:targetPackage="net.example.android">
<!-- A key of the nested preference -->
<extra android:name="category" android:value="children" />
</intent>
</Preference>
</PreferenceScreen>
res/xml/pref_settings_children.xml
:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="pref_child"
android:title="Child" />
</PreferenceScreen>
public class SettingsActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String category = getIntent().getStringExtra("category");
if ("children".equlas(category)) {
addPreferencesFromResource(R.xml.prefs_settings_children);
} else {
addPreferencesFromResource(R.xml.prefs_settings);
}
}
}
Preference Headers
画面幅がある場合、設定カテゴリの一覧と設定コンテンツの二つのペインに切り替えたい場合がある。
PreferenceFragment を用いて、ヘッダ部とコンテンツ部に分ければよいが、シンプルなタイトルのみのヘッダであれば、PreferenceActivity だけで実現できる。
ヘッダ部の XML リソースを定義して、各カテゴリごとに、どの PreferenceFragment を使うか設定すればよい。
res/xml/prefs_headers.xml
:
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="net.example.android.GeneralPreferenceFragment"
android:title="General" />
<header
android:fragment="net.example.android.AdvancedPreferenceFragment"
android:title="Advanced" />
</preference-headers>
PreferenceActivity#onBuildHeaders
内で、PreferenceActivity#loadHeadersFromResource
を用いて、この XML を読み込むだけでよい。Android 4.4 以降の場合は、PreferenceActivity#isValidFragment
も実装する必要がある。
public class SettingsActivity extends PreferenceActivity {
...
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.prefs_headers, target);
}
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
protected boolean isValidFragment(String fragmentName) {
// Subclasses should override this method and verify
// that the given fragment is valid. If the SDK version
// is older than KITKAT, the default implementation
// always returns true.
return true;
}
}
Android 3.0 以前の場合は、このヘッダは使えない。別途ヘッダのみの PreferenceScreen を作成し、Intent により UI を切り替える。
res/xml/prefs_headers_legacy.xml
:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference android:title="General">
<intent
android:targetClass="net.example.android.SettingsActivity"
android:targetPackage="net.example.android">
<extra android:name="category" android:value="general" />
</intent>
</Preference>
<Preference android:title="Advanced">
<intent
android:targetClass="net.example.android.SettingsActivity"
android:targetPackage="net.example.android">
<extra android:name="category" android:value="advanced" />
</intent>
</Preference>
</PreferenceScreen>
public class SettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
String category = getIntent().getStringExtra("category");
int prefId = R.xml.prefs_headers_legacy;
if (null != category) {
if ("general".equals(category)) {
prefId = R.xml.prefs_settings_general;
} else if ("advanced".equals(category)) {
prefId = R.xml.prefs_settings_advanced;
}
}
addPreferencesFromResource(prefId);
}
}
}