Android对话框(Dialog)完全使用手册

Dialog对话框是android常用的基础视图组件之一,本文总结了对话框常用的几种样式,以及自定义视图和带动画效果的对话框

Dialog虽然可以显示到屏幕上,但是Dialog并非继承自View,而是继承自Object。Dialog的生命周期由Activity来控制,所以当Activity被销毁后,如果再有对Dialog的操作会导致异常:java.lang.IllegalArgumentException: View not attached to window manager。
当Dialog显示的时候,下面的Activity会失去焦点,用户的注意力将全部集中在Dialog上,进行操作;当Dialog消失后,Activity将重新获得焦点。

1 常用样式对话框

1.1 双按钮对话框

先上图

双按钮对话框

再上代码,最后解释

final AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this) {
    @Override
    public AlertDialog create() {
        d("对话框create,创建时调用");
        return super.create();
    }

    @Override
    public AlertDialog show() {
        d("对话框show,显示时调用");
        return super.show();
    }
};

dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        d("对话框取消");
    }
});

dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
    public void onDismiss(DialogInterface dialog) {
        d("对话框销毁");
    }
});

dialog.setIcon(R.mipmap.ic_launcher)
        .setTitle("我是标题")
        .setMessage("我是要显示的消息")
        .setCancelable(true)
        .setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        d("点击确定");
                    }
                })
        .setNegativeButton("取消",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        d("点击取消");
                    }
                });
dialog.show();

创建对话框采用了建造者模式,通过构建AlertDialog.Builder来进行参数设置,最后通过show函数显示对话框。
初始化AlertDialog.Builder的时候,可以重写create和show函数,在创建和显示的时候做一些额外工作。

setOnCancelListener:设置对话框取消时的回调函数
setOnDismissListener:设置对话框消失时的回调函数
setIcon:设置对话框的图标
setTitle:设置对话框标题
setMessage:设置对话框消息
setCancelable:设置对话框是否可以被取消,如果是true,则点击非对话框区域,对话框消失;false则刚好相反
setPositiveButton:设置确定按钮的文字和回调函数
setNegativeButton:设置取消按钮的文字和回调函数
show:显示对话框

这里要注意一点,对话框的cancel和dismiss两个函数的区别
查看源码可以发现

public void cancel() {
    if (mCancelMessage != null) {
        // Obtain a new message so this dialog can be re-used
        Message.obtain(mCancelMessage).sendToTarget();
    }
    dismiss();
}

public void setOnCancelListener(final OnCancelListener listener) {
    if (listener != null) {
        mCancelMessage = mListenersHandler.obtainMessage(CANCEL, listener);
    } else {
        mCancelMessage = null;
    }
}

如果没有调用setOnCancelListener,那么cancel和dismiss两个函数功能是一样的;当调用了setOnCancelListener时,会在执行cancel函数时向观察者发出一个消息,仅此而已。

1.2 三按钮对话框

三按钮对话框
final AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setIcon(R.mipmap.ic_launcher)
        .setTitle("我是标题")
        .setMessage("我是要显示的消息")
        .setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        d("确定");
                    }
                })
        .setNeutralButton("说明",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        d("说明");
                    }
                })
        .setNegativeButton("取消",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        d("取消");
                    }
                });
dialog.show();

与双按钮对话框相比,三按钮对话框多了作为说明操作的按钮,当对话框的message无法显示全部内容是,通过第三个按钮将用户引导到新的界面,显示长篇幅说明文字。

1.3 列表对话框

列表对话框
final String[] items = {"项目1", "项目2", "项目3", "项目4"};
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("我是标题")
        .setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                d("选择: " + items[which]);
            }
        });
dialog.show();

列表对话框,用于显示一个列表并进行单选。
这里主要通过setItems函数为对话框配置要显示的列表和选择时的回调函数,选择后对话框自动消失。
请注意回调函数的参数which从0开始。

1.4 单选对话框

单选对话框
int select = 1; //表示单选对话框初始时选中哪一项
    
