【Androidアプリケーションの設定画面を作ろう】PreferenceActivityで設定画面を作る方法

このエントリーを Google ブックマーク に追加
Pocket
[`yahoo` not found]

PreferenceActivityは設定画面を作るためのActivityです。
Android3.0(API レベル 11)を境に前と後で実装方針が大きく変化しています。
  • Android3.0以前はPreferenceActivityを単体で使い設定画面を作成します。
  • Android3.0以後は「ActivityとPreferenceFragment」や「PreferenceActivityとPreferenceFragment」を組み合わせて作成します。
Android3.0(API レベル 11)以降はPreferenceActivityで直接設定項目を操作することは非推奨となり、 通常のActivityにPreferenceFragmentを配置して作成するか、PreferenceActivityにPreferenceFragmentを配置して複数階層構造を作る方法が推奨されています。 今回は、Android3.0(API レベル 11)以降の実装方法を説明します。
新しい実装方法では設定画面はヘッダーリストペインとコンテンツペインの二つのペインで構成されます。
ヘッダーとはPreferenceFragmentを元に作られたPreferenceActivity.Headerのことを指します。このヘッダーを並べたリストをヘッダーリストと呼びます。
ヘッダーリストの項目をクリックすることでPreferenceFragment内で設定された設定項目がコンテンツペインに表示されます。

ヘッダーリストを定義する

ヘッダーリストはxmlファイルを使って定義します。ルートタグにはpreference-headersを使用します。
このpreference-headersタグの子要素にheaderタグを並べることでヘッダーをリスト化することができます。
headerタグには下記の属性があります。
android:id識別するidを設定します。
android:iconアイコン画像を画像リソースIDを指定します。
android:titleタイトルを設定します。
android:summaryヘッダーリストの説明を設定します。
android:breadCrumbTitleパンくずリストのタイトルを設定します。
android:breadCrumbShortTitleパンくずリストの省略タイトルを設定します。
android:fragmentヘッダーに対応するフラグメントを完全修飾名で指定します
headerタグの子要素としてextraタグとintentタグがあります。
extraタグを設定することでandroid:fragmentに指定したFragmentに値を渡すことができます。 一方、intentタグを設定することでインテントからActivityを起動することができます。
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
    <header
        android:id="@+id/setting_1"
        android:icon="@drawable/ic_info_black_24dp"
        android:title="設定 1"
        android:summary="summary"
        android:breadCrumbTitle="breadCrumbTitle"
        android:breadCrumbShortTitle="breadCrumbShortTitle"
        android:fragment="yona.mypreference.MyPreference1Activity$Prefs1Fragment"/>
    <header
        android:id="@+id/setting_2"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="設定 2"
        android:summary="summary"
        android:breadCrumbTitle="breadCrumbTitle"
        android:breadCrumbShortTitle="breadCrumbShortTitle"
        android:fragment="yona.mypreference.MyPreference1Activity$Prefs2Fragment">
        <extra
            android:name="header"
            android:value="value from xml"/>
    </header>
    <header
        android:icon="@drawable/ic_info_black_24dp"
        android:title="設定 3">
        <intent android:action="android.settings.SYNC_SETTINGS" />
    </header>
</preference-headers>
また、上記のandroid:fragment属性に設定されているクラスを内部クラスとして定義します。
これらのPreferenceActivityで使用されるFragment達は事前に登録されている必要があります。
boolean isValidFragment(String fragmentName)をオーバーライドします、引数はフラグメントの名前が渡されます。
この名前を元に正当なフラグメントの場合はtrueを、不正なフラグメントの場合はfalseを返却するように実装します。
    public static class Prefs1Fragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_general);
        }
    }

    public static class Prefs2Fragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Bundle arguments = getArguments();
            addPreferencesFromResource(R.xml.pref_data_sync);
        }
    }

    @Override
    protected boolean isValidFragment(String fragmentName) {
        return Prefs1Fragment.class.getName().equals(fragmentName) || Prefs2Fragment.class.getName().equals(fragmentName);
    }

設定画面を作成する

