Android開發(fā)技巧之永不關(guān)閉的Toast信息框(長時(shí)間顯示而非系統(tǒng)關(guān)閉)
更新時(shí)間:2013年01月22日 10:26:07 作者:
Toast信息提示框之所以在顯示一定時(shí)間后會自動關(guān)閉,是因?yàn)樵谙到y(tǒng)中有一個(gè)Toast隊(duì)列;那么有些時(shí)候需要這個(gè)Toast信息提示框長時(shí)間顯示,直到需要關(guān)閉它時(shí)通過代碼來控制,而不是讓系統(tǒng)自動來關(guān)閉Toast信息提示框
Toast信息提示框之所以在顯示一定時(shí)間后會自動關(guān)閉,是因?yàn)樵谙到y(tǒng)中有一個(gè)Toast隊(duì)列。系統(tǒng)會依次從隊(duì)列中?。ǔ鲫?duì)列)一個(gè)Toast,并顯示它。在顯示一段時(shí)間后,再關(guān)閉,然后再顯示下一個(gè)Toast信息提示框。直到Toast隊(duì)列中所有Toast都顯示完為止。那么有些時(shí)候需要這個(gè)Toast信息提示框長時(shí)間顯示,直到需要關(guān)閉它時(shí)通過代碼來控制,而不是讓系統(tǒng)自動來關(guān)閉Toast信息提示框。不過這個(gè)要求對于Toast本身來說有些過分,因?yàn)門oast類并沒有提供這個(gè)功能。雖然如此,但方法總比問題多。通過一些特殊的處理還是可以實(shí)現(xiàn)這個(gè)功能的,而且并不復(fù)雜。
從7.3.1節(jié)的內(nèi)容可以知道,Toast信息提示框需要調(diào)用Toast.show方法來顯示。下面來看一下show方法的源代碼。
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ù)雜,可以很容易找到如下的代碼。
service.enqueueToast(pkg,tn,mDuration);
從上面的代碼可以很容易推斷出它的功能是將當(dāng)前的Toast加入到系統(tǒng)的Toast隊(duì)列中??吹竭@里,各位讀者應(yīng)該想到。雖然show方法的表面功能是顯示Toast信息提示框,但其實(shí)際的功能是將Toast加入到隊(duì)列中,再由系統(tǒng)根據(jù)Toast隊(duì)列來顯示Toast信息提示框。那么我們經(jīng)過更進(jìn)一步地思考,可以大膽地做出一個(gè)初步的方案。既然系統(tǒng)的Toast隊(duì)列可以顯示Toast信息提示框,那么我們?yōu)槭裁床豢梢宰约簛盹@示它呢?這樣不是可以自己來控制Toast的信息提示框的顯示和關(guān)閉了嗎!當(dāng)然,這就不能再調(diào)用show方法來顯示Toast信息提示框了(因?yàn)閟how方法會將Toast加入隊(duì)列,這樣我們就控制不了Toast了)。
既然初步方案已擬定,現(xiàn)在就來實(shí)施它。先在Toast類找一下還有沒有其他的show方法。結(jié)果發(fā)現(xiàn)了一個(gè)TN類,該類是Toast的一個(gè)內(nèi)嵌類。在TN類中有一個(gè)show方法。TN是ITransientNotification.Stub的子類。從ITransientNotification和TN類中的show方法初步推斷(因?yàn)門ransient的中文意思是“短暫的”)系統(tǒng)是從Toast隊(duì)列中獲得了Toast對象后,利用TN對象的show方法顯示Toast,再利用TN.hide方法來關(guān)閉Toast。首先聲明,這只是假設(shè),我們還不知道這么做是否可行!當(dāng)然,這也是科學(xué)研究的一般方法,先推斷或假設(shè),然后再證明推斷或假設(shè)。
現(xiàn)在關(guān)鍵的一步是獲得TN對象。遺憾的是TN被聲明成private類型,外部無法訪問。不過別著急。在Toast類中有一個(gè)mTN變量。雖然不是public變量,但仍然可以通過反射技術(shù)訪問該變量。mTN變量會在創(chuàng)建Toast對象時(shí)初始化。因此,只要獲得mTN變量,就獲得了TN對象。下面的代碼顯示了一個(gè)永遠(yuǎn)不會自動關(guān)閉的Toast信息提示框。
//先創(chuàng)建一個(gè)Toast對象
Toasttoast=Toast.makeText(this,"永不消失的Toast",Toast.LENGTH_SHORT);
//設(shè)置Toast信息提示框顯示的位置(在屏幕頂部水平居中顯示)
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL,0,0);
try
{
//從Toast對象中獲得mTN變量
Fieldfield=toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
Objectobj=field.get(toast);
//TN對象中獲得了show方法
Methodmethod=obj.getClass().getDeclaredMethod("show",null);
//調(diào)用show方法來顯示Toast信息提示框
method.invoke(obj,null);
}
catch(Exceptione)
{
}
上面的代碼中try{…}catch(…){…}語句中的代碼是關(guān)鍵。先利用事先創(chuàng)建好的Toast對象獲得了mTN變量。然后再利用反射技術(shù)獲得了TN對象的show方法。
關(guān)閉Toast和顯示Toast的方法類似,只是需要獲得hide方法,代碼如下:
try
{
//需要將前面代碼中的obj變量變成類變量。這樣在多個(gè)地方就都可以訪問了
Methodmethod=obj.getClass().getDeclaredMethod("hide",null);
method.invoke(obj,null);
}
catch(Exceptione)
{
}
上面的代碼已經(jīng)很完美地實(shí)現(xiàn)了通過代碼控制Toast信息提示框顯示和關(guān)閉的功能。但如果想實(shí)現(xiàn)得更完美,可以在AndroidSDK源代碼中找一個(gè)叫ITransientNotification.aidl的文件(該文件是AIDL服務(wù)定義文件,將在后面詳細(xì)介紹),并在Android工程的src目錄中建一個(gè)android.app包,將這個(gè)文件放到這個(gè)包中。然后ADT會自動在gen目錄中生成了一個(gè)android.app包,包中有一個(gè)ITransientNotification.java文件。由于AndroidSDK自帶的ItransientNotification接口屬于內(nèi)部資源,外部程序無法訪問,因此,只能將從Toast對象中獲得的mTN變量轉(zhuǎn)換成剛才生成的ITransientNotification對象了。這樣就不需要使反射技術(shù)獲得show和hide方法了。經(jīng)過改良的顯示和關(guān)閉Toast信息提示框的代碼如下:
ITransientNotificationnotification=(ITransientNotification)field.get(toast);
//顯示Toast信息提示框
notification.show();
//關(guān)閉Toast信息提示框
notification.hide();
從7.3.1節(jié)的內(nèi)容可以知道,Toast信息提示框需要調(diào)用Toast.show方法來顯示。下面來看一下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ì)列來顯示Toast信息提示框。那么我們經(jīng)過更進(jìn)一步地思考,可以大膽地做出一個(gè)初步的方案。既然系統(tǒng)的Toast隊(duì)列可以顯示Toast信息提示框,那么我們?yōu)槭裁床豢梢宰约簛盹@示它呢?這樣不是可以自己來控制Toast的信息提示框的顯示和關(guān)閉了嗎!當(dāng)然,這就不能再調(diào)用show方法來顯示Toast信息提示框了(因?yàn)閟how方法會將Toast加入隊(duì)列,這樣我們就控制不了Toast了)。
既然初步方案已擬定,現(xiàn)在就來實(shí)施它。先在Toast類找一下還有沒有其他的show方法。結(jié)果發(fā)現(xiàn)了一個(gè)TN類,該類是Toast的一個(gè)內(nèi)嵌類。在TN類中有一個(gè)show方法。TN是ITransientNotification.Stub的子類。從ITransientNotification和TN類中的show方法初步推斷(因?yàn)門ransient的中文意思是“短暫的”)系統(tǒng)是從Toast隊(duì)列中獲得了Toast對象后,利用TN對象的show方法顯示Toast,再利用TN.hide方法來關(guān)閉Toast。首先聲明,這只是假設(shè),我們還不知道這么做是否可行!當(dāng)然,這也是科學(xué)研究的一般方法,先推斷或假設(shè),然后再證明推斷或假設(shè)。
現(xiàn)在關(guān)鍵的一步是獲得TN對象。遺憾的是TN被聲明成private類型,外部無法訪問。不過別著急。在Toast類中有一個(gè)mTN變量。雖然不是public變量,但仍然可以通過反射技術(shù)訪問該變量。mTN變量會在創(chuàng)建Toast對象時(shí)初始化。因此,只要獲得mTN變量,就獲得了TN對象。下面的代碼顯示了一個(gè)永遠(yuǎn)不會自動關(guān)閉的Toast信息提示框。
復(fù)制代碼 代碼如下:
//先創(chuàng)建一個(gè)Toast對象
Toasttoast=Toast.makeText(this,"永不消失的Toast",Toast.LENGTH_SHORT);
//設(shè)置Toast信息提示框顯示的位置(在屏幕頂部水平居中顯示)
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL,0,0);
try
{
//從Toast對象中獲得mTN變量
Fieldfield=toast.getClass().getDeclaredField("mTN");
field.setAccessible(true);
Objectobj=field.get(toast);
//TN對象中獲得了show方法
Methodmethod=obj.getClass().getDeclaredMethod("show",null);
//調(diào)用show方法來顯示Toast信息提示框
method.invoke(obj,null);
}
catch(Exceptione)
{
}
上面的代碼中try{…}catch(…){…}語句中的代碼是關(guān)鍵。先利用事先創(chuàng)建好的Toast對象獲得了mTN變量。然后再利用反射技術(shù)獲得了TN對象的show方法。
關(guān)閉Toast和顯示Toast的方法類似,只是需要獲得hide方法,代碼如下:
復(fù)制代碼 代碼如下:
try
{
//需要將前面代碼中的obj變量變成類變量。這樣在多個(gè)地方就都可以訪問了
Methodmethod=obj.getClass().getDeclaredMethod("hide",null);
method.invoke(obj,null);
}
catch(Exceptione)
{
}
上面的代碼已經(jīng)很完美地實(shí)現(xiàn)了通過代碼控制Toast信息提示框顯示和關(guān)閉的功能。但如果想實(shí)現(xiàn)得更完美,可以在AndroidSDK源代碼中找一個(gè)叫ITransientNotification.aidl的文件(該文件是AIDL服務(wù)定義文件,將在后面詳細(xì)介紹),并在Android工程的src目錄中建一個(gè)android.app包,將這個(gè)文件放到這個(gè)包中。然后ADT會自動在gen目錄中生成了一個(gè)android.app包,包中有一個(gè)ITransientNotification.java文件。由于AndroidSDK自帶的ItransientNotification接口屬于內(nèi)部資源,外部程序無法訪問,因此,只能將從Toast對象中獲得的mTN變量轉(zhuǎn)換成剛才生成的ITransientNotification對象了。這樣就不需要使反射技術(shù)獲得show和hide方法了。經(jīng)過改良的顯示和關(guān)閉Toast信息提示框的代碼如下:
復(fù)制代碼 代碼如下:
ITransientNotificationnotification=(ITransientNotification)field.get(toast);
//顯示Toast信息提示框
notification.show();
//關(guān)閉Toast信息提示框
notification.hide();
您可能感興趣的文章:
- Android開發(fā)獲取當(dāng)前系統(tǒng)日期和時(shí)間功能示例
- Android動態(tài)顯示當(dāng)前年月日時(shí)分秒系統(tǒng)時(shí)間(示例代碼)
- Android 用Time和Calendar獲取系統(tǒng)當(dāng)前時(shí)間源碼分享(年月日時(shí)分秒周幾)
- Android獲取系統(tǒng)時(shí)間的多種方法
- Android獲取系統(tǒng)時(shí)間以及網(wǎng)絡(luò)時(shí)間
- Android調(diào)用系統(tǒng)時(shí)間格式顯示時(shí)間信息
- 解析android中系統(tǒng)日期時(shí)間的獲取
- Android實(shí)現(xiàn)顯示系統(tǒng)實(shí)時(shí)時(shí)間
相關(guān)文章
Phonegap使用拍照功能時(shí)的內(nèi)存問題
最近幾天在學(xué)習(xí)使用phonegap進(jìn)行android應(yīng)用的開發(fā),網(wǎng)上的資料比較亂,個(gè)人參考了很多資料,也試驗(yàn)了很多次,一直在摸索,總算小有心得,這此過程中也遇到了一些問題,這里給大家分享下解決Phonegap使用拍照功能時(shí)的內(nèi)存問題的方法,這里簡單的整理一下2015-05-05
Android中EditText+Button組合導(dǎo)致輸入板無法收起的原因分析及解決辦法
這篇文章主要介紹了Android中EditText+Button組合導(dǎo)致輸入板無法收起的原因分析及解決辦法的相關(guān)資料,需要的朋友可以參考下
2016-01-01
Android圓形頭像拍照后“無法加載此圖片”的問題解決方法(適配Android7.0)
這篇文章主要介紹了Android圓形頭像拍照后“無法加載此圖片”的問題解決方法(適配Android7.0) ,需要的朋友可以參考下
2017-10-10
Flutter進(jìn)階之實(shí)現(xiàn)動畫效果(三)
這篇文章主要為大家詳細(xì)介紹了Flutter進(jìn)階之實(shí)現(xiàn)動畫效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
2018-08-08
解決Android SearchView不顯示搜索icon的問題
這篇文章主要介紹了解決Android SearchView不顯示搜索icon問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
2019-05-05
flutter ExpansionTile 層級菜單的實(shí)現(xiàn)
這篇文章主要介紹了flutter ExpansionTile 層級菜單的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
2019-07-07 
