ZNDS智能電視網(wǎng) 推薦當(dāng)貝市場(chǎng)

TV應(yīng)用下載 / 資源分享區(qū)

軟件下載 | 游戲 | 討論 | 電視計(jì)算器

綜合交流 / 評(píng)測(cè) / 活動(dòng)區(qū)

交流區(qū) | 測(cè)硬件 | 網(wǎng)站活動(dòng) | Z幣中心

新手入門(mén) / 進(jìn)階 / 社區(qū)互助

新手 | 你問(wèn)我答 | 免費(fèi)刷機(jī)救磚 | ROM固件

查看: 13022|回復(fù): 0
上一主題 下一主題
[教程]

Android 利用Java反射技術(shù)阻止通過(guò)按鈕關(guān)閉對(duì)話(huà)框

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
發(fā)表于 2013-8-28 16:27 | 只看該作者 回帖獎(jiǎng)勵(lì) |倒序?yàn)g覽 |閱讀模式
7現(xiàn)在我們來(lái)看看第一個(gè)需求:如果某個(gè)應(yīng)用需要彈出一個(gè)對(duì)話(huà)框。當(dāng)單擊“確定“按鈕時(shí)完成某些工作,如果這些工作失敗,對(duì)話(huà)框不能關(guān)閉。而當(dāng)成功完成工作后,則關(guān)閉對(duì)話(huà)框。當(dāng)然,無(wú)論何程度情況,單擊“取消”按鈕都會(huì)關(guān)閉對(duì)話(huà)框。   
   
       這個(gè)需求并不復(fù)雜,也并不過(guò)分(雖然我們可以自己弄個(gè)來(lái)完成這個(gè)工作,也可在View上自己放按鈕,但這顯示有些大炮打蚊子了,如果對(duì)話(huà)框上只有一行文本,費(fèi)這么多勁太不值了)。但使用過(guò)AlertDialog的讀者都知道,無(wú)論單擊的哪個(gè)按鈕,無(wú)論按鈕單擊事件的執(zhí)行情況如何,對(duì)話(huà)框是肯定要關(guān)閉的。也就是說(shuō),用戶(hù)無(wú)法控制對(duì)話(huà)框的關(guān)閉動(dòng)作。實(shí)際上,關(guān)閉對(duì)話(huà)框的動(dòng)作已經(jīng)在Android SDK寫(xiě)死了,并且未給使用者留有任何接口。但我的座右銘是“宇宙中沒(méi)有什么是不能控制的”。   
   
        既然要控制對(duì)放框的關(guān)閉行為,首先就得分析是哪些類(lèi)、哪些代碼使這個(gè)對(duì)話(huà)框關(guān)閉的。進(jìn)入AlertDialog類(lèi)的源代碼。在AlertDialog中只定義了一個(gè)變量:mAlert。這個(gè)變量是類(lèi)型。類(lèi)是Android的內(nèi)部類(lèi),在包中,無(wú)法通過(guò)普通的方式訪(fǎng)問(wèn)。也無(wú)法在Eclipse中通過(guò)按Ctrl鍵跟蹤進(jìn)源代碼。但可以直接在Android源代碼中找到AlertController.java。我們?cè)倩氐紸lertDialog類(lèi)中。AlertDialog類(lèi)實(shí)際上只是一個(gè)架子。象設(shè)置按鈕、設(shè)置標(biāo)題等工作都是由AlertController類(lèi)完成的。因此,AlertController類(lèi)才是關(guān)鍵。   
   
       找到文件。打開(kāi)后不要感到頭暈哦,這個(gè)文件中的代碼是很多地。不過(guò)這么多代碼對(duì)本文的主題也沒(méi)什么用處。下面就找一下控制按鈕的代碼。   
   
       在類(lèi)的開(kāi)頭就會(huì)看到如下的代碼:   
   
   
  1. 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ù)制代碼
從這段代碼中可以猜出來(lái),前幾行代碼用來(lái)觸發(fā)對(duì)話(huà)框中的三個(gè)按鈕( Positive 、 Negative 和 Neutral )的單擊事件,而最后的代碼則用來(lái)關(guān)閉對(duì)話(huà)框   
   
   
   
  1. mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)   
       
    .sendToTarget();
