Android中AlertDialog消息内部代码探析

本文详细解析了AlertDialog的窗口创建过程,包括如何处理各种消息监听,如显示、取消、关闭等事件,以及如何自定义按钮的设置与响应。通过分析核心代码流程,揭示了AlertDialog背后的工作原理。

AlertDialog

下面是自我提问。。。

1.创建窗口的过程?
2.如何处理OnDismissListener, OnCancelListener, OnShowListener和OnKeyListener这些消息的?


自我总结:

窗口创建过程

一般当你调用AlertDialog dlg = new AlertDialog(context);

的时候最终在Dialog类的构造函数里面创建窗口,代码如下:

    Dialog(Context context, int theme, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (theme == 0) {
                TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
                        outValue, true);
                theme = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, theme);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
        Window w = PolicyManager.makeNewWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);
        mListenersHandler = new ListenersHandler(this);
    }

当我们调用

setOnShowListener(new OnShowListener() {
			@Override
			public void onShow(DialogInterface dialog) {
				// TODO Auto-generated method stub
				
			}
		});
时,其内部实现为:

    public void setOnShowListener(OnShowListener listener) {
        if (listener != null) {
            mShowMessage = mListenersHandler.obtainMessage(SHOW, listener);
        } else {
            mShowMessage = null;
        }
    }

    public final Message obtainMessage(int what, Object obj)
    {
        return Message.obtain(this, what, obj);
    }

    public static Message obtain(Handler h, int what, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.obj = obj;

        return m;
    }
这时mListenerHandler获取一个为SHOW的消息,并赋给mShowMessage。其中mShowMessage的Target就是mListenerHandler。


然后继续调用

 dlg.show();

下面有:
 public void show() {
        // ...
        try {
            mWindowManager.addView(mDecor, l);
            mShowing = true;
    
            sendShowMessage();
        } finally {
        }
    }

    private void sendShowMessage() {
        if (mShowMessage != null) {
            // Obtain a new message so this dialog can be re-used
            Message.obtain(mShowMessage).sendToTarget();
        }
    }
看到没,刚才我们调用setOnShowListener的时候,已经给mShowMessage赋过值了。

这时,mListenerHandler的handleMessage会收到消息SHOW。

我们来看下ListenerHandler的handleMessage的处理函数:

    private static final class ListenersHandler extends Handler {
        private WeakReference<DialogInterface> mDialog;

        public ListenersHandler(Dialog dialog) {
            mDialog = new WeakReference<DialogInterface>(dialog);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DISMISS:
                    ((OnDismissListener) msg.obj).onDismiss(mDialog.get());
                    break;
                case CANCEL:
                    ((OnCancelListener) msg.obj).onCancel(mDialog.get());
                    break;
                case SHOW:
                    ((OnShowListener) msg.obj).onShow(mDialog.get());
                    break;
            }
        }
    }
这里通过Callback触发用户定义的Listener。。。

其他的诸如Dismiss, Cancel是一样的道理。

OK,这里完成了Dialog的Dismiss, Cancel, 以及Show的事件监听的调用过程。。。


下面打算分析一下AlertDialog特有的3个按钮(BUTTON_POSITIVE,BUTTON_NEGATIVE,BUTTON_NEUTRAL)的调用过程。

一般我们通过如下代码设定来给一个AlertDialog设定按钮以及事件监听接口。

        setButton(DialogInterface.BUTTON_NEUTRAL,
                "Neutral",
                new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // TODO Auto-generated method stub        
                Log.d("java-hh", "AlertDialog -- BUTTON_NEUTRAL click");
            }
        });

内部跟踪代码得到:
    public void setButton(int whichButton, CharSequence text, OnClickListener listener) {
        mAlert.setButton(whichButton, text, listener, null);
    }

继续跟踪:
    public void setButton(int whichButton, CharSequence text,
            DialogInterface.OnClickListener listener, Message msg) {

        if (msg == null && listener != null) {
            msg = mHandler.obtainMessage(whichButton, listener);
        }
        
        switch (whichButton) {

            case DialogInterface.BUTTON_POSITIVE:
                mButtonPositiveText = text;
                mButtonPositiveMessage = msg;
                break;
                
            case DialogInterface.BUTTON_NEGATIVE:
                mButtonNegativeText = text;
                mButtonNegativeMessage = msg;
                break;
                
            case DialogInterface.BUTTON_NEUTRAL:
                mButtonNeutralText = text;
                mButtonNeutralMessage = msg;
                break;
                
            default:
                throw new IllegalArgumentException("Button does not exist");
        }
    }
这里同样有一个Handler,我们获取一个Target为Handler的消息(该消息ID为whichButton,即按钮的ID),

然后使用成员变量mButtonNeutralMessage保存该消息。但是何时该消息才会被调用呢?看代码:

package com.android.internal.app;

public class 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();
        }
    };
// ...
}
原来有个mButtonHandler一开始就将3个按钮的消息处理函数设置好了,然后会检查用于是否设置了相关的消息监听接口。

如果用户设置了,直接触发该消息(

m.sendToTarget();
)。最后发送一个MSG_DISMISS_DIALOG来关闭AlertDialog,这就是为什么单击Cancel, OK, Neutral都能关闭AlertDialog的原因。

下面再看下mHandler消息处理函数的实现:

mHandler = new ButtonHandler(di);

    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();
            }
        }
    }

怎么样?很清楚了吧,3个按钮消息,外加一个关闭AlertDialog的dismiss消息处理函数。


需要考虑的是ButtonHandler里面使用WeakReference。我估计是因为考虑到如下的情况:

用户单击了OK按钮,Handler还未处理到

case DialogInterface.BUTTON_POSITIVE:
而此时Dialog已经因为某中特殊情况被关闭了,此时mDialog会为NULL的。。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值