前言
相信有些人用過MIUI,會發現小米的Toast跟Android傳統的Toast特么是不一樣的,他會從底部向上飛入,然后漸變消失。看起來效果是挺不錯的,但是對於Android原生Toast是不支持自定義動畫的。那這個效果到底是怎么實現的呢?下面就來告訴你。。。。
分析
如果園友看過我的另一篇博客《Android:剖析源碼,隨心所欲控制Toast顯示》,就會知道其實原生Toast就是infate出一個View實例,然后將其加載到WindowManager上面來達到顯示效果。我們很多人都知道WindowManager是可以實現一個懸浮在所有應用界面的視圖而不會獲取焦點,這也就是Toast所需要具備的核心的功能:簡約提示信息傳遞給用戶,而不額外執行其他操作;在這里,我們這個效果也是基於WindowManager來實現的。
正文
我們需要自定義一個Toast的類,但是不需要繼承Toast。既然是仿照着寫一個自定義Toast,那么我們就從Toast的入口開始完善這個自定義Toast。
新建一個類
public class MiuiToast { }
干干凈凈的不用去繼承其他類(除了Object.......)
開始碼代碼
我們一般使用原生Toast都是直接 Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); 其中makeText這個靜態方法會返回一個Toast實例,然后調用show方法來顯示Toast。
我們先來搞定makeText方法
public static MiuiToast MakeText(Context context, String text, boolean showTime) { MiuiToast result = new MiuiToast(context, text, showTime); return result; }
邏輯簡單粗暴,直接調用構造函數實例化一個MiuiToast對象並返回。
接下來該是MiuiToast的構造方法了
private MiuiToast(Context context, String text, boolean showTime ){ mShowTime = showTime;//記錄Toast的顯示長短類型 mIsShow = false;//記錄當前Toast的內容是否已經在顯示 mWdm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); //通過Toast實例獲取當前android系統的默認Toast的View布局 mToastView = Toast.makeText(context, text, Toast.LENGTH_SHORT).getView(); mTimer = new Timer(); //設置布局參數 setParams(); }
在構造方法中,更多的是對數據的初始化,由於設置布局參數比較多,所以單獨抽出一個函數來
瞧瞧setParams()方法
private void setParams() { mParams = new WindowManager.LayoutParams(); mParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mParams.format = PixelFormat.TRANSLUCENT; mParams.windowAnimations = R.style.anim_view;//設置進入退出動畫效果 mParams.type = WindowManager.LayoutParams.TYPE_TOAST; mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; mParams.gravity = Gravity.CENTER_HORIZONTAL; mParams.y = 250; }
這個設置參數更多是參考源代碼中原生Toast的設置參數的類型,在這里我們需要注意的是 mParams.windowAnimations = R.style.anim_view;//設置進入退出動畫效果,這個使我們這個仿MIUI的Toast動畫實現的基石。其他參數也沒什么好講的,谷歌就是這么設置他們的Toast的哈,接下來我們得看看動畫的配置XML。
這個R.style.anim_view的情況呢
<resources> <style name="anim_view"> <item name="@android:windowEnterAnimation">@anim/anim_in</item> <item name="@android:windowExitAnimation">@anim/anim_out</item> </style> </resources>
這里定義了一個進入動畫和退出動畫的引用,接下來就是我們設置動畫效果的時刻了!
anim_in:Toast的進入動畫
<set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="0" android:toYDelta="85" android:duration="1" /> <translate android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="0" android:toYDelta="-105" android:duration="350" android:fillAfter="true" android:interpolator="@android:anim/decelerate_interpolator" /> <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="100" /> <translate android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="0" android:toYDelta="20" android:duration="80" android:fillAfter="true" android:startOffset="350" /> </set>
在這里我配置了四個動畫效果,如果大家要重新設置新的動畫效果,盡可以在這里面修改哈。粗略的說幾句,由於在加載動畫的前后,WindowManager就通過mParams來確定Toast的顯示位置,所以第一個translate作用是讓Toast在開始載入的時候跳轉到進入的位置,而其他的動畫就是完成Toast從下端飛入的效果而已,這期間利用了android:startOffset進行時間的控制達到動畫的銜接效果。
anim_out:Toast退出動畫
<set xmlns:android="http://schemas.android.com/apk/res/android"> <alpha android:fromAlpha="1" android:toAlpha="0" android:duration="800"/> </set>
簡簡單單的一個淡出動畫。。。。
然后我們來看下show()方法吧
public void show(){ if(!mIsShow){//如果Toast沒有顯示,則開始加載顯示 mIsShow = true; mWdm.addView(mToastView, mParams);//將其加載到windowManager上 mTimer.schedule(new TimerTask() { @Override public void run() { mWdm.removeView(mToastView); mIsShow = false; } }, (long)(mShowTime ? 3500 : 2000)); } }
在show方法中我們會對mIsShow 判斷當前的Toast是否已經在顯示,如果正在顯示我們沒理由相信我們會那么SB得再去顯示他一次。。
mWdm.addView(mToastView, mParams);將View加載到WindowManager上面,達到類似的懸浮效果,啟動定時器,到達指定的時間后將其移除,整個邏輯就是這樣了。
因為——Toast!就是這么簡單!
給大家瞄瞄效果圖
源碼請戳這里(點我吧)
作者:enjoy風鈴
出處:http://www.cnblogs.com/net168/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則下次不給你轉載了。