Androidの設定画面では、PreferenceScreenをクリックすると次の階層の設定画面に進みます。
これはActivityのように思えますが、実際にはダイアログが表示されています。
このタイトル部分のスタイルを変えようと思ったのですが、最初の画面だけが変更され、PreferenceScreenで遷移した先の画面のタイトルバーを変えられず、苦労したので記録しておきます。
結論から言うと、PreferenceScreenのタイトルバーはカスタマイズできず、同じような見た目・挙動になる別の方法をとりました。
まず該当部分をカスタマイズできるのか、Androidのソースコードを見てみたところ、android.preference.PreferenceScreenに以下のような記述がありました。
private void showDialog(Bundle state) {
Context context = getContext();
if (mListView != null) {
mListView.setAdapter(null);
}
mListView = new ListView(context);
bind(mListView);
// Set the title bar if title is available, else no title bar
final CharSequence title = getTitle();
Dialog dialog = mDialog = new Dialog(context, TextUtils.isEmpty(title)
? com.android.internal.R.style.Theme_NoTitleBar
: com.android.internal.R.style.Theme);
dialog.setContentView(mListView);
if (!TextUtils.isEmpty(title)) {
dialog.setTitle(title);
}
dialog.setOnDismissListener(this);
if (state != null) {
dialog.onRestoreInstanceState(state);
}
// Add the screen to the list of preferences screens opened as dialogs
getPreferenceManager().addPreferencesScreen(dialog);
dialog.show();
}
これを見ると、Dialogに適用されるテーマはデフォルトのThemeかTheme_NoTitleBarとなっていて、外から変えられません。
ではこのdialogを操作できるかというと、ローカル変数なので操作できません。
それならshowDialog()の呼び出し元をオーバーライドして書き換えてしまえば…と思いましたが、これもダメです。
PreferenceScreenはfinalなので継承できません。
つまり、PreferenceScreenのスタイルを変えたい場合は、この部分だけ変更可能にしたカスタムPreferenceScreenを作成しなくてはなりません。
これはあまり良い方法に思えませんでしたので、別の方法を探りました。
他にも方法はあるかもしれませんが、Activityを分ける方法でうまくいきました。
PreferenceActivityの本体のタイトルバーは変更できるので、PreferenceScreenの代わりにPreferenceを使い、そのPreferenceにOnClickListenerを設定して、クリック時に次の画面のPreferenceActivityを呼び出すという方式です。
以下、具体的な方法です。( BasicWall に適用しています。)
ソースコードは重要な部分を抜粋・改変したものなので、そのままでは動かないかもしれません。
ベースとなるPreferenceActivityを作成します(FirstPreferenceActivityとします)。
通常PreferenceScreenとして作成する画面を別のPreferenceActivityとして作成します(SecondPreferenceActivityとします)。
プリファレンスのXMLを定義します(res/xml/preferences.xmlとします)。PreferenceScreenだった部分は単純なPreferenceとして定義します。
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen ...>
<Preference
android:key="pref_second"
android:title="..."
android:summary="..." />
:
</PreferenceScreen>
PreferenceScreenの中に含めていたPreferenceの定義を別のXMLに移します(preferences_second.xmlとします)。
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen ...>
<ListPreference
:
</PreferenceScreen>
FirstPreferenceActivityのPreference(キーはpref_second)をクリックしたときに、SecondPreferenceActivityを起動するようにします。
protected final void onCreate(final Bundle savedState) {
:
addPreferencesFromResource(R.xml.preferences);
Preference pref = findPreference("pref_second");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(final Preference preference) {
startActivity(new Intent(FirstPreferenceActivity.this,
SecondPreferenceActivity.class));
return true;
}
});
}
SecondPreferenceActivityでは、普通のPreferenceActivityと同様にXMLからプリファレンスを読み込みます。
protected final void onCreate(final Bundle savedState) {
:
addPreferencesFromResource(R.xml.preferences_second);
:
}
各アクティビティをAndroidManifest.xmlに登録します。
<application ..>
<activity android:label="First preferences" android:name=".FirstPreferenceActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:label="Second preferences" android:name=".SecondPreferenceActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
:
各PreferenceActivityに適用するタイトルバーのスタイルを、テーマとして定義します。例えば、res/values/styles.xmlに以下のように書きます。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="CustomTheme.Black" parent="@android:style/Theme.Black">
<item name="android:windowTitleBackgroundStyle">@style/WindowTitleBackground</item>
<item name="android:windowTitleStyle">@style/WindowTitle</item>
<item name="android:windowTitleSize">45dip</item>
</style>
<style name="WindowTitleBackground">
<item name="android:background">@drawable/title_background</item>
</style>
<style name="WindowTitle">
<item name="android:singleLine">true</item>
<item name="android:textAppearance">@style/TextAppearance.WindowTitle</item>
<item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
<item name="android:paddingLeft">10dip</item>
<item name="android:typeface">sans</item>
</style>
<style name="TextAppearance.WindowTitle" parent="@android:style/TextAppearance.WindowTitle">
<item name="android:textColor">#fff</item>
<item name="android:textSize">18sp</item>
<item name="android:textStyle">bold</item>
</style>
</resources>
上記のようにスタイルを定義しているときは、グラデーションをDrawableとして作成する必要があります。上記に合わせるとres/drawable/title_background.xmlです。
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#FF444444"
android:endColor="#FF222222"
android:angle="270" />
</shape>
テーマをアプリケーション全体もしくはアクティビティに適用します。
アプリケーション全体なら以下のようにします。
<application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/CustomTheme.Black">
これで、階層化されたプリファレンスのタイトルバーを全てカスタマイズできるはずです。
(もちろん、この方法ならタイトルバー以外もカスタマイズできるはずです。)
(完成版のイメージは、 BasicWall をダウンロードしてご確認いただけると嬉しいです。)