復(fù)制代碼
上面的代碼并不是直接來(lái)關(guān)閉對(duì)話(huà)框的,而是通過(guò)一個(gè) Handler 來(lái)處理,代碼如下:   
   
   
  1. 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();   
       
    }   
       
    }   
       
    }   
復(fù)制代碼
從上面代碼的最后可以找到現(xiàn)在看了這么多源代碼,我們來(lái)總結(jié)一下對(duì)話(huà)框按鈕單擊事件的處理過(guò)程。在AlertController處理對(duì)話(huà)框按鈕時(shí)會(huì)為每一個(gè)按鈕添加一個(gè)。而這個(gè)事件類(lèi)的對(duì)象實(shí)例就是上面的。在這個(gè)單擊事件中首先會(huì)通過(guò)發(fā)送消息的方式調(diào)用為按鈕設(shè)置的單擊事件(也就是通過(guò)等方法的第二個(gè)參數(shù)設(shè)置的單擊事件),在觸發(fā)完按鈕的單擊事件后,會(huì)通過(guò)發(fā)送消息的方式調(diào)用dismiss方法來(lái)關(guān)閉對(duì)話(huà)框。而在AlertController類(lèi)中定義了一個(gè)全局的mHandler變量。在AlertController類(lèi)中通過(guò)ButtonHandler類(lèi)來(lái)對(duì)象來(lái)為mHandler賦值。因此,我們只要使用我們自己Handler對(duì)象替換就可以阻止調(diào)用dismiss方法來(lái)關(guān)閉對(duì)話(huà)框。下面先在自己的程序中建立一個(gè)新的ButtonHandler類(lèi)。   
   
   
  1. 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 ;   
       
    }   
       
    }   
       
    }   
復(fù)制代碼
我們可以看到,上面的類(lèi)和AlertController中的ButtonHandler類(lèi)很像,只是支掉了switch語(yǔ)句的最后一個(gè)case子句(用于調(diào)用dismiss方法)和相關(guān)的代碼。   
   
       下面我們就要為AlertController中的mHandler重新賦值。由于mHandler是private變量,因此,在這里需要使用Java的反射技術(shù)來(lái)為mHandler賦值。由于在AlertDialog類(lèi)中的mAlert變量同樣也是private,因此,也需要使用同樣的反射技術(shù)來(lái)獲得mAlert變量。代碼如下:   
   
       先建立一個(gè) AlertDialog 對(duì)象   
   
   
  1. 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()
復(fù)制代碼
上面的對(duì)話(huà)框很普通,單擊哪個(gè)按鈕都會(huì)關(guān)閉對(duì)話(huà)框。下面在調(diào)用 show 方法之前來(lái)修改一個(gè) mHandler 變量的值, OK ,下面我們就來(lái)見(jiàn)證奇跡的時(shí)刻。   
   
   
  1. 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類(lèi)   
    field.set(obj, new ButtonHandler(alertDialog1));   
       
    }   
       
    catch (Exception e)   
       
    {   
       
    }   
       
    // 顯示對(duì)話(huà)框   
    alertDialog.show();   
