目录
- 为什么需要自定义 Dialog?
- 创建自定义 Dialog 的几种方式
- 通过 XML 布局文件(最常用、最推荐)
- 通过 Java/Kotlin 代码动态创建
- 完全自定义绘制(高级)
- 完整实践:通过 XML 布局创建一个自定义 Dialog
- 步骤 1:创建 Dialog 布局文件 (
custom_dialog.xml) - 步骤 2:在 Activity/Fragment 中创建并显示 Dialog
- 步骤 3:处理 Dialog 内部控件的点击事件
- 步骤 4:设置 Dialog 的样式(可选,如全屏、背景透明等)
- 步骤 1:创建 Dialog 布局文件 (
- 高级技巧与最佳实践
- 使用
DialogFragment(强烈推荐) - 主题样式 (
Theme.AppCompat.Dialog) - 处理屏幕旋转
- 让 Dialog 点击外部不消失
- 让 Dialog 不可取消
- 使用
为什么需要自定义 Dialog?
系统默认的 AlertDialog 虽然功能强大,但样式和布局都非常固定,当你的 App 需要一个与整体设计风格一致、包含复杂布局(如图片、列表、输入框等)的对话框时,就必须使用自定义 Dialog。

创建自定义 Dialog 的几种方式
通过 XML 布局文件(最常用、最推荐)
这是最简单、最直观的方式,你只需要像创建普通的 Activity 布局一样,创建一个 XML 文件,然后在代码中将其“充气”(inflate)到 Dialog 的一个 View 上即可。
优点:
- 布局清晰,易于维护。
- 可以直接在 Android Studio 的布局编辑器中预览和调整。
- 逻辑和视图分离。
通过 Java/Kotlin 代码动态创建
这种方式不使用 XML 文件,而是通过 new View(), new LinearLayout(), new TextView() 等代码来手动构建整个视图树。
优点:

- 适用于需要根据数据动态生成内容的场景。
- 不需要额外的 XML 文件。
缺点:
- 代码冗长,可读性差,维护困难。
- 无法在布局编辑器中预览。
完全自定义绘制(高级)
这种方式继承 Dialog 或 AppCompatDialog,并重写 onDraw() 方法,使用 Canvas 进行绘制,这通常用于创建一些特殊效果的对话框,比如圆形、半透明模糊背景等。
优点:
- 实现极致的 UI 自由度。
缺点:

- 实现复杂,需要较强的绘图功底。
- 性能开销相对较大。
完整实践:通过 XML 布局创建一个自定义 Dialog
我们将创建一个包含标题、内容、两个按钮(“确认”和“取消”)的自定义 Dialog。
步骤 1:创建 Dialog 布局文件 (custom_dialog.xml)
在 res/layout/ 目录下,新建一个布局文件 custom_dialog.xml。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp"
android:background="@drawable/dialog_background"> <!-- 可选:添加圆角背景 -->
<TextView
android:id="@+id/dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="自定义标题"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/dialog_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="这是一个自定义对话框的内容,你可以在这里放任何东西,比如图片、列表、输入框等。"
android:textSize="16sp"
android:gravity="center"
android:layout_marginBottom="24dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/dialog_btn_cancel"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:text="取消"
android:backgroundTint="@android:color/darker_gray"
android:textColor="@android:color/white"
android:layout_marginEnd="8dp" />
<Button
android:id="@+id/dialog_btn_confirm"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
android:text="确认"
android:backgroundTint="@color/colorPrimary"
android:textColor="@android:color/white" />
</LinearLayout>
</LinearLayout>
小提示:添加圆角背景
为了让 Dialog 有圆角效果,可以在 res/drawable 目录下创建一个 dialog_background.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners android:radius="16dp" />
</shape>
然后在 custom_dialog.xml 的根布局 android:background 中引用它。
步骤 2:在 Activity/Fragment 中创建并显示 Dialog
在你的 Activity 或 Fragment 的 Java/Kotlin 代码中,编写如下代码:
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val showDialogButton: Button = findViewById(R.id.show_dialog_button)
showDialogButton.setOnClickListener {
showCustomDialog()
}
}
private fun showCustomDialog() {
// 1. 创建 AlertDialog.Builder 对象
val builder = AlertDialog.Builder(this)
// 2. 设置自定义布局
// inflate 方法需要三个参数:布局文件、ViewGroup 的父视图、是否附加到父视图上
// 第三个参数通常为 false,因为我们不需要将视图附加到父视图上
val view = layoutInflater.inflate(R.layout.custom_dialog, null)
builder.setView(view)
// 3. 获取布局中的控件
val titleTextView: TextView = view.findViewById(R.id.dialog_title)
val messageTextView: TextView = view.findViewById(R.id.dialog_message)
val cancelButton: Button = view.findViewById(R.id.dialog_btn_cancel)
val confirmButton: Button = view.findViewById(R.id.dialog_btn_confirm)
// 4. (可选)为按钮设置点击事件
cancelButton.setOnClickListener {
// 取消按钮的逻辑
// dialog.dismiss() // 关闭对话框
println("取消按钮被点击")
}
confirmButton.setOnClickListener {
// 确认按钮的逻辑
// dialog.dismiss() // 关闭对话框
println("确认按钮被点击")
}
// 5. 创建并显示 Dialog
// builder.create() 会返回一个 AlertDialog 对象
val dialog = builder.create()
// 6. (可选)设置 Dialog 的其他属性
// 让 Dialog 的背景透明,这样我们自定义的圆角背景才能显示出来
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
// 显示 Dialog
dialog.show()
}
}
关键点解释:
AlertDialog.Builder: 这是构建 Dialog 的标准方式,提供了链式调用,非常方便。layoutInflater.inflate(): 这是将 XML 布局文件转换为实际的View对象的核心方法。builder.setView(view): 将我们自定义的View设置为 Dialog 的内容视图。dialog.window?.setBackgroundDrawableResource(...): 这是一个非常重要的技巧,默认情况下,Dialog 的窗口背景是黑色的,会覆盖掉我们自定义的圆角背景,将其设置为透明后,我们布局中的android:background就能正确显示了。
高级技巧与最佳实践
使用 DialogFragment(强烈推荐)
直接使用 AlertDialog 在某些情况下会遇到问题,最常见的就是屏幕旋转,当手机屏幕旋转时,Activity 会重建,此时所有通过 new 创建的 Dialog 都会消失,甚至可能导致内存泄漏。
DialogFragment 是官方推荐的解决方案,它是一个特殊的 Fragment,生命周期与 Activity 绑定,可以完美地处理屏幕旋转等配置变更。
DialogFragment 的基本用法:
- 创建一个类继承
DialogFragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
class MyCustomDialogFragment : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 返回你的自定义布局
return inflater.inflate(R.layout.custom_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val titleTextView: TextView = view.findViewById(R.id.dialog_title)
val cancelButton: Button = view.findViewById(R.id.dialog_btn_cancel)
val confirmButton: Button = view.findViewById(R.id.dialog_btn_confirm)
cancelButton.setOnClickListener {
dismiss() // DialogFragment 使用 dismiss() 来关闭自己
}
confirmButton.setOnClickListener {
// 执行确认逻辑
dismiss()
}
}
// (可选)设置 Dialog 的样式
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
return dialog
}
}
- 在 Activity 中显示
DialogFragment:
// 在 Activity 中 val dialogFragment = MyCustomDialogFragment() // 使用 show() 方法,它会自动处理 Fragment 的管理 dialogFragment.show(supportFragmentManager, "MyCustomDialogTag")
主题样式 (Theme.AppCompat.Dialog)
你可以在 res/values/styles.xml 中定义一个 Dialog 的主题,然后在创建 Dialog 时应用它。
<!-- res/values/styles.xml -->
<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>
</style>
<style name="CustomDialogTheme" parent="Theme.AppCompat.Dialog">
<!-- 设置对话框的背景色为透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<!-- 去除对话框的标题栏 -->
<item name="android:windowNoTitle">true</item>
<!-- 让对话框的边框不可见 -->
<item name="android:windowFrame">@null</item>
</style>
</resources>
然后在代码中应用这个主题:
// 在创建 Dialog.Builder 时传入主题 val builder = AlertDialog.Builder(context, R.style.CustomDialogTheme) // ... 后续代码相同
处理屏幕旋转
使用 DialogFragment 是最佳实践,如果你坚持使用普通 Dialog,可以在 AndroidManifest.xml 中为对应的 Activity 添加 android:configChanges="orientation|screenSize",但这只是一种“规避”策略,而不是“解决”策略,通常不推荐。
让 Dialog 点击外部不消失
默认情况下,点击 Dialog 外部区域,Dialog 会消失,如果需要禁用这个行为:
val dialog = builder.create() // 设置为 false,点击外部不会消失 dialog.setCanceledOnTouchOutside(false) // 或者设置整个 Dialog 都不可取消 // dialog.setCancelable(false) dialog.show()
让 Dialog 不可取消
如果用户必须做出选择才能关闭 Dialog,可以设置为不可取消:
val dialog = builder.create() dialog.setCancelable(false) // 按 Back 键和点击外部都无法关闭 dialog.show()
| 特性 | 普通 AlertDialog | DialogFragment (推荐) |
|---|---|---|
| 生命周期管理 | 弱,与 Activity 解耦,易被销毁 | 强,与 Activity 绑定,自动处理配置变更 |
| 屏幕旋转 | 问题:Dialog 会消失 | 解决:自动重建,状态可保留 |
| 代码组织 | 逻辑和视图在 Activity/Fragment 中 | 逻辑和视图封装在 Fragment 中,更清晰 |
| 复用性 | 较低 | 较高,可在不同地方复用 |
| 显示方式 | dialog.show() |
fragmentManager.show() |
最终建议:
- 对于简单的、一次性的对话框,可以直接使用
AlertDialog.Builder加载 XML 布局。 - 对于任何复杂的、需要复用、或者希望健壮地处理屏幕旋转的对话框,请务必使用
DialogFragment,这是现代安卓开发的标准做法。
