<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ān)閉對(duì)話框

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

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

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

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

    上面的代碼并不是直接來關(guān)閉對(duì)話框的,而是通過一個(gè)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();。現(xiàn)在看了這么多源代碼,我們來總結(jié)一下對(duì)話框按鈕單擊事件的處理過程。在AlertController處理對(duì)話框按鈕時(shí)會(huì)為每一個(gè)按鈕添加一個(gè)onclick事件。而這個(gè)事件類的對(duì)象實(shí)例就是上面的mButtonHandler。在這個(gè)單擊事件中首先會(huì)通過發(fā)送消息的方式調(diào)用為按鈕設(shè)置的單擊事件(也就是通過setPositiveButton等方法的第二個(gè)參數(shù)設(shè)置的單擊事件),在觸發(fā)完按鈕的單擊事件后,會(huì)通過發(fā)送消息的方式調(diào)用dismiss方法來關(guān)閉對(duì)話框。而在AlertController類中定義了一個(gè)全局的mHandler變量。在AlertController類中通過ButtonHandler類來對(duì)象來為mHandler賦值。因此,我們只要使用我們自己Handler對(duì)象替換ButtonHandler就可以阻止調(diào)用dismiss方法來關(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語句的最后一個(gè)case子句(用于調(diào)用dismiss方法)和相關(guān)的代碼。
        下面我們就要為AlertController中的mHandler重新賦值。由于mHandler是private變量,因此,在這里需要使用Java的反射技術(shù)來為mHandler賦值。由于在AlertDialog類中的mAlert變量同樣也是private,因此,也需要使用同樣的反射技術(shù)來獲得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方法之前來修改一個(gè)mHandler變量的值,OK,下面我們就來見證奇跡的時(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語句,單擊對(duì)話框中的確定按鈕不會(huì)關(guān)閉對(duì)話框(除非在代碼中調(diào)用dismiss方法),單擊取消按鈕則會(huì)關(guān)閉對(duì)話框(因?yàn)檎{(diào)用了dismiss方法)。如果去了try…catch代碼段,對(duì)話框又會(huì)恢復(fù)正常了。
        雖然上面的代碼已經(jīng)解決了問題,但需要編寫的代碼仍然比較多,為此,我們也可采用另外一種方法來阻止關(guān)閉對(duì)話框。這種方法不需要定義任何的類。
        這種方法需要用點(diǎn)技巧。由于系統(tǒng)通過調(diào)用dismiss來關(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ì)話框還沒有關(guān)閉),這樣dismiss方法就失效了,這樣即使系統(tǒng)調(diào)用了dismiss方法也無法關(guān)閉對(duì)話框了。
        下面讓我們回到AlertDialog的源代碼中,再繼續(xù)跟蹤到AlertDialog的父類Dialog的源代碼中。找到dismissDialog方法。實(shí)際上,dismiss方法是通過dismissDialog方法來關(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中通過設(shè)置這個(gè)變量就可以使系統(tǒng)認(rèn)為對(duì)話框已經(jīng)關(guān)閉,就不再繼續(xù)關(guān)閉對(duì)話框了。由于mShowing也是private變量,因此,也需要反射技術(shù)來設(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è)按鈕就再也無法關(guān)閉對(duì)話框了。如果要關(guān)閉對(duì)話框,只需再將mShowing設(shè)為true即可。要注意的是,在一個(gè)按鈕里設(shè)置了mShowing變量,也會(huì)影響另一個(gè)按鈕的關(guān)閉對(duì)話框功能,因此,需要在每一個(gè)按鈕的單擊事件里都設(shè)置mShowing變量的值。

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





    Android開發(fā)完全講義(第2版)(本書版權(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 銀河使者 閱讀(4479) 評(píng)論(2)  編輯  收藏 所屬分類: java 原創(chuàng)移動(dòng)(mobile)Android/OPhone

    評(píng)論

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

    dismiss內(nèi)部調(diào)用的是dismissDialog方法關(guān)閉對(duì)話框的。
    2010-07-28 18:19 | 銀河使者
    主站蜘蛛池模板: 黄色片网站在线免费观看| 香港一级毛片免费看| 人妻巨大乳hd免费看| 四虎国产精品永久免费网址| 成年人免费网站在线观看| 亚洲国产成人影院播放| 亚洲网站免费观看| 国产亚洲福利一区二区免费看| 亚洲欧美自偷自拍另类视| 四虎国产精品免费永久在线| 成人五级毛片免费播放| 亚洲国产另类久久久精品黑人| 久久亚洲国产成人影院| 91视频免费观看高清观看完整| 免费毛片在线看片免费丝瓜视频| 国产亚洲美女精品久久久| 久久亚洲国产最新网站| 黄网站色视频免费在线观看的a站最新| 久久久久久99av无码免费网站 | 亚洲精品永久www忘忧草| 国产亚洲综合精品一区二区三区| 日日麻批免费40分钟无码| 免费a级毛片永久免费| 亚洲精品乱码久久久久久下载 | 亚洲高清免费在线观看| 亚洲男人的天堂一区二区| 国产婷婷综合丁香亚洲欧洲| 爽爽爽爽爽爽爽成人免费观看| 久久久久国产精品免费免费搜索| 亚洲国产精品va在线播放| 青青免费在线视频| 很黄很色很刺激的视频免费| 国产亚洲av片在线观看16女人| 国产亚洲精品精品精品| 国产成人午夜精品免费视频| 亚洲日本在线看片| 久久嫩草影院免费看夜色| 国产成人精品免费直播| 久久精品国产亚洲av麻豆图片| 日本免费人成网ww555在线| 亚洲精品老司机在线观看|