【Android源码 栏目提醒】:本文主要为网学会员提供“Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框 - 计算机教材”,希望对需要Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框 - 计算机教材网友有所帮助,学习一下!
Android系统原理与
源码分析1利用Java反射技术阻止通过按钮关闭对话框 本文为原创如需转载请注明作者和出处谢谢 众所周知AlertDialog类用于显示对话框。
关于AlertDialog的基本用法在这里就不详细介绍了网上有很多读者可以自己搜索。
那么本文要介绍的是如何随心所欲地控制AlertDialog。
现在我们来看看第一个需求如果某个应用需要弹出一个对话框。
当单击“确定“按钮时完成某些工作如果这些工作失败对话框不能关闭。
而当成功完成工作后则关闭对话框。
当然无论何程度情况单击“取消”按钮都会关闭对话框。
这个需求并不复杂也并不过分虽然我们可以自己弄个Activity来完成这个工作也可在View上自己放按钮但这显示有些大炮打蚊子了如果对话 框上只有一行文本费这么多劲太不值了。
但使用过AlertDialog的读者都知道无论单击的哪个按钮无论按钮单击事件的执行情况如何对话框是 肯定要关闭的。
也就是说用户无法控制对话框的关闭动作。
实际上关闭对话框的动作已经在
Android SDK写死了并且未给使用者留有任何接口。
但我的座右铭是“宇宙中没有什么是不能控制的”。
既然要控制对放框的关闭行为首先就得分析是哪些类、哪些代码使这个对话框关闭的。
进入AlertDialog类的源代码。
在AlertDialog中只 定义了一个变量mAlert。
这个变量是AlertController类型。
AlertController类是
Android的内部类在 com.
android.internal.app包中无法通过普通的方式访问。
也无法在Eclipse中通过按Ctrl键跟踪进源代码。
但可以直接在
Android源代码中找到AlertController.java。
我们再回到AlertDialog类中。
AlertDialog类实际上只是一个 架子。
象设置按钮、设置标题等工作都是由AlertController类完成的。
因此AlertController类才是关键。
找到AlertController.java文件。
打开后不要感到头晕哦这个文件中的代码是很多地。
不过这么多代码对本文的主题也没什么用处。
下面就找一下控制按钮的代码。
在AlertController类的开头就会看到如下的代码 代码 View.OnClickListener mButtonHandler new View.OnClickListener public void onClickView v Message m null if v mButtonPositive mButtonPositiveMessage null m Message.obtainmButtonPositiveMessage else if v mButtonNegative mButtonNegativeMessage null m Message.obtainmButtonNegativeMessage else if v mButtonNeutral mButtonNeutralMessage null m Message.obtainmButtonNeutralMessage if m null m.sendToTarget // Post a message so we dismiss after the above handlers are executed mHandler.obtainMessageButtonHandler.MSG_DISMISS_DIALOG mDialogInterface .sendToTarget 上面的代码并不是直接来关闭对话框的而是通过一个Handler来处理代码如下 代码 private static final class ButtonHandler extends Handler // Button clicks have Message.what as the BUTTON123 constant private static final int MSG_DISMISS_DIALOG 1 private WeakReference mDialog public ButtonHandlerDialogInterface dialog mDialog new WeakReferencedialog Override public void handleMessageMessage msg switch msg.what case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEUTRAL: DialogInterface.OnClickListener msg.obj.onClickmDialog.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 mDialog public ButtonHandlerDialogInterface dialog mDialog new WeakReferencedialog Override public void handleMessageMessage msg switch msg.what case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEUTRAL: DialogInterface.OnClickListener msg.obj.onClickmDialog .get msg.what break 我们可以看到上面的类和AlertController中的ButtonHandler类很像只是支掉了switch语句的最后一个case子句用于调用dismiss方法和相关的代码。
下面我们就要为AlertController中的mHandler重新赋值。
由于mHandler是private变量因此在这里需要使用Java 的反射技术来为mHandler赋值。
由于在AlertDialog类中的mAlert变量同样也是private因此也需要使用同样的反射技术来获 得mAlert变量。
代码如下 先建立一个AlertDialog对象 代码 AlertDialog alertDialog new AlertDialog.Builderthis .setTitleabc .setMessagecontent .setIconR.drawable.icon .setPositiveButton “确定” new OnClickListener Override public void onClickDialogInterface dialog int which .setNegativeButton取消 new OnClickListener Override public void onClickDialogInterface dialog int which dialog.dismiss .create 上面的对话框很普通单击哪个按钮都会关闭对话框。
下面在调用show方法之前来修改一个mHandler变量的值OK下面我们就来见证奇迹的时刻。
代码 try Field field alertDialog1.getClass.getDeclaredFieldmAlert field.setAccessibletrue // 获得mAlert变量的值 Object obj field.getalertDialog1 field obj.getClass.getDeclaredFieldmHandler field.setAccessibletrue // 修改mHandler变量的值使用新的ButtonHandler类 field.setobj new ButtonHandleralertDialog1 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.vLOG_TAG Dialog dismiss: already dismissed ignore return if mShowing if Config.LOGV Log.vLOG_TAG Dialog dismiss: not showing ignore return mWindowManager.removeViewmDecor mDecor null mWindow.closeAllPanels onStop mShowing false sendDismissMessage 该方法后面的代码不用管它先看ifmShowing??这段代码。
这个mShowing变量就是判断对话框是否已关闭的。
因此我们在代码中通过设置这个变量就可以使系统认为对话框已经关闭就不再继续关闭对话框了。
由于mShowing也是private变量因此也需要反射技术来设置这个变量。
我们可以在对话框按钮的单击事件中设置mShowing代码如下 代码 try Field field dialog.getClass .getSuperclass.getDeclaredField mShowing field.setAccessibletrue // 将mShowing变量设为false表示对话框已关闭 field.setdialog false dialog.dismiss catch Exception e 将上面的代码加到哪个按钮的单击事件代码中哪个按钮就再也无法关闭对话框了。
如果要关闭对话框只需再将mShowing设为true即可。
要注意的是在一个按钮里设置了mShowing变量也会影响另一个按钮的关闭对话框功能因此需要在每一个按钮的单击事件里都设置mShowing变量的值。
从本文可以看出虽然使用普通方法控制对话框的某些功能但通过反射技术可以很容易地做到看似不可能完成的任务。
当然除了控制对话框的关闭功能外还可以控制对话框其他的行为剩下的就靠读者自己挖掘了。
新浪微博http://t.sina.com.cn/androidguy 昵称李宁_Lining 华章培训网视频教程实现
Android版的多功能日历 《
Android/OPhone开发完全讲义》本书版权已输出到台湾 样章和目录下载 互动网 当当网 卓越亚马逊 《人人都玩开心网Ext JSAndroidSSH整合开发Web与移动SNS》 样章下载 互动网 乐博
Android手机客户端新浪微博发布 Tag标签: javaandroidalberdialog反射