答: 通过设置 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