知识点:
1、利用反射,阻止AlertDialog每次的dismiss事件;
在使用AlertDialog的时候,我们设置positive,negative和neutral的button,在点击之后,即使不手动调用dismiss方法,系统都会自动的帮我们dismiss掉了。
但是我这里可能点击了之后,还有一些时间比较长的工作处理之后,才能够dismiss掉此AlertDialog;那么这就是一个问题了。我们先直接看怎么来阻止这个系统的dismiss事件。
话不多述,我们直接先上代码,看看如何操作的,然后在稍微看看源码,一探究竟:
首先是来一个button,设置点击事件,弹出dialog
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_rDialog:
showDialog();
break;
}
}
然后在activity里头,创建showDialog方法,如下:
/**
* 利用反射,阻止dialog点击确定或者取消按钮,总是会使得dialog消失的结果
*/
void showDialog() {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setTitle("title")
.setMessage("content")
.setIcon(R.drawable.__leak_canary_icon)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//就算阻止了dialog的dismiss事件,这里调用dismiss也还是可以dismiss掉dialog的
// dialog.dismiss();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).create();
alertDialog.setCanceledOnTouchOutside(false);
setDialogCancelable(alertDialog);
// setDialogIsCanceled(alertDialog, false);
/* 显示对话框 */
alertDialog.show();
}
/**
* 通过重新设置一个button处理类,达到点击确定按钮不dismiss掉dialog的效果
*
* @param alertDialog alertDialog
*/
void setDialogCancelable(AlertDialog alertDialog) {
try {
Field field = alertDialog.getClass().getDeclaredField("mAlert");
field.setAccessible(true);
/* 获得mAlert变量的值 */
Object obj = field.get(alertDialog);
field = obj.getClass().getDeclaredField("mHandler");
field.setAccessible(true);
/* 修改mHandler变量的值,使用新的ButtonHandler类 */
field.set(obj, new IButtonHandler(alertDialog));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 通过获取mShowing字段,修改它的值,达到点击确定按钮不dismiss掉dialog的效果
* 但是好像是没有用的,不起效果
*
* @param alertDialog alertDialog
* @param isCanceled isCanceled false=此dialog已经关闭了,反之则是为关闭
*/
void setDialogIsCanceled(AlertDialog alertDialog, boolean isCanceled) {
try {
/* 这里有一个层级关系需要记住,看字段是属于父类,还是属于父类的父类 */
Field field = alertDialog.getClass()
.getSuperclass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
/* 将mShowing变量设为false,表示对话框已关闭 */
field.set(alertDialog, isCanceled);
alertDialog.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
}
还有涉及到一个IButtonHandler 的类
package com.yaojt.ui.reflect;
import android.content.DialogInterface;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
* desc:自定义的dialog点击按钮事件处理机制
* <p>
* author:kuyu.yaojt (tanksu)
* <p>
* email:yaojt@kuyumall.com
* <p>
* date:17/3/7
*/
public class IButtonHandler extends Handler {
/* 使用弱引用,避免内存泄漏 */
private WeakReference<DialogInterface> mDialog;
/**
* 构造方法
*
* @param dialog dialog
*/
public IButtonHandler(DialogInterface dialog) {
mDialog = new WeakReference<>(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,最终都会调用以下代码,并且传入MSG_DISMISS_DIALOG标志
* // Post a message so we dismiss after the above handlers are executed
mHandler.obtainMessage(IButtonHandler.MSG_DISMISS_DIALOG, mDialog)
.sendToTarget();
然后到下面代码
//以下是原始的ButtonHandler类 可以看到最后的最后,都会走到这一个入口里去了
导致所有的dialog方法都会被dismiss掉,只要拦截这个就可以了。做法就是重新定义一个ButtonHandler
设置取代原来的ButtonHandler实例即可。有一点不好那就是会影响到所有的dialog。
但是我们在确定监听里头是调用dismiss,是可以dismiss掉dialog的
其实就是不要dismiss的这个方法入口
private static final class IButtonHandler 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 IButtonHandler(DialogInterface dialog) {
mDialog = new WeakReference<>(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();
}
}
}
*/
}
}
}
代码就是以上这些,都有注释的了。
反射确实挺好用的,可以在运行时动态地加载一个类,然后调用执行类里面的方法,在高级开发中,这个是必须的技能,要好好学习。
另外,这个也是有风险的做法,其他人也可以使用这个来获取到你的类的相关信息,然后做一些坏事什么的。