DevWiki

如何自定义 AlertDialog 的样式?
答: 通过设置 Theme 的 alertDialogTheme.1. 分析源码首先, 我们看下如何新建 Aler...
扫描右侧二维码阅读全文
29
2018/10

如何自定义 AlertDialog 的样式?

答: 通过设置 Theme 的 alertDialogTheme.

1. 分析源码

首先, 我们看下如何新建 AlertDialog, 通常代码如下:

private void showDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Title")
            .setMessage("Message")
            .setPositiveButton("OK", (dialog, which) -> {
                Toast.makeText(this, "OK", Toast.LENGTH_SHORT).show();
            })
            .setNegativeButton("Cancel", (dialog, which) -> {
                Toast.makeText(this, "Cancel", Toast.LENGTH_SHORT).show();
            });
    builder.create().show();
}

使用 Builder 创建一个 AlertDialog, 打开源码我们可以看看 Builder的源码:

public Builder(@NonNull Context context) {
    this(context, resolveDialogTheme(context, 0));
}

static int resolveDialogTheme(@NonNull Context context, @StyleRes int resid) {
    // Check to see if this resourceId has a valid package ID.
    if (((resid >>> 24) & 0x000000ff) >= 0x00000001) {   // start of real resource IDs.
        return resid;
    } else {
        TypedValue outValue = new TypedValue();
        context.getTheme().resolveAttribute(R.attr.alertDialogTheme, outValue, true);
        return outValue.resourceId;
    }
}

从源码中可以看出, 默认使用的是 R.attr.alertDialogTheme.

2. 查看主题

我们新建工程, 可以看到使用的主题如下:

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

这里面没有 alertDialogTheme ,我们往父主题查询:

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


<style name="Base.Theme.AppCompat.Light.DarkActionBar" parent="Base.Theme.AppCompat.Light">
    <item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
    <item name="actionBarWidgetTheme">@null</item>
    <item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>

    <!-- Panel attributes -->
    <item name="listChoiceBackgroundIndicator">@drawable/abc_list_selector_holo_dark</item>

    <item name="colorPrimaryDark">@color/primary_dark_material_dark</item>
    <item name="colorPrimary">@color/primary_material_dark</item>
</style>


<style name="Base.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light">


<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
    <item name="viewInflaterClass">android.support.v7.app.AppCompatViewInflater</item>

    <item name="windowNoTitle">false</item>
    <item name="windowActionBar">true</item>
    
    <!--此处省略部分代码-->

    <!-- Dialog attributes -->
    <item name="dialogTheme">@style/ThemeOverlay.AppCompat.Dialog</item>
    <item name="dialogPreferredPadding">@dimen/abc_dialog_padding_material</item>
    <item name="alertDialogTheme">@style/ThemeOverlay.AppCompat.Dialog.Alert</item>
    <item name="alertDialogStyle">@style/AlertDialog.AppCompat.Light</item>
    <item name="alertDialogCenterButtons">false</item>
    <item name="textColorAlertDialogListItem">@color/abc_primary_text_material_light</item>
    <item name="listDividerAlertDialog">@null</item>

可以看出 AlertDialogTheme 是 ThemeOverlay.AppCompat.Dialog.Alert, 继续查看其源码:

<style name="ThemeOverlay.AppCompat.Dialog.Alert" parent="Base.ThemeOverlay.AppCompat.Dialog.Alert"/>

<style name="Base.ThemeOverlay.AppCompat.Dialog" parent="Base.V7.ThemeOverlay.AppCompat.Dialog"/>
<style name="Base.ThemeOverlay.AppCompat.Dialog.Alert">
    <item name="android:windowMinWidthMajor">@dimen/abc_dialog_min_width_major</item>
    <item name="android:windowMinWidthMinor">@dimen/abc_dialog_min_width_minor</item>
</style>

此处可以看到 AlertDialog 的主题配置, 但是感觉缺少很多, 连 Title 的大小和颜色都没有?

No, No, No!

AlertDialog 的父类 是 Dialog, 那么我们看上面的 Base.V7.ThemeOverlay.AppCompat.Dialog代码:

