上篇我讲了自定义Toast和不重复显示Toast,后来我在想官方Toast只有一个淡入淡出的,要自定义Taost动画该怎样做了。
首先来看Toast的源码:
构造函数如下:
public Toast(Context context) { mContext = context; mTN = new TN(); mTN.mY = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.toast_y_offset); mTN.mGravity = context.getResources().getInteger( com.android.internal.R.integer.config_toastDefaultGravity); }
大家看到没有这里有一个 TN 的对象mTN,接着来看看TN这个类
private static class TN extends ITransientNotification.Stub { final Runnable mShow = new Runnable() { @Override public void run() { handleShow(); } }; final Runnable mHide = new Runnable() { @Override public void run() { handleHide(); // Don't do this in handleHide() because it is also invoked by handleShow() mNextView = null; } }; private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); final Handler mHandler = new Handler(); int mGravity; int mX, mY; float mHorizontalMargin; float mVerticalMargin; View mView; View mNextView; WindowManager mWM; TN() { // XXX This should be changed to use a Dialog, with a Theme.Toast // defined that sets up the layout params appropriately. final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; } /** * schedule handleShow into the right thread */ @Override public void show() { if (localLOGV) Log.v(TAG, "SHOW: " + this); mHandler.post(mShow); } /** * schedule handleHide into the right thread */ @Override public void hide() { if (localLOGV) Log.v(TAG, "HIDE: " + this); mHandler.post(mHide); } public void handleShow() { } private void trySendAccessibilityEvent() {} public void handleHide() {} } }
代码很多这里删掉了几个函数的代码,大家主要看这几个属性
int mGravity; //控制Toast的显示的方式居上居中还是居下等 int mX, mY; //Toast x y的偏移量 float mHorizontalMargin; float mVerticalMargin;
大家注意到没有实际上Toast的所有控制的属性都是封装在这个内部类中,外部类也就是Toast类只是去设置这个类的属性。大家来看
public void setGravity(int gravity, int xOffset, int yOffset) { mTN.mGravity = gravity; mTN.mX = xOffset; mTN.mY = yOffset; }
上面的函数是设置Toast显示的位置我相信我们也经常调用,实际上这都是设置TN这个类的属性。下面我们来找找我们需要的动画属性:
final WindowManager.LayoutParams params = mParams; params.windowAnimations = com.android.internal.R.style.Animation_Toast;
看到没有在这里,但是问题来了Toast没有暴露出我们可以动态设置此属性的方法,并且此类是mTn对象是defualt类型的变量也就是说我们应用想要直接通过mTN对象来设置params的windowAnimations的属性是不可能的。那这样我们岂不是束手无策了,显然不是我们还有一个java给我们的强大的反射机制。下面我们来看看该怎样处理:
import java.lang.reflect.Field; import android.content.Context; import android.view.WindowManager; import android.widget.Toast; /** * 自定义动画的Toast * @author ziyao * */ public class MyToast extends Toast { public MyToast(Context context) { super(context); } /** * 调用有动画的Toast * @param context * @param text * @param duration * @param 自定义的动画id * @return */ public static Toast makeTextAnim(Context context, CharSequence text, int duration,int styleId) { Toast toast = makeText(context, text, duration); toast.setText(text); toast.setDuration(duration); try { Object mTN = null; mTN = getField(toast, "mTN"); if (mTN != null) { Object mParams = getField(mTN, "mParams"); if (mParams != null && mParams instanceof WindowManager.LayoutParams) { WindowManager.LayoutParams params = (WindowManager.LayoutParams) mParams; params.windowAnimations = styleId; } } } catch (Exception e) { e.printStackTrace(); } return toast; } /** * 反射字段 * @param object 要反射的对象 * @param fieldName 要反射的字段名称 * @return * @throws NoSuchFieldException * @throws IllegalAccessException */ private static Object getField(Object object, String fieldName) throws NoSuchFieldException, IllegalAccessException { Field field = object.getClass().getDeclaredField(fieldName); if (field != null) { field.setAccessible(true); return field.get(object); } return null; } }
首先通过反射机制获得Toast中的mTN对象然后再反射得到mTN中的mParams属性最后设置mParams.windowAnimations属性
Object mTN = null; mTN = getField(toast, "mTN");
Object mParams = getField(mTN, "mParams");
params.windowAnimations = styleId;
到此Toast自定义动画就算完成了,当然也有其他的方法,可以将Toast的源码整理,自己写一个Toast也是可以的。