XMLレイアウトでAndroidアプリに“設定画面”を追加Androidで動く携帯Javaアプリ作成入門(19)(3/3 ページ)

» 2010年08月19日 00時00分 公開
[緒方聡,株式会社イーフロー]
前のページへ 1|2|3       

リスナーの登録

 XMLですべてのレイアウトを定義できますが、「値が変更された」「クリックされた」というリスナーは、コーディングしなければなりません。以下に1つ取り上げてみます。

// チェックボックス設定のインスタンスを、キーを基に取得する
CheckBoxPreference cbp = (CheckBoxPreference)findPreference("checkbox_preference");
// リスナーを設定する
cbp.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        String summary;
        if (((Boolean)newValue).booleanValue()) {
            summary = "Selected";
        } else {
            summary = "Unselected";
        }
        // 更新された値に応じて概要を変更する
        ((CheckBoxPreference)preference).setSummary(summary);
        // 変更を適用するために true を返す
        return true;
    }
});

 設定を変更することで、以下のように概要が変化します。

図3 概要の変化 図3 概要の変化

 XMLレイアウトで必要なコーディングは、基本的にこのリスナー登録だけです。今回のリスナーは「入力の状態に応じて概要を変更する」のみですが、実際には「入力チェックを行って、不正な入力の際には値を反映させない(falseを返す)」「リングトーン設定であれば、実際にデバイスの設定を変更する」などが行われます。

 実際に動作させてみて気が付かれた方がいるかもしれませんが、リスナーは変更時に概要を設定し直すので、画面表示時には状態が分かりません。

図4 開いてみるまで設定内容が分からない 図4 開いてみるまで設定内容が分からない

 このような場合はPreferenceManagerを使用して、現在の設定内容を取得します。PreferenceManagerの使い方は、この後すぐに解説します。

設定値を取得する

 PreferenceActivityで設定された内容は、PreferenceActivityの表示時に自動的に読み込まれますが、設定された内容をアプリ本体で取得する場合は、PreferenceManagerとSharedPreferencesを使用します。

// 【1】デフォルトの SharedPreference を取得する
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
// 【2】設定内容をコミットする
sp.edit().commit();
// 【3】設定内容をすべて取得する
Map<String, ?> map = sp.getAll();
 
CharSequence[] list = new CharSequence[map.size()];
int i = 0;
for (String key : map.keySet()) {
    list[i++] = key + "=" + map.get(key);
}

 【1】では、デフォルトのSharedPreferencesを取得しています。SharedPreferencesは、名前を付けて取得するメソッド「ContextWrapper#getSharedPreferences(String, int)」が用意されていて、このメソッドを使用する場合、以下の呼び出しが同じ動作になります。

getSharedPreferences("com.example.android.preference_preferences", MODE_PRIVATE);

 デフォルトのSharedPreferencesの名前は、「パッケージ名_preference」というふうに決まっています。設定を初期化したい場合は、このファイルを削除すれば簡単に実現できます。1つのアプリで複数の設定を使用したい場合は、デフォルトを使用するのではなく、上記メソッドを使用して作成するとよいでしょう。作成する際に、第2引数に、定数「MODE_WORLD_READABLE」「MODE_WORLD_WRITEABLE」を指定してパーミッションをコントロールできます。

 【2】では、SharedPreferencesのエディタを取得して、設定内容をコミットしています。SharedPreferencesは、永続化のためにストレージ上にファイルとして保存されますが、保存されるのはアプリが終了するタイミングで、それまではメモリ上に展開されています。ここでcommit()メソッドを呼び出しておくことで、メモリ上で変更された内容をストレージにコミットし、その内容を取得するようにしています。

 【3】では、すべての設定項目をMapオブジェクトとして取得しています。個別にキーとデフォルト値を指定して値を取得するgetBoolean()、getFloat()、getInt()、getLong()、getString()が用意されているので、通常はこちらを使用します。

コンポーネントの依存関係を決めるには

 しばしば設定項目には依存関係があるものもあります。例えばWi-Fiを使う場合はアクセスポイントの設定が必要ですが、Wi-Fiを使用しない場合はアクセスポイントの設定は不要です。

 今回のアプリでは、以下のように動作します。

図5 親がチェックされている場合のみ、子が編集可能 図5 親がチェックされている場合のみ、子が編集可能

 このような依存関係もXMLで簡単に定義できます。

【1】↓依存元の設定項目を定義
<CheckBoxPreference
    android:key="parent_checkbox_preference"
    android:title="@string/parent_preference_title"
    android:summary="@string/parent_preference_summary" />
<CheckBoxPreference
    android:key="child_checkbox_preference"
【2】↓依存元のキーを指定
    android:dependency="parent_checkbox_preference"
【3】↓自動的に使用可・使用不可が切り替わるレイアウトを設定
    android:layout="?android:attr/preferenceLayoutChild"
    android:title="@string/child_preference_title"
    android:summary="@string/child_preference_summary" />

 親の設定項目を定義し、子の設定項目のandroid:dependencyに親のキーを、「android:layout」に「?android:attr/preferenceLayoutChild」を指定します。この設定を行うことで、「親がチェックされているときだけ子が使用できる」という動作が実現できます。

 これをコーディングで行う場合、以下のようになります。

// 子設定を生成する際にonDependencyChangedをオーバーライド
CheckBoxPreference childCheckBoxPref = new CheckBoxPreference(this) {
    @Override
    public void onDependencyChanged(Preference dependency, boolean disableDependent) {
        setEnabled(!disableDependent);
    }
};
 
setPreferenceScreen(createPreferenceHierarchy());
// setPreferenceScreen が終わってから setDependency() を呼び出し
getPreferenceManager().findPreference("child_checkbox_preference").setDependency("parent_checkbox_preference");

 本来であれば、setDependency()というメソッドで依存関係を設定できますが、setLayoutResourceで設定するandroid.R.attr.preferenceLayoutChildが正常に機能しないため、期待通りの動作になりません(常に使用可能です)。ApiDemoのオリジナルのソースコードは別の不具合(setKey()が行われていない)がありますが、不具合を修正してもやはり期待通りに動作しません。

 Androidのソースコードを検索してsetDependency()を使用している個所を確認したところ、使用しているすべてでXMLレイアウトの補助として使用されていることが分かり、XMLレイアウトで定義したファイルに対してsetDependency()を行ったところ、期待通りの動作になるため、これはもしかするとAndroidの不具合なのかもしれません。

 上記の回避方法でも、まったく同様の動作ですが、XMLレイアウトは属性を2つ追加するだけなので、前述したとおりレイアウトはXMLで行うことを強く推奨します。

次回は、アニメーションで華やかな演出を

 アプリに設定を持たせたい場合は、ごく簡単なものであればMenuなどでトグル状態を実装するなどの方法がありますが、いずれにせよ設定された値は永続化しなければならないため、その手間を考えるとPreferenceActivityを使うことは良い選択肢といえるでしょう。

 次回はアニメーションで華やかな演出を行う方法について解説する予定です。


「Androidで動く携帯Javaアプリ作成入門」バックナンバー
前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。