Edited at

AlertDialogのスタイルをカスタマイズ

More than 1 year has passed since last update.

この記事は、Android その3 Advent Calendar 2016 - Qiitaの12日目の記事です!


注意!!

この記事は、appcompat-v7のバージョン25.0.1で試した内容です。最新バージョン25.1.0では、android.support.v7.app.AlertControllerの実装が変更されており、使用できない箇所があります。



AlertDialogのスタイルをカスタマイズ

Support Library v7 appcompat ライブラリを使用するると、Android 2.3(API レベル 9)以上のデバイスで、アプリでマテリアルデザインを使用することができ、ダイアログもマテリアルデザインになります。

import android.support.v7.app.AlertDialog;

// 省略

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("確認");
builder.setMessage("XXXしてもよろしいですか?");
builder.setPositiveButton("はい", null);
builder.setNegativeButton("いいえ", null);
builder.setNeutralButton("キャンセル", null);
builder.create().show();

device-2016-12-09-143313.png

NeutralButtonが左端にあり、PositiveButtonとNegativeButtonが右に寄ったデザインですね。

通常のユーザーが使用するアプリであればこのデザインでよいとは思いますが、幼児/高年齢向けアプリを考えたとき、もっと大きな文字で、押しやすいボタンのデザインにしたいと思ったので、ダイアログをカスタマイズしてみます。


styles.xml

まず、スタイルを定義します。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

</style>


AlertDialog.Builderの、第二引数 int themeResIdにテーマを指定することでカスタムのスタイルの適用することができます。AlertDialog.Builder | Android Developers

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);

アプリすべてのダイアログにスタイルを適用する場合は、AppThemeのandroid:alertDialogThemeに指定します。


themes.xml

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="android:alertDialogTheme">@style/MyAlertDialogStyle</item>
</style>


ボタンのテキストカラー

colorAccentを指定します。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

<item name="colorAccent">@color/primary</item>
</style>

device-2016-12-11-220726.png


タイトルとボタンのテキストの大きさ

android:textSizeを指定します。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

<item name="colorAccent">@color/primary</item>
<item name="android:textSize">@dimen/dialog_text_size</item>
</style>

device-2016-12-11-221633.png

ボタンが縦に並んでしまいました...

メッセージのサイズは変更されませんね...


ダイアログの大きさを変更

android:windowMinWidthMajor, android:windowMinWidthMinorを指定します。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

<item name="colorAccent">@color/primary</item>
<item name="android:textSize">@dimen/dialog_text_size</item>
<item name="android:windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
</style>


  • android:windowMinWidthMajor ... 最大のサイズ

  • android:windowMinWidthMinor ... 最小のサイズ


dimenは画面全体の比率(%)で指定することができます。


dimens.xml

<resources>

<item type="dimen" name="dialog_min_width_major">95%</item>
<item type="dimen" name="dialog_min_width_minor">95%</item>
</resources>

device-2016-12-11-223226.png


テキストスタイル

android:textColorPrimaryを指定するとテキストカラーを変更できます。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

<item name="colorAccent">@color/primary</item>
<item name="android:textColorPrimary">@color/colorPrimary</item>
</style>

android:windowTitleStyleを定義するとタイトルのスタイルを変更できます。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

<item name="colorAccent">@color/primary</item>
<item name="android:windowTitleStyle">@style/windowTitleStyle</item>
</style>

<style name="windowTitleStyle" parent="TextAppearance.AppCompat.Large">
<item name="android:textSize">@dimen/dialog_text_size</item>
</style>



メッセージのテキストサイズ

メッセージのテキストサイズを指定きる属性が見当たりませんでしたのでコードでハックします。

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);

builder.setTitle("確認");
builder.setMessage("XXXしてもよろしいですか?");
builder.setPositiveButton("はい", null);
builder.setNegativeButton("いいえ", null);
builder.setNeutralButton("キャンセル", null);

AlertDialog dialog = builder.show();

// メッセージテキストを変更する。
TextView textView = (TextView) dialog.findViewById(android.R.id.message);
textView.setTextSize(48.0f);

device-2016-12-11-224301.png


ボタンのスタイル

buttonBarNeutralButtonStyle, buttonBarNegativeButtonStyle, buttonBarPositiveButtonStyle で指定することが出来ます。


styles.xml

<style name="MyAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog">

<item name="colorAccent">@color/primary</item>
<item name="android:textSize">@dimen/dialog_text_size</item>
<item name="android:windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@dimen/dialog_min_width_minor</item>

<item name="buttonBarNeutralButtonStyle">@style/buttonBarButtonStyle.Neutral</item>
<item name="buttonBarNegativeButtonStyle">@style/buttonBarButtonStyle.Negative</item>
<item name="buttonBarPositiveButtonStyle">@style/buttonBarButtonStyle.Positive</item>

</style>



テキストカラー


styles.xml

<style name="buttonBarButtonStyle" parent="Widget.AppCompat.Button.Borderless.Colored">

</style>

<style name="buttonBarButtonStyle.Neutral">
</style>

<style name="buttonBarButtonStyle.Negative">
<item name="android:textColor">@color/red</item>
</style>

<style name="buttonBarButtonStyle.Positive">
<item name="android:textColor">@color/blue</item>
</style>


親(parent)に Widget.AppCompat.Button.Borderless.Colored を指定すると、テキストカラーのデフォルトがcolorAccentになります。

device-2016-12-11-230939.png


カラーボタン

親(parent)に Widget.AppCompat.Button.Coloredを指定すると、colorAccentを背景にしたボタンを配置されます。


styles.xml

<style name="buttonBarButtonStyle" parent="Widget.AppCompat.Button.Colored">

</style>

<style name="buttonBarButtonStyle.Neutral">
</style>

