【转】如何让AlertDialog 在点击确定或者取消时不消失

来源:互联网 发布:淘宝windows 编辑:程序博客网 时间:2024/05/19 14:01

转自: http://bbs.3gstdy.com/td-1033-1-1.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();
        }
    };

从这段代码中可以猜出来,前几行代码用来触发对话框中的三个按钮(PositiveNegativeNeutral)的单击事件,而最后的代码则用来关闭对话框(因为我们发现了MSG_DISMISS_DIALOG、猜出来的)。




-->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对象


Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->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)
        {
        }
      
//  显示对话框
      alertDialog.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,代码如下:


Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->try
{
    Field field 
= dialog.getClass()
            .getSuperclass().getDeclaredField(
                    
"mShowing");
    field.setAccessible(
true);
    
//  将mShowing变量设为false,表示对话框已关闭
    field.set(dialog, false);
    dialog.dismiss();

}
catch (Exception e)
{

}


//=============

http://www.linuxidc.com/Linux/2012-09/71397.htm

Android 4.0设置Dialog点击屏幕不消失

业务的需求是变化莫测的,最近就遇到一个需求是——用户只有点击Dialog的取消按钮才会消失,点击屏幕的时候不消失。Android ICS对UI做了很大的变动,系统提倡使用DialogFragment,但是系统默认的操作习惯是点击屏幕Dialog会自动消失。

为了实现业务的需求,想过使用Dialog风格的Activity,但是做出来的效果和系统的UI效果不匹配,最终只有失败告终。在黔驴技穷的时候,决定再仔细撸一下Android文档,终于在文档中发现了Dialog的setCanceledOnTouchOutside属性,具体使用如下:

  1. public class MyAlertDialogFragment extends DialogFragment {  
  2.   
  3.     public static MyAlertDialogFragment newInstance(int title) {  
  4.         MyAlertDialogFragment frag = new MyAlertDialogFragment();  
  5.         Bundle args = new Bundle();  
  6.         args.putInt("title", title);  
  7.         frag.setArguments(args);  
  8.         return frag;  
  9.     }  
  10.   
  11.     @TargetApi(11)  
  12.     @Override  
  13.     public Dialog onCreateDialog(Bundle savedInstanceState) {  
  14.         int title = getArguments().getInt("title");  
  15.   
  16.           
  17.         AlertDialog dialog = new AlertDialog.Builder(getActivity())  
  18.         .setIcon(R.drawable.ic_launcher)  
  19.           
  20.         .setTitle(title)  
  21.         .setPositiveButton(R.string.alert_dialog_ok,  
  22.             new DialogInterface.OnClickListener() {  
  23.                 public void onClick(DialogInterface dialog, int whichButton) {  
  24.                     ((MainActivity)getActivity()).doPositiveClick();  
  25.                 }  
  26.             }  
  27.         )  
  28.         .setNegativeButton(R.string.alert_dialog_cancel,  
  29.             new DialogInterface.OnClickListener() {  
  30.                 public void onClick(DialogInterface dialog, int whichButton) {  
  31.                     ((MainActivity)getActivity()).doNegativeClick();  
  32.                 }  
  33.             }  
  34.         )  
  35.         .create();  
  36.         dialog.setCanceledOnTouchOutside(false);// 设置点击屏幕Dialog不消失   
  37.           
  38.         return dialog;  
  39.     }  
  40. }  

以上只是设置Dialog的一个小技巧,希望对大家有所帮助。



原创粉丝点击