天天看點

android 永不關閉toast

Toast資訊提示框之是以在顯示一定時間後會自動關閉,是因為在系統中有一個Toast隊列;那麼有些時候需要這個Toast資訊提示框長時間顯示,直到需要關閉它時通過代碼來控制,而不是讓系統自動來關閉Toast資訊提示框

Toast資訊提示框之是以在顯示一定時間後會自動關閉,是因為在系統中有一個Toast隊列。系統會依次從隊列中取(出隊列)一個Toast,并顯示它。在顯示一段時間後,再關閉,然後再顯示下一個Toast資訊提示框。直到Toast隊列中所有Toast都顯示完為止。那麼有些時候需要這個Toast資訊提示框長時間顯示,直到需要關閉它時通過代碼來控制,而不是讓系統自動來關閉Toast資訊提示框。不過這個要求對于Toast本身來說有些過分,因為Toast類并沒有提供這個功能。雖然如此,但方法總比問題多。通過一些特殊的處理還是可以實作這個功能的,而且并不複雜。 

Toast資訊提示框需要調用Toast.show方法來顯示。下面來看一下show方法的源代碼。 

publicvoidshow(){ 
if(mNextView==null){ 
thrownewRuntimeException("setViewmusthavebeencalled"); 
} 
INotificationManagerservice=getService(); 
Stringpkg=mContext.getPackageName(); 
TNtn=mTN; 
try{ 
//将目前Toast加入到Toast隊列 
service.enqueueToast(pkg,tn,mDuration); 
}catch(RemoteExceptione){ 
//Empty 
} 
} 
           

  show方法的代碼并不複雜,可以很容易找到如下的代碼。 

service.enqueueToast(pkg,tn,mDuration); 

從上面的代碼可以很容易推斷出它的功能是将目前的Toast加入到系統的Toast隊列中。看到這裡,各位讀者應該想到。雖然show方法的表面功能是顯示Toast資訊提示框,但其實際的功能是将Toast加入到隊列中,再由系統根據Toast隊列來顯示Toast資訊提示框。那麼我們經過更進一步地思考,可以大膽地做出一個初步的方案。既然系統的Toast隊列可以顯示Toast資訊提示框,那麼我們為什麼不可以自己來顯示它呢?這樣不是可以自己來控制Toast的資訊提示框的顯示和關閉了嗎!當然,這就不能再調用show方法來顯示Toast資訊提示框了(因為show方法會将Toast加入隊列,這樣我們就控制不了Toast了)。

既然初步方案已拟定,現在就來實施它。先在Toast類找一下還有沒有其他的show方法。結果發現了一個TN類,該類是Toast的一個内嵌類。在TN類中有一個show方法。TN是ITransientNotification.Stub的子類。從ITransientNotification和TN類中的show方法初步推斷(因為Transient的中文意思是“短暫的”)系統是從Toast隊列中獲得了Toast對象後,利用TN對象的show方法顯示Toast,再利用TN.hide方法來關閉Toast。首先聲明,這隻是假設,我們還不知道這麼做是否可行!當然,這也是科學研究的一般方法,先推斷或假設,然後再證明推斷或假設。

現在關鍵的一步是獲得TN對象。遺憾的是TN被聲明成private類型,外部無法通路。不過别着急。在Toast類中有一個mTN變量。雖然不是public變量,但仍然可以通過反射技術通路該變量。mTN變量會在建立Toast對象時初始化。是以,隻要獲得mTN變量,就獲得了TN對象。下面的代碼顯示了一個永遠不會自動關閉的Toast資訊提示框。 

//先建立一個Toast對象 
Toasttoast=Toast.makeText(this,"永不消失的Toast",Toast.LENGTH_SHORT); 
//設定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); 
//調用show方法來顯示Toast資訊提示框 
method.invoke(obj,null); 
} 
catch(Exceptione) 
{ 
} 
           

  

上面的代碼中try{…}catch(…){…}語句中的代碼是關鍵。先利用事先建立好的Toast對象獲得了mTN變量。然後再利用反射技術獲得了TN對象的show方法。 

關閉Toast和顯示Toast的方法類似,隻是需要獲得hide方法,代碼如下: 

try 
{ 
//需要将前面代碼中的obj變量變成類變量。這樣在多個地方就都可以通路了 
Methodmethod=obj.getClass().getDeclaredMethod("hide",null); 
method.invoke(obj,null); 
} 
catch(Exceptione) 
{ 
}       

上面的代碼已經很完美地實作了通過代碼控制Toast資訊提示框顯示和關閉的功能。但如果想實作得更完美,可以在AndroidSDK源代碼中找一個叫ITransientNotification.aidl的檔案(該檔案是AIDL服務定義檔案,将在後面詳細介紹),并在Android工程的src目錄中建一個android.app包,将這個檔案放到這個包中。然後ADT會自動在gen目錄中生成了一個android.app包,包中有一個ITransientNotification.java檔案。由于AndroidSDK自帶的ItransientNotification接口屬于内部資源,外部程式無法通路,是以,隻能将從Toast對象中獲得的mTN變量轉換成剛才生成的ITransientNotification對象了。這樣就不需要使反射技術獲得show和hide方法了。經過改良的顯示和關閉Toast資訊提示框的代碼如下:

ITransientNotificationnotification=(ITransientNotification)field.get(toast); 
//顯示Toast資訊提示框 
notification.show(); 
//關閉Toast資訊提示框 
notification.hide();