final String[] items = {"项目1", "项目2", "项目3", "项目4"};
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("我是标题")
        .setSingleChoiceItems(items, select,
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        select = which;
                        d("选择: " + items[select]);
                    }
                })
        .setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (select != -1) {
                            d("确定: " + items[select]);
                        }
                    }
                });
dialog.show();

单选对话框,与列表对话框功能相似,主要区别是选择后对话框是否消失。单选对话框可以反复选择,直到用户点击确定按钮。
setSingleChoiceItems函数的第一个参数是要显示的列表文本,第二个参数表示默认选中哪一项,下标从0开始。

1.5 多选对话框

多选对话框
private ArrayList<Integer> list = new ArrayList<>();

final String[] items = {"项目1", "项目2", "项目3", "项目4"};
final boolean selected[] = {false, true, false, false};
list.clear();
for (int i = 0, size = selected.length; i < size; ++i) {
    if (selected[i]) {
        list.add(i);
    }
}
AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("我是标题")
        .setMultiChoiceItems(items, selected,
                new DialogInterface.OnMultiChoiceClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
                        if (isChecked) {
                            list.add(which);
                        } else {
                            list.remove(Integer.valueOf(which));
                        }
                    }
                })
        .setPositiveButton("确定",
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (list.size() == 0) {
                            d("你什么都没选啊,小伙");
                        } else {
                            StringBuilder str = new StringBuilder();
                            for (int i = 0, size = list.size(); i < size; i++) {
                                str.append(items[list.get(i)]);
                                if (i < size - 1) {
                                    str.append(", ");
                                }
                            }
                            d("你选中了: " + str.toString());
                        }
                    }
                });
dialog.show();

setMultiChoiceItems函数
参数1:设置要显示的文本列表items
参数2:和文本数据个数一致的boolean数组selected,selected[i]表示items[i]是否是选中状态
参数3:选项被点击时的回调函数,isChecked为true表示选中;false表示取消

1.6 等待对话框

等待对话框
ProgressDialog dialog = new ProgressDialog(MainActivity.this);
dialog.setTitle("我是标题");
dialog.setMessage("等待中...  想关闭请杀掉app");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();

这里使用了ProgressDialog,由于setCancelable设置为false,对话框无法取消,所以此时只能杀死app!
setIndeterminate参数为true表示该进度条不能明确等待进度,仅仅告知用户需要等待,没有预期。

1.7 进度对话框

进度对话框
int progress = 0;

final int MAX_PROGRESS = 100;
final ProgressDialog dialog = new ProgressDialog(MainActivity.this);
dialog.setTitle("我是标题");
dialog.setProgress(0);
dialog.setMax(MAX_PROGRESS);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        mHandler.removeCallbacksAndMessages(null);
        d("进度被打断");
    }
});
dialog.show();

progress = 0;
mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        d("" + progress);
        progress++;
        dialog.setProgress(progress);
        if (progress == 100) {
            dialog.cancel();
        } else {
            mHandler.postDelayed(this, 100);
        }
    }
}, 100);

同样使用ProgressDialog表示一个有进度概念的等待对话框,给用户一个心理预期。
setProgress函数:设置当前进度值
setMax函数:设置最大进度值
setProgressStyle函数:设置进度条类型

1.8 日期选择对话框

日期选择对话框
Calendar c = Calendar.getInstance();
DatePickerDialog dialog = new  DatePickerDialog(MainActivity.this,
        new DatePickerDialog.OnDateSetListener() {
            @Override
            public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                d("选择日期:" + year + "年" + (monthOfYear+1) + "月" + dayOfMonth + "日");
            }
        }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
dialog.show();

注意,当选择成功回调的时候,月份是从0开始的,所以要注意加一。

1.9 时间选择对话框

时间选择对话框
Calendar c = Calendar.getInstance();
TimePickerDialog dialog = new TimePickerDialog(MainActivity.this,
        new TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                d("选择时间:" + hourOfDay + "时" + minute + "分");
            }
        }, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true);