復(fù)制代碼
我們發(fā)現(xiàn),如果加上try   catch語(yǔ)句,單擊對(duì)話(huà)框中的確定按鈕不會(huì)關(guān)閉對(duì)話(huà)框(除非在代碼中調(diào)用dismiss方法),單擊取消按鈕則會(huì)關(guān)閉對(duì)話(huà)框(因?yàn)檎{(diào)用了方法)。如果去了try…catch代碼段,對(duì)話(huà)框又會(huì)恢復(fù)正常了。   
      
       雖然上面的代碼已經(jīng)解決了問(wèn)題,但需要編寫(xiě)的代碼仍然比較多,為此,我們也可采用另外一種方法來(lái)阻止關(guān)閉對(duì)話(huà)框。這種方法不需要定義任何的類(lèi)。   
   
       這種方法需要用點(diǎn)技巧。由于系統(tǒng)通過(guò)調(diào)用來(lái)關(guān)閉對(duì)話(huà)框,那么我們可以在dismiss方法上做點(diǎn)文章。在系統(tǒng)調(diào)用dismiss方法時(shí)會(huì)首先判斷對(duì)話(huà)框是否已經(jīng)關(guān)閉,如果對(duì)話(huà)框已經(jīng)關(guān)閉了,就會(huì)退出dismiss方法而不再繼續(xù)關(guān)閉對(duì)話(huà)框了。因此,我們可以欺騙一下系統(tǒng),當(dāng)調(diào)用dismiss方法時(shí)我們可以讓系統(tǒng)以為對(duì)話(huà)框已經(jīng)關(guān)閉(雖然對(duì)話(huà)框還沒(méi)有關(guān)閉),這樣dismiss方法就失效了,這樣即使系統(tǒng)調(diào)用了dismiss方法也無(wú)法關(guān)閉對(duì)話(huà)框了。   
   
       下面讓我們回到的源代碼中,再繼續(xù)跟蹤到AlertDialog的父類(lèi)Dialog的源代碼中。找到方法。實(shí)際上,方法是通過(guò)方法來(lái)關(guān)閉對(duì)話(huà)框的,dismissDialog方法的代碼如下:   
   
   
  1. 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();   
       
    }   
復(fù)制代碼
該方法后面的代碼不用管它,先看 if(!mShowing){ … } 這段代碼。這個(gè) mShowing 變量就是判斷對(duì)話(huà)框是否已關(guān)閉的。因此,我們?cè)诖a中通過(guò)設(shè)置這個(gè)變量就可以使系統(tǒng)認(rèn)為對(duì)話(huà)框已經(jīng)關(guān)閉,就不再繼續(xù)關(guān)閉對(duì)話(huà)框了。由于 mShowing 也是 private 變量,因此,也需要反射技術(shù)來(lái)設(shè)置這個(gè)變量。我們可以在對(duì)話(huà)框按鈕的單擊事件中設(shè)置 mShowing ,代碼如下:   
   
   
  1. try   
    {   
       
    Field field = dialog.getClass()   
       
    .getSuperclass().getDeclaredField(   
       
    " mShowing " );   
       
    field.setAccessible( true );   
       
    // 將mShowing變量設(shè)為false,表示對(duì)話(huà)框已關(guān)閉   
    field.set(dialog, false );   
       
    dialog.dismiss();   
       
       
    }   
       
    catch (Exception e)   
       
    {   
       
       
    }   
復(fù)制代碼
將上面的代碼加到哪個(gè)按鈕的單擊事件代碼中,哪個(gè)按鈕就再也無(wú)法關(guān)閉對(duì)話(huà)框了。如果要關(guān)閉對(duì)話(huà)框,只需再將 mShowing 設(shè)為 true 即可。要注意的是,在一個(gè)按鈕里設(shè)置了 mShowing 變量,也會(huì)影響另一個(gè)按鈕的關(guān)閉對(duì)話(huà)框功能,因此,需要在每一個(gè)按鈕的單擊事件里都設(shè)置 mShowing 變量的值。   

上一篇:Android游戲開(kāi)發(fā)之單點(diǎn)觸摸與多點(diǎn)觸摸的響應(yīng)方式(二十三)
下一篇:學(xué)習(xí)Android界面設(shè)計(jì)的超級(jí)利器HierarchyView.bat

本版積分規(guī)則

Archiver|新帖|標(biāo)簽|軟件|Sitemap|ZNDS智能電視網(wǎng) ( 蘇ICP備2023012627號(hào) )

網(wǎng)絡(luò)信息服務(wù)信用承諾書(shū) | 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證:蘇B2-20221768 丨 蘇公網(wǎng)安備 32011402011373號(hào)

GMT+8, 2025-1-30 11:08 , Processed in 0.059989 second(s), 13 queries , Redis On.

Powered by Discuz!

監(jiān)督舉報(bào):report#znds.com (請(qǐng)將#替換為@)

© 2007-2025 ZNDS.Com

快速回復(fù) 返回頂部 返回列表