一、引言
在开发的过程中你会发现Android自身的Toast提示有许多限制,比如我想自定义Toast的动画、自定义一个美观的View显示在Toast中、更多的是让Toast显示指定的时长等等。
首先一下效果如何:
二、原理
自定义的原理也很简单,就是给WindowManager添加View和删除View,不过需要设置WindowManager.LayoutParams和View的样式,使其看起来和Android系统的Toast看起来很相像。
具体代码如下:
/**
* Custom Toast
*
* @author Lucky
*
*/
public class ToastHelper {
public static final int LENGTH_LONG = 3500;
public static final int LENGTH_SHORT = 2000;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowParams;
private View toastView;
private Context mContext;
private Handler mHandler;
private String mToastContent = "";
private int duration = 0;
private int animStyleId = android.R.style.Animation_Toast;
private final Runnable timerRunnable = new Runnable() {
@Override
public void run() {
removeView();
}
};
private ToastHelper(Context context) {
// Notice: we should get application context
// otherwise we will get error
// "Activity has leaked window that was originally added"
Context ctx = context.getApplicationContext();
if (ctx == null) {
ctx = context;
}
this.mContext = ctx;
mWindowManager = (WindowManager) mContext
.getSystemService(Context.WINDOW_SERVICE);
init();
}
private void init() {
mWindowParams = new WindowManager.LayoutParams();
mWindowParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWindowParams.alpha = 1.0f;
mWindowParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
mWindowParams.format = PixelFormat.TRANSLUCENT;
mWindowParams.type = WindowManager.LayoutParams.TYPE_TOAST;
mWindowParams.setTitle("ToastHelper");
mWindowParams.packageName = mContext.getPackageName();
mWindowParams.windowAnimations = animStyleId;// TODO
mWindowParams.y = mContext.getResources().getDisplayMetrics().widthPixels / 5;
}
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private View getDefaultToastView() {
TextView view = new TextView(mContext);
view.setText(mToastContent);
view.setGravity(Gravity.CENTER_VERTICAL | Gravity.START);
view.setFocusable(false);
view.setClickable(false);
view.setFocusableInTouchMode(false);
view.setTextColor(android.graphics.Color.WHITE);
Drawable drawable = mContext.getResources().getDrawable(
android.R.drawable.toast_frame);
if (Build.VERSION.SDK_INT < 16) {
view.setBackgroundDrawable(drawable);
} else {
view.setBackground(drawable);
}
return view;
}
public void show() {
removeView();
if (toastView == null) {
toastView = getDefaultToastView();
}
mWindowParams.gravity = android.support.v4.view.GravityCompat
.getAbsoluteGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
android.support.v4.view.ViewCompat
.getLayoutDirection(toastView));
removeView();
mWindowManager.addView(toastView, mWindowParams);
if (mHandler == null) {
mHandler = new Handler();
}
mHandler.postDelayed(timerRunnable, duration);
}
public void removeView() {
if (toastView != null && toastView.getParent() != null) {
mWindowManager.removeView(toastView);
mHandler.removeCallbacks(timerRunnable);
}
}
/**
* @param context
* @param content
* @param duration
* @return
*/
public static ToastHelper makeText(Context context, String content,
int duration) {
ToastHelper helper = new ToastHelper(context);
helper.setDuration(duration);
helper.setContent(content);
return helper;
}
/**
* @param context
* @param strId
* @param duration
* @return
*/
public static ToastHelper makeText(Context context, int strId, int duration) {
ToastHelper helper = new ToastHelper(context);
helper.setDuration(duration);
helper.setContent(context.getString(strId));
return helper;
}
public ToastHelper setContent(String content) {
this.mToastContent = content;
return this;
}
public ToastHelper setDuration(int duration) {
this.duration = duration;
return this;
}
public ToastHelper setAnimation(int animStyleId) {
this.animStyleId = animStyleId;
mWindowParams.windowAnimations = this.animStyleId;
return this;
}
/**
* custom view
*
* @param view
*/
public ToastHelper setView(View view) {
this.toastView = view;
return this;
}
}
另外分享一个自定义的Anim:
1.显示Toast的动画:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="true" >
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<translate
android:fromYDelta="20%"
android:toYDelta="0%" />
<scale
android:fromXScale="0.5"
android:fromYScale="0.5"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.0"
android:toYScale="1.0" />
</set>
2.退出Toast的动画:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fillAfter="true" >
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0" />
<translate
android:fromYDelta="0%"
android:toYDelta="20%" />
</set>
给WIndowManager中的View添加动画需要定义一个style,如下:
<style name="PopToast">
<item name="@android:windowEnterAnimation">@anim/anim_toast_enter</item>
<item name="@android:windowExitAnimation">@anim/anim_toast_exit</item>
</style>
最后可以按照如下的方式去使用:
ToastHelper
.makeText(this, "hello world 你好,哈拉雷速度发说得对",
ToastHelper.LENGTH_SHORT)
.setAnimation(R.style.PopToast).show();
三、参考资料:
SuperToast: https://github.com/JohnPersano/SuperToasts