原文出处:博主宇宙的极客http://www.cnblogs.com/nokiaguy/archive/2010/07/27/1786482.html

众所周知,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 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变量的值。

从本文可以看出,虽然使用普通方法控制对话框的某些功能,但通过反射技术可以很容易地做到看似不可能完成的任务。当然,除了控制对话框的关闭功能外,还可以控制对话框其他的行为,剩下的就靠读者自己挖掘了。

Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框的更多相关文章

  1. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  2. ConcurrentHashMap实现原理及源码分析

    ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对Ha ...

  3. HashMap和ConcurrentHashMap实现原理及源码分析

    HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...

  4. (转)ReentrantLock实现原理及源码分析

    背景:ReetrantLock底层是基于AQS实现的(CAS+CHL),有公平和非公平两种区别. 这种底层机制,很有必要通过跟踪源码来进行分析. 参考 ReentrantLock实现原理及源码分析 源 ...

  5. 【转】HashMap实现原理及源码分析

    哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...

  6. 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造

    原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论   自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...

  7. 《深入探索Netty原理及源码分析》文集小结

    <深入探索Netty原理及源码分析>文集小结 https://www.jianshu.com/p/239a196152de

  8. HashMap实现原理及源码分析之JDK8

    继续上回HashMap的学习 HashMap实现原理及源码分析之JDK7 转载 Java8源码-HashMap  基于JDK8的HashMap源码解析  [jdk1.8]HashMap源码分析 一.H ...

  9. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

随机推荐

  1. Unix/Linux环境C编程入门教程(34) 编程管理系统中的用户

    1.用户管理相关函数介绍 geteuid(取得有效的用户识别码) 相关函数 getuid,setreuid,setuid 表头文件 #include<unistd.h> #include& ...

  2. vmware重装系统后虚拟机实例文件*.vmdk重用

    如题:vmware重装系统后自定义的:虚拟机名称*.vmdk文件重用. 一.问题描述 系统磁盘坏道,装不上系统直接换了硬盘,但是新装的Vmware不能够通过open方式打开“自定义*.vmdk”(这个 ...

  3. 栈的C数组实现

    栈是一种先进后出的数据结构.栈的基本操作包括:入栈,出栈,初始化栈,清空栈,遍历栈. C代码如下: #include <stdio.h> #define MaxSize 20 typede ...

  4. flex——将Sprite控件添加到FLEX UI中

    在Flex的帮助文档里,有很多例子都是扩展Sprite类的.如果想把这些实例添加到你的s:Application中,如:addChild(DisplayObject ),肯定会出错.错误的大致意思是: ...

  5. Fast RCNN 学习

    因为项目需要,之前没有接触过深度学习的东西,现在需要学习Fast RCNN这个方法. 一步步来,先跟着做,然后再学习理论 Fast RCNN 训练自己数据集 (1编译配置) Fast RCNN 训练自 ...

  6. 代理方法keywordAction与Fun的使用

    代理是一种特殊的,指向某个方法模块所在的地址.一般来讲,那个方法模块,能够是一个普通的方法,很多其它的时候,是一团匿名的lamda表达式,即一个匿名方法.如今简单理解一下代理的简写方式,即Action ...

  7. Linux学习1——首次登录

    一.写在前面 在本节将介绍首次登录Linux系统(本文中为CentOS)所需要了解的一些基本操作.二.完成目标 1.了解GNOME和KDE窗口管理程序 2.使用在线求助man和info 3.基本命令操 ...

  8. asp.net application

    Application 对象用于存储和访问来自任何页面的变量,类似于 session 对象.不同之处在于,所有的用户分享一个 Application 对象,而 session 对象和用户的关系是一一对 ...

  9. SQL Server插入中文数据后出现乱码

    今天在做项目的过程中遇到如标题的问题,情况如下图: 数据库使用的是SQL Server2012版本,创建表的脚本如下: CREATE TABLE [dbo].[Type](  [TypeId] INT ...

  10. Spring IOC的配置使用(转)

    转:http://www.cnblogs.com/linjiqin/p/3408306.html Spring IOC的配置使用 1.1.1 XML配置的结构一般配置文件结构如下: <beans ...