ヘッダーリストを作成するにはvoid onBuildHeaders(List target)をオーバーライドします。
PreferenceActivity#onBuildHeadersはPreferenceActivityがヘッダーリストを作成するタイミングで呼ばれます。
PreferenceActivity#onBuildHeadersの中でvoid loadHeadersFromResource(int resid, List target)を呼びます。
第一引数にはヘッダーリストを定義したxmlファイルリソースIDを渡します。第二引数にはPreferenceActivity#onBuildHeadersの引数で渡されたインスタンスを渡します。
また、ヘッダーリストが作成された後にPreferenceActivity.Header onGetInitialHeader()が呼ばれます。
onGetInitialHeaderをオーバーライドすることで最初に表示する設定画面を変更することができます。

    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    @Override
    public Header onGetInitialHeader() {
        Header header = new Header();
        header.fragment = Prefs2Fragment.class.getName();
        header.breadCrumbTitle = "onGetInitialHeader";
        return header;
    }

また、PreferenceActivityがヘッダーリストを使っているかどうかはboolean hasHeaders()を使用します。 PreferenceActivity#hasHeadersはヘッダーリストを使用している時にtrueを返却し、使用していない時にfalseを返却します。

ペインのモードを変更する

PreferenceActivityで作ることができる設定画面には二つの表示モードがあります。
シングルペインモードとマルチペインモードの二つの表示モードがあります。
シングルペインモードは最初の画面にヘッダーリストが表示され、ヘッダーリストの項目をクリックした時に次の画面が表示され、コンテンツペインが表示されます。
マルチペインモードはヘッダーリストペインとコンテンツペインを並べて表示され、ヘッダーリストから選択したヘッダーに対応するPreferenceFragmentがコンテンツペインに配置されます。
モードを切り替える時はboolean onIsMultiPaneをオーバーライドします。
今回は画面が縦の時はシングルペインモード、横の時はマルチペインモードになるようにします。
    @Override
    public boolean onIsMultiPane() {
        int orientation = getResources().getConfiguration().orientation;
        return orientation == Configuration.ORIENTATION_LANDSCAPE;
    }
また、ペインモードを知るにはboolean isMultiPaneを使用します。現在、マルチペインの場合はtrueを返却し、 シングルペインの場合はfalseを返却します。

ヘッダーのクリックを検知する

void onHeaderClick(PreferenceActivity.Header header, int position)はヘッダーリストをクリックした時に呼ばれるメソッドです。
そのため、ヘッダーがクリックされた時に何かをしたい時はPreferenceActivity#onHeaderClickをオーバーライドします。
第一引数はクリックされたヘッダーのPreferenceActivity.Headerインスタンス、第二引数はクリックされたポジションとなります。
    @Override
    public void onHeaderClick(Header header, int position) {
        super.onHeaderClick(header, position);
        Log.d(TAG, "onHeaderClick: " + position);
        Log.d(TAG, "onHeaderClick: " + header.fragment);
    }

まとめ

今回はPreferenceActivityを使った近代的な設定画面の実装方法に必要なメソッドについて説明しました。
public class MyPreference1Activity extends PreferenceActivity {

    private static final String TAG = "MyPreference1Activity";

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

    @Override
    public boolean onIsMultiPane() {
        int orientation = getResources().getConfiguration().orientation;
        return orientation == Configuration.ORIENTATION_LANDSCAPE;
    }

    @Override
    public void onBuildHeaders(List<Header> target) {
        loadHeadersFromResource(R.xml.pref_headers, target);
    }

    @Override
    protected boolean isValidFragment(String fragmentName) {
        return Prefs1Fragment.class.getName().equals(fragmentName) || Prefs2Fragment.class.getName().equals(fragmentName);
    }

    @Override
    public void onHeaderClick(Header header, int position) {
        super.onHeaderClick(header, position);
        Log.d(TAG, "onHeaderClick: " + position);
        Log.d(TAG, "onHeaderClick: " + header.fragment);
    }

    public static class Prefs1Fragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.pref_general);
        }

        @Override
        public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
            ((MyPreference1Activity)getActivity()).finishPreferencePanel(this,100,new Intent());
            return super.onPreferenceTreeClick(preferenceScreen, preference);
        }
    }

    public static class Prefs2Fragment extends PreferenceFragment {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Bundle arguments = getArguments();
            addPreferencesFromResource(R.xml.pref_data_sync);
        }
    }
}