PopupWindow和Dialog

1.What?

​ 我们在开发时常常会碰到这样一些需求:点击某个按钮时弹出一个对话框和用户交互,或者是在某个时刻弹出一个在所有UI顶层之上的悬浮框。怎么实现这种效果呢?

​ Android为我们提供了PopupWindow和Dialog,正如名字所示,它们就是一类在顶层出现的视图空间。

​ 来看看官方对这两个类的说明。

PopupWindow

This class represents a popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current activity.

PopupWindow代表一个可以用于显示任何view的弹出窗口。它是一个出现在当前activity顶层的悬浮的容器。

Dialog

对话框是提示用户作出决定或输入额外信息的小窗口。 对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件。

那么这两者有什么区别呢?

注意Dialog的最后一句话:通常用于需要用户采取行动才能继续执行的模式事件。Dialog是阻塞UI线程的,也就是当弹出一个Dialog后,用户必须先处理了Dialog才能继续做其它的事情,而PopupWindow仅仅是一个view的容器,它不会对当前线程造成影响,它是非阻塞的。

另一个重要的区别是:Dialog有固定的位置,它总在屏幕的中央,而PopupWindow没有这个限制,它可以出现在任何地方。

2.How?

下面让我们来看看如何使用PopupWindow和Dialog。

PopupWindow:

使用PopupWindow主要分为两个步骤:

(1)初始化PopupWindow对象,设置contentView、长、宽、是否可以获取焦点等等参数。

(2)显示PopupWindow。

之所以要分为两个阶段,是因为显示是比较重要的问题,前面我们说了PopupWindow与Dialog的不同,正因为PopupWindow可以显示在任意位置,所以如何显示它是需要考虑的,不同于使用Dialog,我们只需要调用dialog.show()就能解决问题,因为dialog的位置固定,这就是灵活性和复杂性的问题,一个东西越灵活,那么使用起来它就一定越复杂。

首先来看初始化PopupWindow。

创建一个PopupWindow对象,可以在构造函数里传一些初始参数,也可以在创建以后以后手动set。

下面是常用的一些初始化操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
final PopupWindow window = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT,true);
//设置window本身的高度
window.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
//设置window本身的高度
window.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
//设置window里的view
window.setContentView();
//设置window背景图片
window.setBackgroundDrawable();
//设置是否能获取焦点 焦点的作用在于按下返回键是否考虑window
window.setFocusable();
//设置能否触摸,参与事件分发
window.setTouchable();

当设置好了window以后就可以来显示它了。

显示PopupWindow的方法有两个:

p1

(1)showAsDropDown

使用这种显示方式,window将会显示在指定的view的左下方,可以自己传入x、y方向的偏移量。

(2)showAtLocation

使用这种显示方式,window将会显示在指定的view的内部,并且以该view的左上角为原点。

Demo就不贴了,几行代码比较简单。

需要注意的是不能直接在Activity的onCreate方法里直接显示PopupWindow,这样会直接报错!这也很好理解,如果你要在开始就显示一个PopupWindow,那么为什么不直接在原来的View里添加呢?

接下来是Dialog。

使用Dialog相比PopupWindow要简单很多。

复杂的是Dialog的继承关系,Google官方的文档上明确的说了不推荐我们直接使用Dialog,而是应该使用它的子类。

下面来看官方说明:

Dialog 类是对话框的基类,但您应该避免直接实例化 Dialog,而是使用下列子类之一:

  • AlertDialog

    此对话框可显示标题、最多三个按钮、可选择项列表或自定义布局。

  • DatePickerDialogTimePickerDialog

    此对话框带有允许用户选择日期或时间的预定义 UI。这些类定义您的对话框的样式和结构,但您应该将 DialogFragment 用作对话框的容器。DialogFragment 类提供您创建对话框和管理其外观所需的所有控件,而不是调用 Dialog 对象上的方法。

    使用 DialogFragment 管理对话框可确保它能正确处理生命周期事件,如用户按“返回”按钮或旋转屏幕时。 此外,DialogFragment 类还允许您将对话框的 UI 作为嵌入式组件在较大 UI 中重复使用,就像传统 Fragment 一样(例如,当您想让对话框 UI 在大屏幕和小屏幕上具有不同外观时)。

    本指南的后文将描述如何将 DialogFragmentAlertDialog 对象结合使用。 如果您想创建一个日期或时间选取器,应改为阅读选取器指南。注:由于 DialogFragment 类最初是通过 Android 3.0(API 级别 11)添加的,因此本文描述的是如何使用支持库附带的 DialogFragment 类。 通过将该库添加到您的应用,您可以在运行 Android 1.6 或更高版本的设备上使用 DialogFragment 以及各种其他 API。如果您的应用支持的最低版本是 API 级别 11 或更高版本,则可使用 DialogFragment 的框架版本,但请注意,本文中的链接适用于支持库 API。 使用支持库时,请确保您导入的是android.support.v4.app.DialogFragment 类,而不是 android.app.DialogFragment

p2

这个图是我直接从Android Studio中查看类继承关系得到的。官方文档里只提到了其中的几个。

每个类具体的说明这里就不说了,需要时再查。

官方文档里说的DialogFragment其实就是一个内嵌了Dialog的Fragment,它不在这个继承树里,使用这个Fragment的好处时结合生命周期对Dialog进行一些处理。

Dialog的使用非常之简单,下面是一个基本的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Created by zuliangwang on 16/12/1.
*/


public class MyDialog extends DialogFragment {


@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setMessage("GGSima");
builder.setPositiveButton("hh", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getContext(),"ttt",Toast.LENGTH_SHORT);
}
});

return builder.create();
}
}

这是一个自定义的简单Dialog,接下来在Activity里使用它。

1
2
MyDialog myDialog = new MyDialog();
myDialog.show(getSupportFragmentManager(),"tag");

就是这么简单。一个show就可以解决所有事情。

本篇到此为止,更多深入的知识在生产环境上再学习。