<style name="buttonBarButtonStyle.Negative">
<item name="android:textColor">@color/red</item>
</style>

<style name="buttonBarButtonStyle.Positive">
<item name="android:textColor">@color/blue</item>
</style>


device-2016-12-11-235950.png


背景

背景はのサイズを大きくしたり、カラーを変更したり場合には、android:backgroundを変更します。

リップル効果が着いた背景にしたい場合は、appcompatのソースを参考にするとよいでしょう。

背景カラーを変更したり場合には、app:backgroundTint を指定します。

<style name="buttonBarButtonStyle.Positive">

<item name="backgroundTint">@color/colorAccent</item>
</style>


ボタンの配置

マテリアルデザイン以前のダイアログように、ボタンが中央に3つ並べたスタイルにしたい場合は、コードでハックします。

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this, R.style.MyAlertDialogStyle);

builder.setTitle("確認");
builder.setMessage("XXXしてもよろしいですか?");
builder.setPositiveButton("はい", null);
// builder.setNegativeButton("いいえ", null);
builder.setNeutralButton("キャンセル", null);

AlertDialog dialog = builder.show();

// メッセージテキストを変更する。
TextView textView = (TextView) dialog.findViewById(android.R.id.message);
textView.setTextSize(48.0f);

// ボタンパネルのコンテナを取得する。
LinearLayout containerButtons = (LinearLayout) dialog.findViewById(R.id.buttonPanel);

// Space Viewを削除する。
containerButtons.removeView(containerButtons.getChildAt(1));

// ボタンパネルの子ViewのボタンのGravityをCENTERに設定する
containerButtons.setGravity(Gravity.CENTER);

// LinearLayoutの比率の合計を3に設定する。
containerButtons.setWeightSum(3.0f);

// 各ボタンを取得する。
Button button3 = (Button) containerButtons.findViewById(android.R.id.button3);
Button button2 = (Button) containerButtons.findViewById(android.R.id.button2);
Button button1 = (Button) containerButtons.findViewById(android.R.id.button1);

// 各ボタンの比率を指定する。
((LinearLayout.LayoutParams)button3.getLayoutParams()).weight = 1.0f;
((LinearLayout.LayoutParams)button2.getLayoutParams()).weight = 1.0f;
((LinearLayout.LayoutParams)button1.getLayoutParams()).weight = 1.0f;

// 各ボタンの幅を0にする。
((LinearLayout.LayoutParams)button3.getLayoutParams()).width = 0;
((LinearLayout.LayoutParams)button2.getLayoutParams()).width = 0;
((LinearLayout.LayoutParams)button1.getLayoutParams()).width = 0;

device-2016-12-12-104514.png


Layoutでハック

appcompat ライブラリで使用されているリソースを上書きすることで、ダイアログのデザインを変更することも可能です。

sdk/extras/android/support/v7/appcompat/res/layout/abc_alert_dialog_material.xml - android_tools - Git at Google

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/topPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/title_template"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingRight="?attr/dialogPreferredPadding"
android:paddingTop="@dimen/abc_dialog_padding_top_material">
<ImageView
android:id="@android:id/icon"
android:layout_width="32dip"
android:layout_height="32dip"
android:scaleType="fitCenter"
android:src="@null"
style="@style/RtlOverlay.Widget.AppCompat.DialogTitle.Icon"/>
<android.support.v7.widget.DialogTitle
android:id="@+id/alertTitle"
style="?attr/android:windowTitleStyle"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart" />
</LinearLayout>
<!-- If the client uses a customTitle, it will be added here. -->
</LinearLayout>
<FrameLayout
android:id="@+id/contentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="48dp">
<View android:id="@+id/scrollIndicatorUp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="top"
android:background="?attr/colorControlHighlight"/>
<android.support.v4.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@android:id/message"
style="@style/TextAppearance.AppCompat.Subhead"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="?attr/dialogPreferredPadding"
android:paddingTop="@dimen/abc_dialog_padding_top_material"
android:paddingRight="?attr/dialogPreferredPadding"/>
<View
android:id="@+id/textSpacerNoButtons"
android:visibility="gone"
android:layout_width="0dp"
android:layout_height="@dimen/abc_dialog_padding_top_material"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<View android:id="@+id/scrollIndicatorDown"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="?attr/colorControlHighlight"/>
</FrameLayout>
<FrameLayout
android:id="@+id/customPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="48dp">
<FrameLayout
android:id="@+id/custom"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</FrameLayout>
<include layout="@layout/abc_alert_dialog_button_bar_material" />
</LinearLayout>

sdk/extras/android/support/v7/appcompat/res/layout/abc_alert_dialog_button_bar_material.xml - android_tools - Git at Google


layout/abc_alert_dialog_button_bar_material.xml

<?xml version="1.0" encoding="utf-8"?>

<android.support.v7.widget.ButtonBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="locale"
android:orientation="horizontal"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:gravity="bottom"
app:allowStacking="@bool/abc_allow_stacked_button_bar"
style="?attr/buttonBarStyle">
<Button
android:id="@android:id/button3"
style="?attr/buttonBarNeutralButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<android.support.v4.widget.Space
android:id="@+id/spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
android:visibility="invisible" />
<Button
android:id="@android:id/button2"
style="?attr/buttonBarNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@android:id/button1"
style="?attr/buttonBarPositiveButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</android.support.v7.widget.ButtonBarLayout>

<android.support.v4.widget.Space android:id="@+id/spacer"/> は必須です。削除するとエラーが発生します。

appcompat ライブラリで使用されているリソースを追うことで、何処でどのstyleや属性が使用しているか確認できますので、

ターゲットユーザーにとって使いやすいデザインにハックしてみたいと思います。


参考