AlertDialog自定义
## 简介
AlertDialog是每一个App所必须的控件,在Android4.0之前不可否认官方正版的Dialog很不美观,但是Android4.0之后的holo风格的Dialog已经可以上的了厅堂了,再后来的Material Design设计风格的拟物扁平化Dialog已经是给人耳目一新的感觉。虽然官方UI标准愈加完善,可是部分UI设计师依然是按照苹果iOS的规范设计App的UI,完全忽略不同平台UI包括交互上的差异性,作为开发者也只好苦逼的遵循UI设计的规范,结果就是Android版本的Dialog多数情况下是以自定义UI风格的方式呈现。系统官方的AlertDialog使用了建造者模式,并且可以链式调用相当方便,所以这次从系统中剥离了AlertDialog源码并进行部分改动以便适用于不同风格。
自定义AlertDialog支持如下方式:
- 系统AlertDialog怎么使用该AlertDialog就怎么使用;
- 支持自定义样式和主题;
- 支持自定义AlertDialog布局文件;
- 支持自定义单选复选列表弹框;
- 支持功能扩展。
## 一些常用的方法
以下是一些常用的方法:
```java
Builder ()
构造方法,默认一个参数的是Theme.Holo.Light主题,可以说设置两个参数,第二个参数可以传入主题
setTitle ()
设置标题栏文本;
setIcon ()
设置标题栏icon;
setIconAttribute ()
设置特定主题下的icon;
setCustomTitle ()
设置自定义标题栏;
setMessage ()
设置弹框内容区域文本;
setItems ()
设置弹框列表;
setSingleChoiceItems ()
设置单选模式列表;
setMultiChoiceItems ()
设置复选模式列表;
setNegativeButton()
设置左侧按钮;
setNeutralButton ()
设置中间按钮;
setPositiveButton ()
设置右侧按钮;
setView ()
设置自定义内容区域View;
```
AlertDialog的基本使用
创建一个标准AlertDialog:
```java
new Builder(this)
.setTitle("title")
.setMessage("hello world")
.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).setNeutralButton("中间", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
```
创建一个无标题栏AlertDialog:
只需要将上述标准Dialog中setTitle方法剔除就可以了。示例代码如下:
```java
new Builder(this)
.setMessage("hello world")
.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).setNeutralButton("中间", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
```
```java
protected AlertDialog(Context context) {
this(context, 0);
}
protected AlertDialog(Context context, int theme) {
super(context, resolveDialogTheme(context, theme));
mAlert = new AlertController(context, this, getWindow());
}
static int resolveDialogTheme(Context context, int resId) {
if (resId == 0) {
return R.style.DialogHoloLight;
} else {
return resId;
}
}
```
首先创建一个深色主题继承一个默认系统已有的深色Dialog主题。代码如下:
```xml
```
这里我们将dialog的布局文件也重新设置了一个,并且设置了该主题下标题栏icon。
要创建一个类似iOS风格的AlertDialog,首先需要创建一个自定义的Builder类,然后设置相应的样式和属性。以下是一个简单的示例:
1. 创建一个名为`AlertDialogBuilder`的自定义Builder类,继承自`AlertDialog.Builder`,并设置主题为iOS风格:
```java
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
public class AlertDialogBuilder extends AlertDialog.Builder {
private Context context;
private int themeResId;
public AlertDialogBuilder(@NonNull Context context) {
super(context);
this.context = context;
this.themeResId = R.style.AlertDialogDark; // 这里设置你的iOS风格主题资源ID
}
@Override
public void setTitle(int titleResId) {
super.setTitle(titleResId);
}
@Override
public void setMessage(CharSequence message) {
super.setMessage(message);
}
@Override
public AlertDialog build() {
AlertDialog alertDialog = super.build();
alertDialog.getWindow().setBackgroundDrawableResource(R.drawable.dialog_background); // 这里设置你的背景图片资源ID
return alertDialog;
}
}
```
2. 在你的Activity或Fragment中使用`AlertDialogBuilder`创建一个类似iOS风格的AlertDialog:
```java
private void showAlertDialog() {
Context ctx = this; // 如果你在Fragment中使用,请替换为getContext()方法返回的对象
AlertDialogBuilder builder = new AlertDialogBuilder(ctx);
builder.setTitle("标题")
.setMessage("这是一个类似iOS风格的AlertDialog")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setNeutralButton("中间", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
AlertDialog alertDialog = builder.build(); // 这将应用你设置的主题和其他属性到新创建的AlertDialog上
alertDialog.show(); // 最后显示这个AlertDialog
}
```
注意:这个示例仅仅是一个基本的实现,你可能需要根据你的需求进行更多的定制,例如添加圆角布局、从底部滑入动画等。
```xml
```
AlertDialog的创建方式跟上面深色主题类似,只需要在构造方法传入主题即可:
```java
new Builder(this, R.style.DialogIOS) ...
```
如果创建类似QQ从底部滑入动画,只需要在主题中设置一个转场动画:
```xml
```
创建一个列表AlertDialog:
```java
List
items.add("Item 1");
items.add("Item 2");
items.add("Item 3");
new AlertDialog.Builder(this, R.style.DialogIOS)
.setTitle("选择一个项目")
.setItems(items.toArray(new String[0]), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Handle click on an item
}
})
.setPositiveButton("确定", null)
.show();
```
创建列表对话框时,包括单选和多选模式实际上都是单行文本模式列表。如果想要支持更复杂列表,可以更改源码或者传入一个自定义View。以下是创建列表对话框的示例代码:
```java
new Builder(this)
.setTitle("title")
.setIconAttribute(android.R.attr.alertDialogIcon)
.setItems(getData(), new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int position) {
dialog.dismiss();
}
})
.show();
```
要创建单复选列表AlertDialog,首先需要使用自定义布局方式来更改系统内置默认的布局。这里我们使用了CheckedTextView控件,因为其checkMark属性可以用来设置单复选图标。接下来定义一个选择器btn_radio_right,如下所示:
```xml
```
最后,定义一个单选模式的布局文件custom_dialog_singlechoice.xml:
```xml
android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> android:id="@+id/checkbox" android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_radio_button_off" /> android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="选项内容" />
```
复选模式的布局在实现上与单选模式类似,主要区别在于添加了一个复选框(checkbox)来表示选中状态。以下是一个简单的复选模式布局实现示例:
```xml
android:layout_width="match_parent" android:layout_height="wrap_content"> android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeightSmall" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorAlertDialogListItem" android:gravity="center_vertical" android:paddingStart="16dip" android:paddingEnd="16dip" android:checkMark="@drawable/btn_radio_light" android:ellipsize="marquee" /> android:id="@+id/checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_marginRight="16dp" />
```
在这个示例中,我们使用`RelativeLayout`作为根布局,然后添加一个`CheckedTextView`作为文本项和一个`CheckBox`作为复选框。通过调整布局参数,可以实现复选模式的布局效果。
设置自定义AlertDialog的代码如下:
```java
// 单选模式
new Builder(this)
.setTitle("标题")
.setIconAttribute(android.R.attr.alertDialogIcon)
.setSingleChoiceItems(getData(), 0, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int position) {
dialog.dismiss();
}
})
.show();
// 复选模式
new Builder(this)
.setTitle("标题")
.setIconAttribute(android.R.attr.alertDialogIcon)
.setMultiChoiceItems(getData(), null, new OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int position, boolean isChecked) {
}
})
.show();
```
自定义View很简单,就是使用setView方法,传入一个自定义的View。在开发中,自定义View的使用频率还是很高的。例如:
```java
new Builder(this)
.setTitle("标题")
.setIconAttribute(android.R.attr.alertDialogIcon)
.setView(View.inflate(this, R.layout.custom_layout, null))
.show();
```
关于AlertDialog的销毁以及事件的监听,可以通过以下方式实现:
1. setCancelable(boolean cancelable):cancelable默认值是true,这时候点击弹框外部区域或者手机返回键,Dialog默认会销毁。反之,点击后Dialog不会销毁。如果在Dialog销毁时还想处理一些逻辑,就需要借助DialogInterface中的两个监听器:`onDismiss(DialogInterface dialog)` 和 `onCancel(DialogInterface dialog)`。例如:
```java
AlertDialog alertDialog = builder.create();
alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
// 这里处理对话框消失时的逻辑
}
});
alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
// 这里处理对话框取消时的逻辑
}
});
alertDialog.show();
```
```java
public void cancel() { if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}
```
从这里可以看出,如果我们设置了setOnDismissListener,调用cancel和dismiss方法都会触发该监听器。但是如果设置setOnCancelListener,则只会在使用cancel方法时触发。差别就在这里。
当一个Dialog被销毁时,OnDismissListener将仍然被通知。但如果你希望在对话框被显示销毁(而不是正常解除)时被通知,则你应该使用setOnCancelListener()注册一个DialogInterface.OnCancelListener。
AlertDialog默认是不全屏的,如果想设置全屏可以使用如下方式:
```java
// 设置布局
View view = View.inflate(this, R.layout.layout_custom01, null);
view.setMinimumWidth(screenWidth);
// 创建AlertDialog
AlertDialog.Builder builder = new Builder(this)
.setTitle("title")
.setView(view);
// 获取AlertDialog实例
AlertDialog dialog = builder.create();
Window dialogWindow = dialog.getWindow();
// 设置屏幕底部居左对齐
dialogWindow.setGravity(Gravity.LEFT | Gravity.BOTTOM);
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.x = 0;
lp.y = 0;
dialogWindow.setAttributes(lp);
dialog.show();
```
以下是重构后的内容,保持了原有段落结构:
## AlertDialog简单使用基本介绍
AlertDialog的代码实现机制在以后的文章中将会有详细的解析。官方并不推荐开发者直接使用Dialog来创建弹出框,因此提供了AlertDialog。然而,从Android 3.0开始,官方又提供了一个新的类DialogFragment,宣称它拥有比AlertDialog更优的用户体验,因为它可以与Fragment的生命周期进行绑定。有关DialogFragment的使用,后续的文章中也会做简单的介绍。个人认为,在简单的实用场景下,既然AlertDialog已经能够满足开发需求,而并非特殊需求的情况下,我仍然选择使用AlertDialog。
在开发过程中,有时我们会遇到一些从底部向上滑动进入的弹框,比如购物车页面或者支付宝支付确认信息界面等。在这种情况下,如果想要自定义动画效果以提升用户体验,可以在AlertDialog中实现这一功能。不过,由于这种交互方式非常常见,Google官方针对此问题也提供了一个非常强大的控件:BottomSheetDialog。该控件最早是在support v23.2支持包中引入的。此外,还有与Fragment绑定的工具BottomSheetDialogFragment。总的来说,BottomSheetDialog可以说是弥补了SlidingDrawer、DrawerLayout、PopupWindow以及AlertDialog在底部弹框交互上的不足。在这里我只想说一个词:“shit”(糟糕)。为什么这些控件不能早一点出现呢?