<style name="Base.V7.ThemeOverlay.AppCompat.Dialog" parent="Base.ThemeOverlay.AppCompat">
    <item name="android:colorBackgroundCacheHint">@null</item>
    <item name="android:colorBackground">?attr/colorBackgroundFloating</item>

    <item name="android:windowFrame">@null</item>
    <item name="android:windowTitleStyle">@style/RtlOverlay.DialogWindowTitle.AppCompat</item>
    <item name="android:windowTitleBackgroundStyle">@style/Base.DialogWindowTitleBackground.AppCompat</item>
    <item name="android:windowBackground">@drawable/abc_dialog_material_background</item>
    <item name="android:windowIsFloating">true</item>
    <item name="android:backgroundDimEnabled">true</item>
    <item name="android:windowContentOverlay">@null</item>
    <item name="android:windowAnimationStyle">@style/Animation.AppCompat.Dialog</item>
    <item name="android:windowSoftInputMode">stateUnspecified|adjustPan</item>

    <item name="windowActionBar">false</item>
    <item name="windowActionModeOverlay">true</item>

    <item name="listPreferredItemPaddingLeft">24dip</item>
    <item name="listPreferredItemPaddingRight">24dip</item>

    <item name="android:listDivider">@null</item>

    <item name="windowFixedWidthMajor">@null</item>
    <item name="windowFixedWidthMinor">@null</item>
    <item name="windowFixedHeightMajor">@null</item>
    <item name="windowFixedHeightMinor">@null</item>

    <item name="android:buttonBarStyle">@style/Widget.AppCompat.ButtonBar.AlertDialog</item>
    <item name="android:borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
    <item name="android:windowCloseOnTouchOutside">true</item>
</style>

这里可以看出关于 windowTitleStyle的配置,如下:

<style name="RtlOverlay.DialogWindowTitle.AppCompat" parent="Base.DialogWindowTitle.AppCompat">
</style>


<style name="Base.DialogWindowTitle.AppCompat" parent="android:Widget">
    <item name="android:maxLines">1</item>
    <item name="android:scrollHorizontally">true</item>
    <item name="android:textAppearance">@style/TextAppearance.AppCompat.Title</item>
</style>

这里定义了 textAppearance, 其代码如下:

<style name="TextAppearance.AppCompat.Title" parent="Base.TextAppearance.AppCompat.Title"/>


<style name="Base.TextAppearance.AppCompat.Title">
    <item name="android:textSize">@dimen/abc_text_size_title_material</item>
    <item name="android:textColor">?android:textColorPrimary</item>
</style>

哈哈, 终于看到了关于标题的配置.
那么我们开始自定义 AlertDialog

3. 自定义 AlertDialog

首先在 styles.xml 文件中添加 alertDialogTheme , 依次设置 windowTitleStyle, textAppearance , 代码如下:

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

        <item name="alertDialogTheme">@style/AppTheme.Dialog.Alert</item>
    </style>

    <style name="AppTheme.Dialog.Alert" parent="ThemeOverlay.AppCompat.Dialog.Alert">
        <item name="android:windowTitleStyle">@style/AppTheme.Dialog.Alert.Window</item>
    </style>

    <style name="AppTheme.Dialog.Alert.Window" parent="RtlOverlay.DialogWindowTitle.AppCompat">
        <item name="android:textAppearance">@style/AppTheme.Dialog.Alert.Title</item>
    </style>

    <style name="AppTheme.Dialog.Alert.Title" parent="TextAppearance.AppCompat.Title">
        <item name="android:textColor">@color/colorPrimaryDark</item>
    </style>

</resources>

这样我们就可以设置 AlertDialog 的 Title 颜色和字号了.

等等, AlertDialog 的 Message 颜色如何设置???

根据 Material Design 的设计规范, AlertDialog 的 Message 属于主要文字, 其颜色配置为:

<item name="android:textColorPrimary">@color/colorPrimaryDark</item>

同理, AlertDialog 的 Button 颜色属于 colorAccent 配置, 修改 alertDialogTheme 如下:

<style name="AppTheme.Dialog.Alert" parent="ThemeOverlay.AppCompat.Dialog.Alert">
    <item name="android:windowTitleStyle">@style/AppTheme.Dialog.Alert.Window</item>
    <item name="colorAccent">@color/colorPrimaryDark</item>
    <item name="android:textColorPrimary">@color/colorPrimaryDark</item>
</style>

可以运行代码了, 运行效果如下:

什么?? 你还想 AlertDialog 每个按钮的颜色都不一样???
没事, Theme 解决不了的事情, Java代码给你解决:

private void showDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Title")
            .setMessage("Message")
            .setPositiveButton("OK", (dialog, which) -> Toast.makeText(this, "OK", Toast.LENGTH_SHORT).show())
            .setNegativeButton("Cancel", (dialog, which) -> Toast.makeText(this, "Cancel", Toast.LENGTH_SHORT).show())
            .setNeutralButton("Emmm", (dialog, which) -> Toast.makeText(this, "Emmm", Toast.LENGTH_SHORT).show());
    AlertDialog dialog = builder.create();
    dialog.show();
    //注意: 此处需要先调用 show()才可以设置颜色, 否则会报空指针异常!
    dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(getResources().getColor(R.color.colorPrimary));
    dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setTextColor(getResources().getColor(R.color.colorPrimaryDark));
    dialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getResources().getColor(R.color.colorAccent));
}

运行结果如下:

源码在我的Github: https://github.com/Dev-Wiki/Theme

Last modification:November 10th, 2018 at 02:38 pm
If you think my article is useful to you, please feel free to appreciate

Leave a Comment