前言
先问自己4个问题:
1) 如何实现自定义对话框, OK, 很容易,是不是。
2) 假如让你实现一个曲线动画效果,该如何实现?
3) 假如让这个曲线动画沿着自定义对话框的最上侧往上飞出屏幕或是往下飞出屏幕,该如何实现?
4)如何适配不同屏幕分辨率呢?
这就是之前困扰我的难题,经过努力,终于解决了。其实是项目中的一个小需求,
当时看起来很简单,实现起来,才发现,小坑很多。说到底,自己是菜鸟,如此而已。
目标
这篇以及下篇blog介绍如何实现这种效果,先看下动画效果截图。
不知道如何在csdn blog中添加短视频。
关键点
Android动画有3种,View动画,Drawable动画,Property动画。
各有特点,各取所需。本例使用属性动画。
属性动画
属性动画可以通过改变对象的属性来达到酷炫的动画效果.(更多详见参考)
这里提个属性动画 跟 View动画中的Tween Animation的区别。
Tween动画事件响应是不会随着移动而改变的,比如 button 从左跑到右,该button的点击事件还是在左边;而属性动画中view对象的事件响应可以移动。
属性动画中的曲线如何实现
本例使用了贝塞尔2阶曲线,并截取其中的一部分。其实也是可以使用抛物线来实现。
使用属性动画中的TypeEvaluator(类型估值,用于设置复杂的动画操作属性的值)
如果对属性动画没有概念,请参考 Android属性动画
关键代码段
static class PlaneEvaluator implements TypeEvaluator<PointF> {
private int type = 0;
public PlaneEvaluator(int type) {
//0: 飞机往上飞; 1: 往下飞
this.type = type;
}
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
final float t = fraction;
float oneMinusT = 1.0f - t;
point0 = (PointF) startValue;
point2 = (PointF) endValue;
if (this.type == 0) {//up
point1.set(width_up, 0);
} else {//down
point1.set(width_down, 0);
}
//其实就是贝塞尔2阶曲线函数
point.x = oneMinusT * oneMinusT * (point0.x) + 2 * oneMinusT * t * (point1.x) + t * t * (point2.x);
point.y = oneMinusT * oneMinusT * (point0.y) + 2 * oneMinusT * t * (point1.y) + t * t * (point2.y);
if (this.type == 0) {
point.y = -point.y;
}
//Log.d("TAG""fraction = " + fraction + ": " + endValue.toString() + ": point(x,y): " + point.toString());
return point;
}
}
//该函数是初始化属性动画的各种参数,以及动画的起始点,和结束点
private static void initValueAnimator(int type) {
planeType = type;
int width = 0;
if (type == 0) {//up
width = width_up;
} else {//down
width = width_down;
}
setTVEnabled(false);
Log.d("TAG", "width = " + width + ", height = " + height + ", type = " + type);
valueAnimator = ValueAnimator.ofObject(new PlaneEvaluator(type), new PointF(0, 0), new PointF(width, height));
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
//动画超出屏幕后需要取消动画,然后在cancel监听中做相关处理
if (valueAnimator.getCurrentPlayTime() > 1500 && valueAnimator.isRunning()) {
valueAnimator.cancel();
}
mIvPlane.setX(pointF.x);
mIvPlane.setY(pointF.y);
}
});
valueAnimator.addListener(new AnimatorListenerAdapter() {
public void onAnimationCancel(Animator animation) {
Log.d("TAG", "Cancel Animation!!!");
setTVEnabled(true);
//飞机不可见
mIvPlane.setVisibility(View.INVISIBLE);
customDialog.dismiss();
if (planeType == TYPE_UP) {//up 做其他事情
}//进入onAnimationCancel就已经执行了消失操作, 无需else
}
});
valueAnimator.setTarget(mIvPlane);
valueAnimator.setRepeatCount(1);
valueAnimator.setRepeatMode(ValueAnimator.REVERSE);
//开始动画
mIvPlane.setVisibility(View.VISIBLE);
valueAnimator.start();
}
待解决问题
这里仅仅是在自定义对话框中飞,透明部分不可见,需要完成如前文提到的
3) 假如让这个曲线动画沿着自定义对话框的最上侧往上飞出屏幕或是往下飞出屏幕,该如何实现?
下篇解决该问题。
参考
1) Android属性动画
2)Android属性动画Property Animation系列一之ValueAnimator
3)不要忘记Android官网
4)沿着贝塞尔曲线移动的Property Animation
5)贝塞尔2阶曲线