利用Java反射技術阻止通過按鈕關閉對話框

衆所周知,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類的開頭就會看到如下的代碼:

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

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

 

上面的代碼並不是直接來關閉對話框的,而是通過一個Handler來處理,代碼如下:


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

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

我們可以看到,上面的類和AlertController中的ButtonHandler類很像,只是支掉了switch語句的最後一個case子句(用於調用dismiss方法)和相關的代碼。
    下面我們就要爲AlertController中的mHandler重新賦值。由於mHandler是private變量,因此,在這裏需要使用Java的反射技術來爲mHandler賦值。由於在AlertDialog類中的mAlert變量同樣也是private,因此,也需要使用同樣的反射技術來獲得mAlert變量。代碼如下:

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

上面的對話框很普通,單擊哪個按鈕都會關閉對話框。下面在調用show方法之前來修改一個mHandler變量的值,OK,下面我們就來見證奇蹟的時刻。

我們發現,如果加上try   catch語句,單擊對話框中的確定按鈕不會關閉對話框(除非在代碼中調用dismiss方法),單擊取消按鈕則會關閉對話框(因爲調用了dismiss方法)。如果去了try…catch代碼段,對話框又會恢復正常了。
    雖然上面的代碼已經解決了問題,但需要編寫的代碼仍然比較多,爲此,我們也可採用另外一種方法來阻止關閉對話框。這種方法不需要定義任何的類。
    這種方法需要用點技巧。由於系統通過調用dismiss來關閉對話框,那麼我們可以在dismiss方法上做點文章。在系統調用dismiss方法時會首先判斷對話框是否已經關閉,如果對話框已經關閉了,就會退出dismiss方法而不再繼續關閉對話框了。因此,我們可以欺騙一下系統,當調用dismiss方法時我們可以讓系統以爲對話框已經關閉(雖然對話框還沒有關閉),這樣dismiss方法就失效了,這樣即使系統調用了dismiss方法也無法關閉對話框了。
    下面讓我們回到AlertDialog的源代碼中,再繼續跟蹤到AlertDialog的父類Dialog的源代碼中。找到dismissDialog方法。實際上,dismiss方法是通過dismissDialog方法來關閉對話框的,dismissDialog方法的代碼如下:

該方法後面的代碼不用管它,先看 if(!mShowing){ … } 這段代碼。這個 mShowing 變量就是判斷對話框是否已關閉的。因此,我們在代碼中通過設置這個變量就可以使系統認爲對話框已經關閉,就不再繼續關閉對話框了。由於 mShowing 也是 private 變量,因此,也需要反射技術來設置這個變量。我們可以在對話框按鈕的單擊事件中設置 mShowing ,代碼如下:

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

將上面的代碼加到哪個按鈕的單擊事件代碼中,哪個按鈕就再也無法關閉對話框了。如果要關閉對話框,只需再將 mShowing 設爲 true 即可。要注意的是,在一個按鈕裏設置了 mShowing 變量,也會影響另一個按鈕的關閉對話框功能,因此,需要在每一個按鈕的單擊事件裏都設置 mShowing 變量的值。

本文來自CSDN博客,轉載請標明出處: http://blog.csdn.net/nokiaguy/archive/2010/07/27/5770263.aspx

發佈了146 篇原創文章 · 獲贊 7 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章