AlterDialog详解
AlertDialog
在实际开发过程中经常用到,并且根据场景不同,设计的提示也大相径庭,前几天我遇到一个需求:弹出提示的同时播放铃声,本来以为很简单,过程跌宕起伏让人抓狂!在这本该陪女盆友的一天,我却在写博客(说的我好像有女盆友一样!)
1.1 AlertDialog的构造函数
AlertDialog大体上来说有两个重载构造函数
//只需要传入上下文context即可,最后会调用具有默认布局的第二个构造函数。
AlertDialog.Builder alertDialog1 = new AlertDialog.Builder(Context context);
//传入上下文,同时传入自定义的布局
AlertDialog.Builder alertDialog2 = new AlertDialog.Builder(context, R.layout.xxx);
1.2 辅助类函数介绍
builder.setMessage(CharSequence char);
//设置提示信息,传入字符串即可
builder.setIcon(Drawable drawable|int id);
//设置提示的图标,可以传入资源id,也可以传入Drawable对象
builder.setTitle(CharSequence char|int id);
//设置提示的标题,传入字符串或者id
builder.setView();
//设置对话框内容为自定义view,仅设置内容区
builder.setContentView();
//设置对话框为自定义view,设置全部
builder.setInverseBackgroundForced();
//已经被弃用,用来更改默认的背景颜色
builder.setCustomTitle();
//自定义Title,可设置背景颜色
builder.setCancelable(true);
//如果设置为true则是可取消,点击屏幕其他区域,提示框自动消失,若设置false则不可
builder.setAdapter()
//设置对话框内容为自定义列表框
builder.setItems(int id,Listener listener)
//设置对话框为简单列表项,传入一个资源数组id和一个列表监听器
builder.setMultiChoiceItems();
//该方法也是简单列表,传入一个数组和一个boolean类型数组和一个监听器
函数真的好多,为了写博客一一验证,改了很多次描述,现在应该比较贴切了
1.3 AlertDialog的一些问题
AlertDialog.Builder(context).create()的返回值为AlertDialog,但是AlertDialog并不能直接实例化,需要借助AlertDialog.Builder来创建。我们简单的浏览几个重要的源码片段来学习他们的不同。
1.3.1 dialog.show()和builder.show()的区别
public AlertDialog show() {
//在内部创建dialog,最后调用dialog.show(),也就是说builder.show()是调用dialog.show()方法的
final AlertDialog dialog = create();
dialog.show();
return dialog;
}
1.3.2 builder的一系列设置方法是怎么添加到dialog中的?
public static class Builder {
//在builder内部有一个 成员变量P,看名字就知道是“关于显示(dialog)的一些列参数”
//从builder.create()中可以得到验证
@UnsupportedAppUsage
private final AlertController.AlertParams P;
}
public AlertDialog create() {
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
//设置dialog的一些列细节信息,如icon,message,都是在Builder中进行设置的,
//最后调用builder.create()方法,把所有信息传递给dialog
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
1.3.3 AlertDialog的默认布局是如何的?
先用一张图来展示
之前提到setView()
方法可以设置内容区,内容区是在message
下面,Buttton
上面的一块区域。而setContentView()
则可以设置整块区域。可以通过设置style
修改默认的AlertDialog
的背景。setCustomTitle()
其实就是修改setTitle
这块的内容,可以传入TextView
,并设置TextView的颜色,这样就可以改变默认背景的颜色。
1.3.4 unable to add window的异常
解决方案:查看传入的Context是否正确。
2.1 消息提示型
简单描述:消息提示类没有复杂的操作,可能只有一个“我知道了”
的按钮,是最简单的一种提示。但是默认的提示太丑了,先贴出默认提示,最后再自定义一个消息提示。
AlertDialog.Builder builder =createMessageDialog(v.getContext());
builder.setIcon(R.drawable.photo);
builder.setMessage("明天上午10点,xxx约您在中南海见面");
builder.setTitle("重要通知");
builder.setPositiveButton("我知道了", new AlertDialog.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "您已接受邀请,请准时赴约", Toast.LENGTH_SHORT).show();
//这里可以做一些其他操作
}
});
builder.show();
效果图如下:
这个提示的代码很简单,下面贴一个自定义的消息提示,时间匆忙,没有做的很好看。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
android:background="#888888"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:gravity="center_horizontal"
android:text="重要通知"
android:textColor="#EEEEEE"
android:layout_marginTop="8dp"
android:textSize="22sp"
android:layout_width="match_parent"
android:layout_height="40dp">
</TextView>
<TextView
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:textSize="18sp"
android:textColor="#CCCCCC"
android:text="明天上午10点,xxx约您在见面"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TextView>
<Button
android:gravity="center_horizontal"
android:text="我知道了"
android:textSize="16sp"
android:layout_marginTop="30dp"
android:background="@null"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
AlertDialog.Builder builder =createMessageDialog(v.getContext());
builder.setView(R.layout.layout_alert);
builder.show();
//三行代码加布局搞定一切
效果图展示:
如果需要添加Button的点击,可以通过android:onClick="xxx"
;并在代码中声明该方法即可。
2.2 逻辑操作型
逻辑操作类型,其实就是底部多了很多按钮,可以进行不同的操作,代码如下:
AlertDialog.Builder builder =createMessageDialog(v.getContext());
View view = getLayoutInflater().inflate(R.layout.layout_alert, null);
builder.setView(view);
TextView textView = new TextView(v.getContext());
textView.setText("Android");
builder.setCancelable(true);
textView.setBackgroundColor(getResources().getColor(R.color.colorAccent));
builder.setCustomTitle(textView);
builder.setNegativeButton("取消", new AlertDialog.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "您点击了取消", Toast.LENGTH_SHORT).show();
}
});
builder.setPositiveButton("确定", new AlertDialog.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "您点击了确定", Toast.LENGTH_SHORT).show();
}
});
builder.setNeutralButton("中立的", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "您点击了中立", Toast.LENGTH_SHORT).show();
}
});
builder.setView(R.layout.layout_alert);
builder.show();
效果图如下:
这里同样也可以自定义,只需要和之前那样,声明按钮的点击方法即可,就不再演示。
2.3 列表选择型
builder.setTitle("选择你最喜爱的城市");
builder.setMultiChoiceItems(province, new boolean[]{true, true, true},
new OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
switch (which) {
case 0:
Toast.makeText(getApplicationContext(), "您选择了A", Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(getApplicationContext(), "您选择了B", Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(getApplicationContext(), "您选择了c", Toast.LENGTH_SHORT).show();
break;
}
}
});
builder.show();
效果展示:
这里做一个仿网易云播放列表的案例
builder2.setIcon(R.drawable.play);
TextView textView1 = new TextView(getApplicationContext());
textView1.setText("播放列表");
textView1.setTextSize(22);
builder2.setCustomTitle(textView1);
//顶部已经设置好
String[] songs = new String[]{"演员","聊表心意","花儿与少年","天后","狐狸","七里香","一路向北"};
String[] singers = new String[]{"薛之谦","薛之谦","薛之谦","陈势安","薛之谦","周杰伦","周杰伦"};
int[] photo = new int[]{R.drawable.xue,R.drawable.xue,R.drawable.xue,R.drawable.chen,R.drawable.xue,R.drawable.zhou,R.drawable.zhou};
View view1 = getLayoutInflater().inflate(R.layout.layout_alert, null);
RecyclerView recyclerView = view1.findViewById(R.id.recyclerview);
SongAdapter adapter = new SongAdapter(photo, songs, singers, getApplicationContext());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
builder2.setView(view1);
builder2.show();
layout_alert
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
song_item
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp">
<ImageView
android:layout_margin="6dp"
android:id="@+id/singer_photo"
android:src="@drawable/xue"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="fitXY">
</ImageView>
<LinearLayout
android:id="@+id/song"
android:layout_toRightOf="@id/singer_photo"
android:orientation="vertical"
android:layout_marginLeft="16dp"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<TextView
android:id="@+id/songs"
android:textSize="20sp"
android:text="认真的雪"
android:textColor="#000000"
android:layout_marginTop="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
<TextView
android:id="@+id/singer"
android:textSize="16sp"
android:text="薛之谦"
android:textColor="#555555"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</TextView>
</LinearLayout>
<ImageView
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_width="32dp"
android:layout_height="32dp"
android:scaleType="center"
android:layout_marginRight="42dp"
android:src="@drawable/start"></ImageView>
<ImageView
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_width="32dp"
android:layout_height="32dp"
android:scaleType="center"
android:src="@drawable/more"></ImageView>
</RelativeLayout>
SongAdapter
class SongAdapter extends RecyclerView.Adapter<ViewHolder>{
private int[] photos;
private String[] songs;
private String[] singers;
private Context context;
public SongAdapter(int[] photos,String[] songs,String[] singers,Context context){
this.photos=photos;
this.songs=songs;
this.singers=singers;
this.context=context;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.song_item,null);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.imageView.setImageDrawable(getResources().getDrawable(photos[position]));
holder.songs.setText(songs[position]);
holder.singer.setText(singers[position]);
}
@Override
public int getItemCount() {
return photos.length;
}
}
class ViewHolder extends RecyclerView.ViewHolder{
ImageView imageView;
TextView songs;
TextView singer;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.singer_photo);
songs = itemView.findViewById(R.id.songs);
singer = itemView.findViewById(R.id.singer);
}
}
效果图,差的不是一点半点哈哈哈哈哈
2.4 自定义提示
自定义提示其实上面已经用到过了,比如自定义的消息提示,自定义的网易云播放列表等,最终要的是思想,我们可以把自定义的布局当成Activity的布局去设计他,大部分情况下,都是可用的。
提一个小案例:淘宝软件上,我们在点击查看已发货时,会弹出一个提示,类似于ViewPager+Fragment
结合设计的功能更加复杂的AlertDialog
自定义提示的步骤如下:
第一步:根据需求,自定义Layout布局
第二步:根据逻辑,将Layout布局转换成view,初始化布局中需要改变的控件。
View view1 = getLayoutInflater().inflate(R.layout.layout_alert, null);
RecyclerView recyclerView = view1.findViewById(R.id.recyclerview);
第三步:添加事件逻辑,如点击,选择,改变UI等。
2.5 封装提示类
提示在应用中比较常用,建议把AlertDialog
封装起来,使用更方便。下面封装一个自定义类型的AlertDialog
public static AlertDialog.Builder
createMessageDialog(Context context,String title,String message, int photoId, int layoutId,boolean setview){
AlertDialog.Builder builder = new AlertDialog.Builder(context);
if(title!=null){
builder.setTitle(title);
}
if(message!=null){
builder.setMessage(message);
}
try{
Drawable drawable = context.getResources().getDrawable(photoId);
builder.setIcon(drawable);
}catch (NullPointerException e){
//do something
}
View view = LayoutInflater.from(context).inflate(layoutId, null);
if(setview){
//设置内容布局
builder.setView(view);
}else{
//设置setContentView,要找builder.show()以后调用
}
return builder;
}
3.1 设置AlertDialog入场出场动画
先上效果图:
这个效果非常简单,就是通过设置动画进行实现的,请看下列实现代码
//this关键字很重要,使用getApplicationContext会出错。
AlertDialog.Builder builder =new AlertDialog.Builder(this);
builder.setIcon(R.drawable.photo);
builder.setMessage("明天上午10点,XXX约您在见面");
builder.setTitle("重要通知");
builder.setPositiveButton("我知道了", new AlertDialog.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "您点击了确定", Toast.LENGTH_SHORT).show();
}
});
Dialog dialog = builder.create();
//在这里指定动画
dialog.getWindow().getAttributes().windowAnimations = R.style.dialogWindowAnim;
dialog.show();
style.xml
<style name="dialogWindowAnim" parent="android:Animation" mce_bogus="1">
<item name="android:windowEnterAnimation">@anim/dialog_enter_anim</item>
<item name="android:windowExitAnimation">@anim/dialog_exit_anim</item>
</style>
两个简单的动画
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="1000" >
</translate>
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="100"
android:fromXDelta="0"
android:fromYDelta="1000"
android:toXDelta="0"
android:toYDelta="0" >
</translate>
3.2 待补充
四 .小结这篇博客耗时两天,总结了AlertDialog的大部分问题,时间有限,如有错误,请各位前辈指出,感谢各位看官。