Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
78
Help us understand the problem. What is going on with this article?
@granoeste

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

More than 3 years have 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や属性が使用しているか確認できますので、
ターゲットユーザーにとって使いやすいデザインにハックしてみたいと思います。

参考

78
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
granoeste
Android and Flutter Developer
dena_coltd
    Delight and Impact the World

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
78
Help us understand the problem. What is going on with this article?