<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 312, 文章 - 14, 評(píng)論 - 1393, 引用 - 0
    數(shù)據(jù)加載中……

    Android系統(tǒng)原理與源碼分析(1):利用Java反射技術(shù)阻止通過(guò)按鈕關(guān)閉對(duì)話框

    本文為原創(chuàng),如需轉(zhuǎn)載,請(qǐng)注明作者和出處,謝謝!

        眾所周知,AlertDialog類用于顯示對(duì)話框。關(guān)于AlertDialog的基本用法在這里就不詳細(xì)介紹了,網(wǎng)上有很多,讀者可以自己搜索。那么本文要介紹的是如何隨心所欲地控制AlertDialog。
        現(xiàn)在我們來(lái)看看第一個(gè)需求:如果某個(gè)應(yīng)用需要彈出一個(gè)對(duì)話框。當(dāng)單擊“確定“按鈕時(shí)完成某些工作,如果這些工作失敗,對(duì)話框不能關(guān)閉。而當(dāng)成功完成工作后,則關(guān)閉對(duì)話框。當(dāng)然,無(wú)論何程度情況,單擊“取消”按鈕都會(huì)關(guān)閉對(duì)話框。
        這個(gè)需求并不復(fù)雜,也并不過(guò)分(雖然我們可以自己弄個(gè)Activity來(lái)完成這個(gè)工作,也可在View上自己放按鈕,但這顯示有些大炮打蚊子了,如果對(duì)話框上只有一行文本,費(fèi)這么多勁太不值了)。但使用過(guò)AlertDialog的讀者都知道,無(wú)論單擊的哪個(gè)按鈕,無(wú)論按鈕單擊事件的執(zhí)行情況如何,對(duì)話框是肯定要關(guān)閉的。也就是說(shuō),用戶無(wú)法控制對(duì)話框的關(guān)閉動(dòng)作。實(shí)際上,關(guān)閉對(duì)話框的動(dòng)作已經(jīng)在Android SDK寫(xiě)死了,并且未給使用者留有任何接口。但我的座右銘是“宇宙中沒(méi)有什么是不能控制的”。
        既然要控制對(duì)放框的關(guān)閉行為,首先就得分析是哪些類、哪些代碼使這個(gè)對(duì)話框關(guān)閉的。進(jìn)入AlertDialog類的源代碼。在AlertDialog中只定義了一個(gè)變量:mAlert。這個(gè)變量是AlertController類型。AlertController類是Android的內(nèi)部類,在com.android.internal.app包中,無(wú)法通過(guò)普通的方式訪問(wèn)。也無(wú)法在Eclipse中通過(guò)按Ctrl鍵跟蹤進(jìn)源代碼。但可以直接在Android源代碼中找到AlertController.java。我們?cè)倩氐紸lertDialog類中。AlertDialog類實(shí)際上只是一個(gè)架子。象設(shè)置按鈕、設(shè)置標(biāo)題等工作都是由AlertController類完成的。因此,AlertController類才是關(guān)鍵。
        找到AlertController.java文件。打開(kāi)后不要感到頭暈哦,這個(gè)文件中的代碼是很多地。不過(guò)這么多代碼對(duì)本文的主題也沒(méi)什么用處。下面就找一下控制按鈕的代碼。
        在AlertController類的開(kāi)頭就會(huì)看到如下的代碼:
       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();
            }
        };

    從這段代碼中可以猜出來(lái),前幾行代碼用來(lái)觸發(fā)對(duì)話框中的三個(gè)按鈕(PositiveNegativeNeutral)的單擊事件,而最后的代碼則用來(lái)關(guān)閉對(duì)話框(因?yàn)槲覀儼l(fā)現(xiàn)了MSG_DISMISS_DIALOG、猜出來(lái)的)。

    mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
                        .sendToTarget();

    上面的代碼并不是直接來(lái)關(guān)閉對(duì)話框的,而是通過(guò)一個(gè)Handler來(lái)處理,代碼如下:

        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();。現(xiàn)在看了這么多源代碼,我們來(lái)總結(jié)一下對(duì)話框按鈕單擊事件的處理過(guò)程。在AlertController處理對(duì)話框按鈕時(shí)會(huì)為每一個(gè)按鈕添加一個(gè)onclick事件。而這個(gè)事件類的對(duì)象實(shí)例就是上面的mButtonHandler。在這個(gè)單擊事件中首先會(huì)通過(guò)發(fā)送消息的方式調(diào)用為按鈕設(shè)置的單擊事件(也就是通過(guò)setPositiveButton等方法的第二個(gè)參數(shù)設(shè)置的單擊事件),在觸發(fā)完按鈕的單擊事件后,會(huì)通過(guò)發(fā)送消息的方式調(diào)用dismiss方法來(lái)關(guān)閉對(duì)話框。而在AlertController類中定義了一個(gè)全局的mHandler變量。在AlertController類中通過(guò)ButtonHandler類來(lái)對(duì)象來(lái)為mHandler賦值。因此,我們只要使用我們自己Handler對(duì)象替換ButtonHandler就可以阻止調(diào)用dismiss方法來(lái)關(guān)閉對(duì)話框。下面先在自己的程序中建立一個(gè)新的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語(yǔ)句的最后一個(gè)case子句(用于調(diào)用dismiss方法)和相關(guān)的代碼。
        下面我們就要為AlertController中的mHandler重新賦值。由于mHandler是private變量,因此,在這里需要使用Java的反射技術(shù)來(lái)為mHandler賦值。由于在AlertDialog類中的mAlert變量同樣也是private,因此,也需要使用同樣的反射技術(shù)來(lái)獲得mAlert變量。代碼如下:

     先建立一個(gè)AlertDialog對(duì)象

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

    上面的對(duì)話框很普通,單擊哪個(gè)按鈕都會(huì)關(guān)閉對(duì)話框。下面在調(diào)用show方法之前來(lái)修改一個(gè)mHandler變量的值,OK,下面我們就來(lái)見(jiàn)證奇跡的時(shí)刻。

            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)
            {
            }
          
    //  顯示對(duì)話框
          alertDialog.show();

      我們發(fā)現(xiàn),如果加上try   catch語(yǔ)句,單擊對(duì)話框中的確定按鈕不會(huì)關(guān)閉對(duì)話框(除非在代碼中調(diào)用dismiss方法),單擊取消按鈕則會(huì)關(guān)閉對(duì)話框(因?yàn)檎{(diào)用了dismiss方法)。如果去了try…catch代碼段,對(duì)話框又會(huì)恢復(fù)正常了。
        雖然上面的代碼已經(jīng)解決了問(wèn)題,但需要編寫(xiě)的代碼仍然比較多,為此,我們也可采用另外一種方法來(lái)阻止關(guān)閉對(duì)話框。這種方法不需要定義任何的類。
        這種方法需要用點(diǎn)技巧。由于系統(tǒng)通過(guò)調(diào)用dismiss來(lái)關(guān)閉對(duì)話框,那么我們可以在dismiss方法上做點(diǎn)文章。在系統(tǒng)調(diào)用dismiss方法時(shí)會(huì)首先判斷對(duì)話框是否已經(jīng)關(guān)閉,如果對(duì)話框已經(jīng)關(guān)閉了,就會(huì)退出dismiss方法而不再繼續(xù)關(guān)閉對(duì)話框了。因此,我們可以欺騙一下系統(tǒng),當(dāng)調(diào)用dismiss方法時(shí)我們可以讓系統(tǒng)以為對(duì)話框已經(jīng)關(guān)閉(雖然對(duì)話框還沒(méi)有關(guān)閉),這樣dismiss方法就失效了,這樣即使系統(tǒng)調(diào)用了dismiss方法也無(wú)法關(guān)閉對(duì)話框了。
        下面讓我們回到AlertDialog的源代碼中,再繼續(xù)跟蹤到AlertDialog的父類Dialog的源代碼中。找到dismissDialog方法。實(shí)際上,dismiss方法是通過(guò)dismissDialog方法來(lái)關(guān)閉對(duì)話框的,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){}這段代碼。這個(gè)mShowing變量就是判斷對(duì)話框是否已關(guān)閉的。因此,我們?cè)诖a中通過(guò)設(shè)置這個(gè)變量就可以使系統(tǒng)認(rèn)為對(duì)話框已經(jīng)關(guān)閉,就不再繼續(xù)關(guān)閉對(duì)話框了。由于mShowing也是private變量,因此,也需要反射技術(shù)來(lái)設(shè)置這個(gè)變量。我們可以在對(duì)話框按鈕的單擊事件中設(shè)置mShowing,代碼如下:

    try
    {
        Field field 
    = dialog.getClass()
                .getSuperclass().getDeclaredField(
                        
    "mShowing");
        field.setAccessible(
    true);
        
    //  將mShowing變量設(shè)為false,表示對(duì)話框已關(guān)閉
        field.set(dialog, false);
        dialog.dismiss();

    }
    catch (Exception e)
    {

    }

    將上面的代碼加到哪個(gè)按鈕的單擊事件代碼中,哪個(gè)按鈕就再也無(wú)法關(guān)閉對(duì)話框了。如果要關(guān)閉對(duì)話框,只需再將mShowing設(shè)為true即可。要注意的是,在一個(gè)按鈕里設(shè)置了mShowing變量,也會(huì)影響另一個(gè)按鈕的關(guān)閉對(duì)話框功能,因此,需要在每一個(gè)按鈕的單擊事件里都設(shè)置mShowing變量的值。

    從本文可以看出,雖然使用普通方法控制對(duì)話框的某些功能,但通過(guò)反射技術(shù)可以很容易地做到看似不可能完成的任務(wù)。當(dāng)然,除了控制對(duì)話框的關(guān)閉功能外,還可以控制對(duì)話框其他的行為,剩下的就靠讀者自己挖掘了。





    Android開(kāi)發(fā)完全講義(第2版)(本書(shū)版權(quán)已輸出到臺(tái)灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

    posted on 2010-07-27 23:05 銀河使者 閱讀(4478) 評(píng)論(2)  編輯  收藏 所屬分類: java 原創(chuàng)移動(dòng)(mobile)Android/OPhone

    評(píng)論

    # re: Android系統(tǒng)原理與源碼分析(1):利用Java反射技術(shù)阻止通過(guò)按鈕關(guān)閉對(duì)話框  回復(fù)  更多評(píng)論   

    void android.app.Activity.dismissDialog(int id)
    不是可以dismiss一個(gè)dialog么?
    2010-07-28 14:51 | Kurt

    # re: Android系統(tǒng)原理與源碼分析(1):利用Java反射技術(shù)阻止通過(guò)按鈕關(guān)閉對(duì)話框  回復(fù)  更多評(píng)論   

    dismiss內(nèi)部調(diào)用的是dismissDialog方法關(guān)閉對(duì)話框的。
    2010-07-28 18:19 | 銀河使者
    主站蜘蛛池模板: 国产91在线免费| 人成电影网在线观看免费| 免费在线观看的网站| 亚洲剧场午夜在线观看| 成人免费视频69| 亚洲区精品久久一区二区三区| 免费视频专区一国产盗摄| 亚洲影视自拍揄拍愉拍| 毛片免费全部免费观看| 亚洲av无码专区在线电影| 日本二区免费一片黄2019| 妇女自拍偷自拍亚洲精品| 全亚洲最新黄色特级网站| 成人特级毛片69免费观看| 久久久久亚洲av毛片大| 国产精品成人免费观看| 亚洲午夜久久久影院伊人| 可以免费观看的毛片| 亚洲一区二区影院| 无码国产精品一区二区免费式影视| 亚洲av永久无码精品三区在线4 | 日韩在线视频免费看| 亚洲国产欧美国产综合一区| 国产一区二区三区免费看| 老司机午夜免费视频| 精品亚洲一区二区三区在线观看| 日韩免费高清播放器| 噜噜噜亚洲色成人网站∨| 成人无码区免费A片视频WWW | 两个人看的www免费视频中文| 久久亚洲精品中文字幕无码| 老汉精品免费AV在线播放| 亚洲av无码片在线观看| 国产女高清在线看免费观看| 一级免费黄色毛片| 久久久久亚洲Av片无码v| 美丽的姑娘免费观看在线播放| 亚洲成年网站在线观看| 亚洲国产精品第一区二区三区| 成全视频高清免费观看电视剧| 亚洲毛片免费视频|