一 效果图
二 视频送礼物,冒泡泡效果
1 需求
- 点击显示礼物图片
- “礼物”由无到有,由小到大,由透明到变实
- “礼物”从底部到顶部逐渐平滑“上浮”,然后消失
2 分析
- 自定义ViewGroup显示,“礼物”是一个ImageView,通过addView显示
- 动画实现 从小到大,从透明变实
- 平滑上浮的轨迹用贝赛尔算法控制ImageView的Point
- 贝赛尔曲线的起始 和终止点在底部居中和顶部任意位置,第二和第三个锚点在屏幕中,通常第三个的位置高于第二个
3 主要代码
贝赛尔算法:
public class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF pointF1;
private PointF pointF2;
public BezierEvaluator(PointF pointF1, PointF pointF2) {
this.pointF1 = pointF1;
this.pointF2 = pointF2;
}
@Override
public PointF evaluate(float t, PointF point0, PointF point3) {
PointF point = new PointF();
point.x = point0.x * (1 - t) * (1 - t) * (1 - t)
+ 3 * pointF1.x * t * (1 - t) * (1 - t)
+ 3 * pointF2.x * t * t * (1 - t)
+ point3.x * t * t * t;
point.y = point0.y * (1 - t) * (1 - t) * (1 - t)
+ 3 * pointF1.y * t * (1 - t) * (1 - t)
+ 3 * pointF2.y * t * t * (1 - t)
+ point3.y * t * t * t;
return point;
}
}
自定义的ViewGroup
public class LoveLayout extends RelativeLayout {
private Interpolator line = new LinearInterpolator();//线性
private Interpolator acc = new AccelerateInterpolator();//加速
private Interpolator dce = new DecelerateInterpolator();//减速
private Interpolator accdec = new AccelerateDecelerateInterpolator();//先加速后减速
private Interpolator[] interpolators;
Drawable[] drawables = new Drawable[3];
private Random random = new Random();
private int dHeight;
private int dWidth;
private LayoutParams params;
private int mWidth;
private int mHeight;
public LoveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public LoveLayout(Context context) {
super(context);
init();
}
public LoveLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
}
private void init() {
interpolators = new Interpolator[4];
interpolators[0] = line;
interpolators[1] = acc;
interpolators[2] = dce;
interpolators[3] = accdec;
//准备图片集合
drawables[0] = getResources().getDrawable(R.drawable.red);
drawables[1] = getResources().getDrawable(R.drawable.yellow);
drawables[2] = getResources().getDrawable(R.drawable.blue);
//得到图片的原始高度
dWidth = drawables[0].getIntrinsicWidth();
dHeight = drawables[0].getIntrinsicHeight();
params = new LayoutParams(dWidth, dHeight);
//将iv添加到父容器底部、水平居中位置
params.addRule(CENTER_HORIZONTAL);
params.addRule(ALIGN_PARENT_BOTTOM);
}
public void addLoveIcon() {
//添加心形,并开始执行动画
final ImageView iv = new ImageView(getContext());
iv.setImageDrawable(drawables[random.nextInt(3)]);
//将iv添加到父容器底部、水平居中位置
iv.setLayoutParams(params);
addView(iv);
//开始属性动画:平移、透明度渐变、缩放动画
AnimatorSet set = getAnimator(iv);
//监听动画执行完毕,将iv移除或者复用
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
removeView(iv);
}
});
set.start();
}
// 得到一个iv的动画集合
private AnimatorSet getAnimator(ImageView iv) {
//平移、透明度渐变、缩放动画
//1.alpha动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(iv, "alpha", 0.3f, 1f);
//2.缩放动画
ObjectAnimator scaleX = ObjectAnimator.ofFloat(iv, "scaleX", 0.3f, 1f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(iv, "scaleY", 0.3f, 1f);
//三个动画同时执行
AnimatorSet enter = new AnimatorSet();
enter.setDuration(600);
enter.playTogether(alpha, scaleX, scaleY);
// enter.setTarget(iv);
//设置平移的曲线动画---贝塞尔曲线
ValueAnimator bezierAnimator = getBezierValueAnimator(iv);
AnimatorSet set = new AnimatorSet();
//按序列执行
set.playSequentially(enter, bezierAnimator);
//加速因子,使用插值器
set.setInterpolator(interpolators[random.nextInt(4)]);
set.setTarget(iv);
return set;
}
//得到一个贝塞尔曲线动画
private ValueAnimator getBezierValueAnimator(final ImageView iv) {
//根据贝塞尔公式确定四个点(起始点p0,拐点1p1,拐点2p2,终点p3)
PointF pointF0 = new PointF((mWidth - dWidth) / 2, mHeight - dHeight);
PointF pointF3 = new PointF(random.nextInt(mWidth), 0);
PointF pointF1 = getPointF(1);
PointF pointF2 = getPointF(2);
//估值器Evaluator,来控制view的行驶路径(不断地修改point.x,point.y)
BezierEvaluator evaluator = new BezierEvaluator(pointF1, pointF2);
//属性动画不仅仅可以改变view的属性,还可以改变自定义的属性(比如Point)
ValueAnimator animator = ValueAnimator.ofObject(evaluator, pointF0, pointF3);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF = (PointF) animation.getAnimatedValue();
iv.setX(pointF.x);
iv.setY(pointF.y);
iv.setAlpha(1 - animation.getAnimatedFraction());//1~0 百分比
}
});
animator.setDuration(4000);
return animator;
}
private PointF getPointF(int i) {
PointF pointF = new PointF();
pointF.x = random.nextInt(mWidth);
//为了好看,尽量保证point2.y>point1.y
if (i == 1) {
pointF.y = random.nextInt(mHeight / 2) + mHeight / 2;
} else {
pointF.y = random.nextInt(mHeight / 2);
}
return pointF;
}
}
4 Demo
三 淘宝商品折叠效果
1 需求
- 页面分两部分,上面的商品图片和下面显示部分的详情
- 点击时,图片翻转缩小,详情部分上滑显示更多内容
2 分析
- 两个部分用两个ViewGroup
- 翻转缩小动画控制第一个ViewGroup显示图片
- 上滑动画控制显示第二个ViewGroup显示详情
3 主要代码
public void startFirstAnim(View v){
//显示first_view:1.透明度动画;2.缩放动画;3.翻转动画
//透明度动画
ObjectAnimator firstAlphaAnim = ObjectAnimator.ofFloat(first_view, "alpha", 1.0f, 0.7f);
firstAlphaAnim.setDuration(300);
//旋转动画1
ObjectAnimator firstRotationXanim = ObjectAnimator.ofFloat(first_view, "rotationX", 0f,20f);
firstRotationXanim.setDuration(300);
//再旋转回来
ObjectAnimator firstResumeRotationXanim = ObjectAnimator.ofFloat(first_view, "rotationX", 20f, 0f);
firstResumeRotationXanim.setDuration(300);
firstResumeRotationXanim.setStartDelay(300);//延迟第一次旋转动画的时间,在这之后再执行
//缩放动画
ObjectAnimator firstScaleXAnim = ObjectAnimator.ofFloat(first_view, "ScaleX", 1.0f, 0.8f);
firstScaleXAnim.setDuration(300);
ObjectAnimator firstScaleYAnim = ObjectAnimator.ofFloat(first_view, "ScaleY", 1.0f, 0.8f);
firstScaleYAnim.setDuration(300);
//由于缩放造成离顶部有一个距离,需要平移
ObjectAnimator firstTranslationYAnim = ObjectAnimator.ofFloat(first_view, "translationY", 0f, -0.1f*first_view.getHeight());
firstTranslationYAnim.setDuration(300);
//第二个view和第一个view动画同时开始执行
ObjectAnimator secondTranslationYAnim = ObjectAnimator.ofFloat(second_view, "translationY", second_view.getHeight(), 0f);
secondTranslationYAnim.setDuration(300);
// secondTranslationYAnim.setStartDelay(200);
secondTranslationYAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
second_view.setVisibility(View.VISIBLE);
bt.setClickable(false);
}
});
AnimatorSet set = new AnimatorSet();
set.playTogether(firstScaleXAnim,firstScaleYAnim,firstAlphaAnim,firstRotationXanim,firstResumeRotationXanim,firstTranslationYAnim,secondTranslationYAnim);
set.start();
}