dialog.show();

构造参数最后一个参数true表示采用24小时制

2 自定义对话框

2.1 自定义UI对话框

自定义UI对话框
public class CustomDialog1 extends Dialog {

    private Context context;

    public CustomDialog1(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        this.context = context;
        build();
    }

    private void build() {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.custom_dialog1_layout, null);

        view.findViewById(R.id.like).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "喜欢", Toast.LENGTH_SHORT).show();
                dismiss();
            }
        });

        view.findViewById(R.id.dislike).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(context, "一般", Toast.LENGTH_SHORT).show();
                dismiss();
            }
        });

        setContentView(view);
    }
}

先通过inflate函数解析布局文件,然后通过setContentView为对话框设置视图

2.2 自定义尺寸位置对话框

自定义尺寸位置对话框
public class CustomDialog2 extends Dialog {

    private Context context;

    public CustomDialog2(Context context) {
        super(context);
//        super(context, R.style.custom_dialog2_style);
        init(context);
    }

    public void init(Context context) {
        this.context = context;
        setContentView(R.layout.custom_dialog2_layout);

        Window window = getWindow();
        window.setBackgroundDrawableResource(R.drawable.custom_dialog1_bg);//设置window的背景色
        WindowManager.LayoutParams lp = window.getAttributes();
        window.setGravity(Gravity.BOTTOM);

        lp.x = 250;
        lp.y = 250;
        lp.width = 400;
        lp.height = 400;
        lp.alpha = 0.8f;
        window.setAttributes(lp);

        setCanceledOnTouchOutside(true);//设置点击Dialog外部任意区域关闭Dialog
    }
}

custom_dialog2_layout布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="80dp"
    android:layout_height="80dp"
    android:background="@android:color/holo_red_dark"
    >

</FrameLayout>

对话框的gravity是通过window的setGravity来实现贴边功能的。

对话框窗口大小是通过WindowManager.LayoutParams来进行调整的:
x:负数表示向左移动,正数表示向右移动
y:负数表示向下移动,正数表示向上移动
width:表示对话框的宽
height:表示对话框的高
alpha:表示对话框的透明度

上面显示了这样一个对话框:
1.对话框尺寸400400
2.对话框居中贴底边
3.右移250,上移250
4.内部有一个80dp
80dp的红色视图

2.3 动画对话框

动画对话框
public class AnimDialog1 extends Dialog {

    private Context context;

    public AnimDialog1(Context context) {
        super(context);
        init(context);
    }

    private void init(Context context) {
        this.context = context;
        build();
    }

    private void build() {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.custom_dialog1_layout, null);

        view.findViewById(R.id.like).setVisibility(View.GONE);
        view.findViewById(R.id.dislike).setVisibility(View.GONE);

        setContentView(view);

        Window window = getWindow();
        window.setWindowAnimations(R.style.dialog1_window_anim);
    }
}

对话框的出现消失动画是通过window的setWindowAnimations函数来设置的,这里并不是设置一个anim,而是设置了一个

<style name="dialog1_window_anim" parent="android:Animation" >
    <item name="android:windowEnterAnimation">@anim/dialog1_enter_anim</item>
    <item name="android:windowExitAnimation">@anim/dialog1_exit_anim</item>
</style>

这里可以清楚的看到,可以为window设置进入和退出动画

部分手机如果没有动画效果,请在Manifest.xml的application下的android:theme的style定义当中加入 <item name="android:windowNoTitle">true</item>

3 其他

3.1 对话框常用样式

<item name="android:windowFrame">@null</item><!--是否有边框-->
<item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
<item name="android:windowIsTranslucent">false</item><!--是佛半透明-->
<item name="android:windowNoTitle">true</item><!--有无标题-->
<item name="android:windowBackground">@android:color/holo_green_light</item><!--背景颜色-->
<item name="android:backgroundDimEnabled">false</item><!--是否充许对话框的背景变暗-->
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容