代码
View.OnClickListener mButtonHandler
=
new
View.OnClickListener() {
public
void
onClick(View v) {
Message m
=
null
;
if
(v
==
mButtonPositive
&&
mButtonPositiveMessage
!=
null
) {
m
=
Message.obtain(mButtonPositiveMessage);
}
else
if
(v
==
mButtonNegative
&&
mButtonNegativeMessage
!=
null
) {
m
=
Message.obtain(mButtonNegativeMessage);
}
else
if
(v
==
mButtonNeutral
&&
mButtonNeutralMessage
!=
null
) {
m
=
Message.obtain(mButtonNeutralMessage);
}
if
(m
!=
null
) {
m.sendToTarget();
}
//
Post a message so we dismiss after the above handlers are executed
mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
.sendToTarget();
}
};
上面的代码并不是直接来关闭对话框的,而是通过一个Handler来处理,代码如下:
代码
private
static
final
class
ButtonHandler
extends
Handler {
//
Button clicks have Message.what as the BUTTON{1,2,3} constant
private
static
final
int
MSG_DISMISS_DIALOG
=
1
;
private
WeakReference
<
DialogInterface
>
mDialog;
public
ButtonHandler(DialogInterface dialog) {
mDialog
=
new
WeakReference
<
DialogInterface
>
(dialog);
}
@Override
public
void
handleMessage(Message msg) {
switch
(msg.what) {
case
DialogInterface.BUTTON_POSITIVE:
case
DialogInterface.BUTTON_NEGATIVE:
case
DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
break
;
case
MSG_DISMISS_DIALOG:
((DialogInterface) msg.obj).dismiss();
}
}
}
从 上面代码的最后可以找到 ((DialogInterface) msg.obj).dismiss();。现在看了这么多源代码,我们来总结一下对话框按钮单击事件的处理过程。在AlertController处理对 话框按钮时会为每一个按钮添加一个onclick事件。而这个事件类的对象实例就是上面的mButtonHandler。在这个单击事件中首先会通过发送 消息的方式调用为按钮设置的单击事件(也就是通过setPositiveButton等方法的第二个参数设置的单击事件),在触发完按钮的单击事件后,会 通过发送消息的方式调用dismiss方法来关闭对话框。而在AlertController类中定义了一个全局的mHandler变量。在 AlertController类中通过ButtonHandler类来对象来为mHandler赋值。因此,我们只要使用我们自己Handler对象替 换ButtonHandler就可以阻止调用dismiss方法来关闭对话框。下面先在自己的程序中建立一个新的ButtonHandler类(也可叫其 他的名)。
代码
class
ButtonHandler
extends
Handler
{
private
WeakReference
<
DialogInterface
>
mDialog;
public
ButtonHandler(DialogInterface dialog)
{
mDialog
=
new
WeakReference
<
DialogInterface
>
(dialog);
}
@Override
public
void
handleMessage(Message msg)
{
switch
(msg.what)
{
case
DialogInterface.BUTTON_POSITIVE:
case
DialogInterface.BUTTON_NEGATIVE:
case
DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog
.get(), msg.what);
break
;
}
}
}
我们可以看到,上面的类和AlertController中的ButtonHandler类很像,只是支掉了switch语句的最后一个case子句(用于调用dismiss方法)和相关的代码。
下面我们就要为AlertController中的mHandler重新赋值。由于mHandler是private变量,因此,在这里需要使用Java 的反射技术来为mHandler赋值。由于在AlertDialog类中的mAlert变量同样也是private,因此,也需要使用同样的反射技术来获 得mAlert变量。代码如下:
先建立一个AlertDialog
对象
代码
AlertDialog alertDialog
=
new
AlertDialog.Builder(
this
)
.setTitle(
"
abc
"
)
.setMessage(
"
content
"
)
.setIcon(R.drawable.icon)
.setPositiveButton( “确定”,
new
OnClickListener()
{
@Override
public
void
onClick(DialogInterface dialog,
int
which)
{
}
}).setNegativeButton(
"
取消
"
,
new
OnClickListener()
{
@Override
public
void
onClick(DialogInterface dialog,
int
which)
{
dialog.dismiss();
}
}).create()
上面的对话框很普通,单击哪个按钮都会关闭对话框。下面在调用show方法之前来修改一个mHandler变量的值,OK,下面我们就来见证奇迹的时刻。
代码
try
{
Field field
=
alertDialog1.getClass().getDeclaredField(
"
mAlert
"
);
field.setAccessible(
true
);
//
获得mAlert变量的值
Object obj
=
field.get(alertDialog1);
field
=
obj.getClass().getDeclaredField(
"
mHandler
"
);
field.setAccessible(
true
);
//
修改mHandler变量的值,使用新的ButtonHandler类
field.set(obj,
new
ButtonHandler(alertDialog1));
}
catch
(Exception e)
{
}
显示对话框
ertDialog.show();
我们发现,如果加上try catch语句,单击对话框中的确定按钮不会关闭对话框(除非在代码中调用dismiss方法),单击取消按钮则会关闭对话框(因为调用了dismiss方法)。如果去了try…catch代码段,对话框又会恢复正常了。
虽然上面的代码已经解决了问题,但需要编写的代码仍然比较多,为此,我们也可采用另外一种方法来阻止关闭对话框。这种方法不需要定义任何的类。
这种方法需要用点技巧。由于系统通过调用dismiss来关闭对话框,那么我们可以在dismiss方法上做点文章。在系统调用dismiss方法时会首 先判断对话框是否已经关闭,如果对话框已经关闭了,就会退出dismiss方法而不再继续关闭对话框了。因此,我们可以欺骗一下系统,当调用 dismiss方法时我们可以让系统以为对话框已经关闭(虽然对话框还没有关闭),这样dismiss方法就失效了,这样即使系统调用了dismiss方 法也无法关闭对话框了。
下面让我们回到AlertDialog的源代码中,再继续跟踪到AlertDialog的父类Dialog的源代码中。找到dismissDialog方 法。实际上,dismiss方法是通过dismissDialog方法来关闭对话框的,dismissDialog方法的代码如下:
代码
private
void
dismissDialog() {
if
(mDecor
==
null
) {
if
(Config.LOGV) Log.v(LOG_TAG,
"
[Dialog] dismiss: already dismissed, ignore
"
);
return
;
}
if
(
!
mShowing) {
if
(Config.LOGV) Log.v(LOG_TAG,
"
[Dialog] dismiss: not showing, ignore
"
);
return
;
}
mWindowManager.removeView(mDecor);
mDecor
=
null
;
mWindow.closeAllPanels();
onStop();
mShowing
=
false
;
sendDismissMessage();
}
该方法后面的代码不用管它,先看if(!mShowing){…}这段代码。这个mShowing变量就是判断对话框是否已关闭的。因此,我们在代码中通过设置这个变量就可以使系统认为对话框已经关闭,就不再继续关闭对话框了。由于mShowing也是private变量,因此,也需要反射技术来设置这个变量。我们可以在对话框按钮的单击事件中设置mShowing,代码如下:
代码
try
{
Field field
=
dialog.getClass()
.getSuperclass().getDeclaredField(
"
mShowing
"
);
field.setAccessible(
true
);
//
将mShowing变量设为false,表示对话框已关闭
field.set(dialog,
false
);
dialog.dismiss();
}
catch
(Exception e)
{
}
将上面的代码加到哪个按钮的单击事件代码中,哪个按钮就再也无法关闭对话框了。如果要关闭对话框,只需再将mShowing设为true即可。要注意的是,在一个按钮里设置了mShowing变量,也会影响另一个按钮的关闭对话框功能,因此,需要在每一个按钮的单击事件里都设置mShowing变量的值。
从本文可以看出,虽然使用普通方法控制对话框的某些功能,但通过反射技术可以很容易地做到看似不可能完成的任务。当然,除了控制对话框的关闭功能外,还可以控制对话框其他的行为,剩下的就靠读者自己挖掘了。