Toast信息提示框之所以在顯示一定時(shí)間后會(huì)自動(dòng)關(guān)閉,是因?yàn)樵谙到y(tǒng)中有一個(gè)Toast隊(duì)列。系統(tǒng)會(huì)依次從隊(duì)列中取(出隊(duì)列)一個(gè)Toast,并顯示它。在顯示一段時(shí)間后,再關(guān)閉,然后再顯示下一個(gè)Toast信息提示框。直到Toast隊(duì)列中所有Toast都顯示完為止。那么有些時(shí)候需要這個(gè)Toast信息提示框長(zhǎng)時(shí)間顯示,直到需要關(guān)閉它時(shí)通過(guò)代碼來(lái)控制,而不是讓系統(tǒng)自動(dòng)來(lái)關(guān)閉Toast信息提示框。不過(guò)這個(gè)要求對(duì)于Toast本身來(lái)說(shuō)有些過(guò)分,因?yàn)門(mén)oast類并沒(méi)有提供這個(gè)功能。雖然如此,但方法總比問(wèn)題多。通過(guò)一些特殊的處理還是可以實(shí)現(xiàn)這個(gè)功能的,而且并不復(fù)雜。
從7.3.1節(jié)的內(nèi)容可以知道,Toast信息提示框需要調(diào)用Toast.show方法來(lái)顯示。下面來(lái)看一下show方法的源代碼。
復(fù)制代碼 代碼如下:
publicvoidshow(){
if(mNextView==null){
thrownewRuntimeException("setViewmusthavebeencalled");
}
INotificationManagerservice=getService();
Stringpkg=mContext.getPackageName();
TNtn=mTN;
try{
//將當(dāng)前Toast加入到Toast隊(duì)列
service.enqueueToast(pkg,tn,mDuration);
}catch(RemoteExceptione){
//Empty
}
}
show方法的代碼并不復(fù)雜,可以很容易找到如下的代碼。
復(fù)制代碼 代碼如下:
service.enqueueToast(pkg,tn,mDuration);
從上面的代碼可以很容易推斷出它的功能是將當(dāng)前的Toast加入到系統(tǒng)的Toast隊(duì)列中。看到這里,各位讀者應(yīng)該想到。雖然show方法的表面功能是顯示Toast信息提示框,但其實(shí)際的功能是將Toast加入到隊(duì)列中,再由系統(tǒng)根據(jù)Toast隊(duì)列來(lái)顯示Toast信息提示框。那么我們經(jīng)過(guò)更進(jìn)一步地思考,可以大膽地做出一個(gè)初步的方案。既然系統(tǒng)的Toast隊(duì)列可以顯示Toast信息提示框,那么我們?yōu)槭裁床豢梢宰约簛?lái)顯示它呢?這樣不是可以自己來(lái)控制Toast的信息提示框的顯示和關(guān)閉了嗎!當(dāng)然,這就不能再調(diào)用show方法來(lái)顯示Toast信息提示框了(因?yàn)閟how方法會(huì)將Toast加入隊(duì)列,這樣我們就控制不了Toast了)。
既然初步方案已擬定,現(xiàn)在就來(lái)實(shí)施它。先在Toast類找一下還有沒(méi)有其他的show方法。結(jié)果發(fā)現(xiàn)了一個(gè)TN類,該類是Toast的一個(gè)內(nèi)嵌類。在TN類中有一個(gè)show方法。TN是ITransientNotification.Stub的子類。從ITransientNotification和TN類中的show方法初步推斷(因?yàn)門(mén)ransient的中文意思是“短暫的”)系統(tǒng)是從Toast隊(duì)列中獲得了Toast對(duì)象后,利用TN對(duì)象的show方法顯示Toast,再利用TN.hide方法來(lái)關(guān)閉Toast。首先聲明,這只是假設(shè),我們還不知道這么做是否可行!當(dāng)然,這也是科學(xué)研究的一般方法,先推斷或假設(shè),然后再證明推斷或假設(shè)。
現(xiàn)在關(guān)鍵的一步是獲得TN對(duì)象。遺憾的是TN被聲明成private類型,外部無(wú)法訪問(wèn)。不過(guò)別著急。在Toast類中有一個(gè)mTN變量。雖然不是public變量,但仍然可以通過(guò)反射技術(shù)訪問(wèn)該變量。mTN變量會(huì)在創(chuàng)建Toast對(duì)象時(shí)初始化。因此,只要獲得mTN變量,就獲得了TN對(duì)象。下面的代碼顯示了一個(gè)永遠(yuǎn)不會(huì)自動(dòng)關(guān)閉的Toast信息提示框。
復(fù)制代碼 代碼如下:
//先創(chuàng)建一個(gè)Toast對(duì)象
Toasttoast=Toast.makeText(this,"永不消失的Toast",Toast.LENGTH_SHORT);
//設(shè)置Toast信息提示框顯示的位置(在屏幕頂部水平居中顯示)
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL,0,0);
try
{
//從Toast對(duì)象中獲得mTN變量
Fieldfield=toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
Objectobj=field.get(toast);
//TN對(duì)象中獲得了show方法
Methodmethod=obj.getClass().getDeclaredMethod("show",null);
//調(diào)用show方法來(lái)顯示Toast信息提示框
method.invoke(obj,null);
}
catch(Exceptione)
{
}
上面的代碼中try{…}catch(…){…}語(yǔ)句中的代碼是關(guān)鍵。先利用事先創(chuàng)建好的Toast對(duì)象獲得了mTN變量。然后再利用反射技術(shù)獲得了TN對(duì)象的show方法。
關(guān)閉Toast和顯示Toast的方法類似,只是需要獲得hide方法,代碼如下:
復(fù)制代碼 代碼如下:
try
{
//需要將前面代碼中的obj變量變成類變量。這樣在多個(gè)地方就都可以訪問(wèn)了
Methodmethod=obj.getClass().getDeclaredMethod("hide",null);
method.invoke(obj,null);
}
catch(Exceptione)
{
}
上面的代碼已經(jīng)很完美地實(shí)現(xiàn)了通過(guò)代碼控制Toast信息提示框顯示和關(guān)閉的功能。但如果想實(shí)現(xiàn)得更完美,可以在AndroidSDK源代碼中找一個(gè)叫ITransientNotification.aidl的文件(該文件是AIDL服務(wù)定義文件,將在后面詳細(xì)介紹),并在Android工程的src目錄中建一個(gè)android.app包,將這個(gè)文件放到這個(gè)包中。然后ADT會(huì)自動(dòng)在gen目錄中生成了一個(gè)android.app包,包中有一個(gè)ITransientNotification.java文件。由于AndroidSDK自帶的ItransientNotification接口屬于內(nèi)部資源,外部程序無(wú)法訪問(wèn),因此,只能將從Toast對(duì)象中獲得的mTN變量轉(zhuǎn)換成剛才生成的ITransientNotification對(duì)象了。這樣就不需要使反射技術(shù)獲得show和hide方法了。經(jīng)過(guò)改良的顯示和關(guān)閉Toast信息提示框的代碼如下:
復(fù)制代碼 代碼如下:
ITransientNotificationnotification=(ITransientNotification)field.get(toast);
//顯示Toast信息提示框
notification.show();
//關(guān)閉Toast信息提示框
notification.hide();