Androidで PreferenceActivity を作っていた時のこと・・・
僕は AndroidX サポートライブラリを使っていて、「設定ダイアログのカスタマイズってどうやるのかな~」と疑問だったので調べてました。
そうすると情報は出てくるけど、手順が複雑そうな感じです。
- DialogPreference の仕様が変わったとか、
- PreferenceFragmentCompat を使えとか、
- onDisplayPreferenceDialog でアレコレしろとか、
色んな情報が出てきたけど,サンプル的なのがなくて苦戦
1日かけて、やっとPreferenceのダイアログのカスタマイズができました。
そこで忘備録も兼ね Preferenceでダイアログをカスタマイズする手順 をまとめます。
このページの目次
Preferenceでのダイアログのカスタマイズ手順
今回はサンプルとして、こういう設定を想定したダイアログを作ってみます。
- 色名を選択する Preference を作成
- タッチすると色名選択ダイアログが開く
- 色が選ばれたら Preference に値保存
実用性皆無だけど、サンプルということで・・・
こういう設定ダイアログを作る手順は次の通りです。
1.Preferenceを継承したクラスを作る
まず専用の Preference クラスを作成
ここでは ColorNamePreference という名前で作成してみました。
その名の通り、色名を保持する目的だけのPreferenceクラス
▼ そのコードがコチラ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
public class ColorNamePreference extends DialogPreference { String oldColorName = ""; public ColorNamePreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes ) { super(context, attrs, defStyleAttr, defStyleRes); } public ColorNamePreference(Context context, AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public ColorNamePreference(Context context, AttributeSet attrs ) { this(context, attrs, TypedArrayUtils.getAttr(context, R.attr.dialogPreferenceStyle, android.R.attr.dialogPreferenceStyle)); } @Override protected Object onGetDefaultValue( TypedArray arr, int index ) { return arr.getString(index); } @Override protected void onSetInitialValue( Object defaultValueObj ) { /// 設定画面表示時のオプション値を設定 String colorName = getPersistedString((String) defaultValueObj); setSummary( colorName ); } /** 現在のPreferenceの値を返す **/ public String retrieveColorName() { return getPersistedString(""); } /** Preferenceに値を設定&反映させる **/ public void saveColorName( String colorName ) { final boolean changed = (colorName != null && !oldColorName.equals(colorName)); if ( changed ) { persistString( colorName ); notifyChanged(); setSummary( colorName ); } } } |
全部説明するとキリがないので、何をしてるかはコメント参照
ダイアログをカスタマイズするときは、こういう風に Preference を拡張したクラスを作っておくと設定の管理がしやすくなります。(なくてもできるけど面倒くさくなる)
2.PreferenceDialogFragmentCompat を拡張したクラス作成
お次は PreferenceDialogFragmentCompat の拡張
これを拡張することで、オリジナルのPreferenceダイアログが作れます。
今回はサンプルなので、色名を選択するダイアログ を作ってみました。
▼ そのダイアログのコード例はこんな感じ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
public class ColorNamePreferenceDialogFragment extends PreferenceDialogFragmentCompat { /// 現在選択されてる色名 private String colorName = ""; /// ダイアログにリスト表示する色名リスト private final ArrayList<CharSequence> COLOR_NAMES = new ArrayList<CharSequence>() {{ add("Red"); add("Orange"); add("Yellow"); add("Green"); add("Blue"); add("Purple"); }}; /// 必ずこのメソッドは定義しないとダメ public static ColorNamePreferenceDialogFragment newInstance(String key) { ColorNamePreferenceDialogFragment fragment = new ColorNamePreferenceDialogFragment(); Bundle b = new Bundle(1); b.putString(PreferenceDialogFragmentCompat.ARG_KEY, key); fragment.setArguments(b); return fragment; } @Override protected void onPrepareDialogBuilder( AlertDialog.Builder builder) { super.onPrepareDialogBuilder(builder); ColorNamePreference preference = getColorNamePreference(); String prefValue = preference.retrieveColorName(); int selectedIndex = COLOR_NAMES.contains( prefValue ) ? COLOR_NAMES.indexOf( prefValue ) : 0; /** ダイアログ実体を作成する **/ final CharSequence[] colorNames = COLOR_NAMES.toArray(new CharSequence[0]); builder.setSingleChoiceItems(colorNames, selectedIndex, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { colorName = COLOR_NAMES.get( i ).toString(); } } ) .setNegativeButton(android.R.string.cancel, this) .setPositiveButton(android.R.string.ok, this); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { /** 必要ならココでレイアウトとか指定可能 **/ AlertDialog dialog = (AlertDialog) super.onCreateDialog(savedInstanceState); return dialog; } @Override public void onDialogClosed(boolean positiveResult) { /** OKボタンが押されたなら設定保存 **/ final ColorNamePreference preference = getColorNamePreference(); if ( positiveResult ) { if ( preference.callChangeListener( colorName ) ) { preference.saveColorName( colorName ); } } } /** 拡張したPreferenceクラスを返す **/ private ColorNamePreference getColorNamePreference() { return (ColorNamePreference) getPreference(); } } |
何をしてるかはコメント参照
一応説明しておくと、特に大事なのは次の3つ
- newInstanceメソッド
必ずこれを作っておく。カスタマイズしたダイアログ表示に使われる
- onPrepareDialogBuilderメソッド
この中でダイアログに OnClickListener とかボタンの設定ができる
- onDialogClosedメソッド
ダイアログが閉じられると呼ばれ、もしOKボタンが押されてたら Preference に値を保存する
この部分が中々情報が少なくてもコードを書くのに苦戦してしまった・・・
でもこういう感じでカスタマイズすればいいと分かれば簡単です。
3.PreferenceActivityのレイアウト作成
次に拡張した Preference をレイアウト上に設置します。
例えばさっきの ColorNamePreference ならこういう感じです。
▼ root_preferences.xml に追加しておく
1 2 3 4 5 |
<com.hoge.example.ColorNamePreference android:key="color_name" android:summary="not selected" android:title="Color name" app:iconSpaceReserved="false" /> |
さっき作った ColorNamePreference をレイアウト上にそのまま書けばいいだけ
4.PreferenceFragmentCompat内でダイアログ呼び出し
最後に PreferenceActivity でダイアログの表示を制御
具体的には PreferenceFragmentCompat を継承したクラスを作ったので、その中の onDisplayPreferenceDialog メソッド内でダイアログフラグメントを呼び出すだけです。
言葉だと分かりにくいので実際のコードで示すと次みたいな感じ
▼ PreferenceActivity内で・・・
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public static class MyPreferenceFragment extends PreferenceFragmentCompat { public MyPreferenceFragment() { } @Override public void onDisplayPreferenceDialog(Preference preference) { if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) { return; } final DialogFragment f; if( preference instanceof ColorNamePreference ) { /// カスタマイズしたダイアログをインスタンス化 f = ColorNamePreferenceDialogFragment.newInstance(preference.getKey()); } else { /// ダイアログは表示しない(通常) f = null; } if (f != null) { /// カスタマイズしたダイアログを表示 f.setTargetFragment(this, 0); f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); } else { super.onDisplayPreferenceDialog(preference); } } } |
SettingsActivity内で必要なのはこれだけ
たったこれだけだけど、この方法もよくわからなくて苦戦したポイントです。
実際にカスタマイズしたダイアログを表示してみた
実際にどういう感じで表示されるかは次の通り
▼ 設定画面で色名選択をタッチ
▼ カスタムしたダイアログが表示された
▼ 好きな色名を選んでOKを押すと・・・
▼ ちゃんと設定が保存されて反映された
Preferenceとして機能してくれてます。
難しそうに思ってたけど、やってみると意外と簡単ですね。ではまた($・・)/~~~