本文中涉及两个点比较重要:一是设计一个OvershootInterpolator,并使用开源库实现的动画效果;二是使用自定义的layout ,并通过WindowManager 的addView() 添加,实现类似toast效果,并增加toast 的点击事件回调处理。
用前文中讲述的动画基础,实现一个toast的动画效果并没有多少困难。就是一个Y 轴的位移动画,并加上一个overshoot 的interpolator插值器。
关心动画效果的同学,直接看这里实现动画效果的代码。
private static Interpolator mOverShootInter = new OvershootInterpolator();
animate(textView).translationY(ViewHelper.getTranslationY(textView) - dp2px(ctx, 200)).setDuration(
200).setInterpolator(mOverShootInter);
public static float dp2px(Context cxt, float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
cxt.getResources().getDisplayMetrics());
}
private static class OvershootInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
if (input < 0.5f)
return (float) (Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
else
return 0.5f * (o(input * 2.0f - 2.0f, 4) + 2.0f);
}
private float o(float t, float s) {
return t * t * ((s + 1) * t + s);
}
}
其中函数dp2px 是为了不同分辨率屏幕兼容性而计算dp 值到px 的转换。就是写个动画的距离。
类OvershootInterpolator是参考系统的AnticipateOvershootInterpolator 和AccelerateDecelerateInterpolator 插值器使用分段函数拼接而成。插值器的分析和设计可以参考前文《Android 动画animation 深入分析》
实现的关键是使用WindowManager 的addView() 动态添加一个自定义的toast view,加入回调接口,在点击事件中回调即可。
public static final int TOAST_LONG = 5000;
public static final int TOAST_SHORT = 3000;
public static void showCustomToast(final Context ctx, CharSequence toastMessage,
final OnClickListener clickCallback, int duration) {
final WindowManager windowManager;
windowManager = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
final RelativeLayout addedLayout = (RelativeLayout) LayoutInflater.from(ctx).inflate(
R.layout.custom_toast_view, null);
RelativeLayout toastHolder = (RelativeLayout) addedLayout.findViewById(R.id.toast_holder);
toastHolder.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(ctx, "toast holder clicked!", Toast.LENGTH_SHORT).show();
windowManager.removeView(addedLayout);
}
});
final ValueAnimator anim = ValueAnimator.ofInt(0, 1);
anim.setDuration(duration);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
try {
windowManager.removeView(addedLayout);
} catch (Exception e) {
// may throw "View not attached to window" exception when
// "mWindowManager.mView == null" or "mWindowManager.mView.length == 0",
// that means no view attached to this window
}
Toast.makeText(ctx, "custom toast removed!", Toast.LENGTH_SHORT).show();
}
});
anim.start();
TextView textView = (TextView) addedLayout.findViewById(R.id.toast_text);
textView.setText(toastMessage);
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
anim.end();
if (clickCallback != null) {
clickCallback.onClick(v);
}
Toast.makeText(ctx, "custom toast clicked!", Toast.LENGTH_SHORT).show();
}
});
LayoutParams addedLayoutParams;
addedLayoutParams = new WindowManager.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT, LayoutParams.TYPE_PHONE, LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_FULLSCREEN, PixelFormat.TRANSPARENT);
addedLayoutParams.x = 0;
addedLayoutParams.y = 0;
windowManager.addView(addedLayout, addedLayoutParams);
animate(textView).translationY(ViewHelper.getTranslationY(textView) - dp2px(ctx, 200)).setDuration(
200).setInterpolator(mOverShootInter);
}