一、效果及原理
先放一张效果图:
从效果图中可以看出,点赞飘心的效果是动画组合而成的,遇到一个复杂的动画效果,首先我们考虑应该是能不能用现有的Api效果拆分实现,如果不行再考虑完全自定义的实现,毕竟写算法很烦。言归正传,上图的这个效果,仔细观察可以将其拆分为俩部分动画,一个是心出现由小到大的动画,一个是往上飘的动画。已拆分发现已有的api完全没问题那么接下来看具体实现。
二、代码实现
private void init() {
interpolators = new Interpolator[4];
interpolators[0] =line;
interpolators[1] =acc;
interpolators[2] =dce;
interpolators[3] =accdec;
//准备图片集合
drawables[0] = getResources().getDrawable(R.mipmap.red);
drawables[1] = getResources().getDrawable(R.mipmap.yellow);
drawables[2] = getResources().getDrawable(R.mipmap.blue);
//得到图片的原始高度
dWidth = drawables[0].getIntrinsicWidth();
dHeight = drawables[0].getIntrinsicHeight();
params = new LayoutParams(dWidth, dHeight);
//将iv添加到父容器底部、水平居中位置
params.addRule(CENTER_HORIZONTAL);
params.addRule(ALIGN_PARENT_BOTTOM);
}
(二)初始化完成后需要的就是给图片添加动画了
1、给图片添加俩动画,刚开始的心出现动画,接着的心上漂动画,然后组合之后按照先后顺序执行,
// 得到一个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 animatorSet = new AnimatorSet();
animatorSet.setDuration(600);
animatorSet.playTogether(alpha,scaleX,scaleY);
//设置平移的曲线动画---贝塞尔曲线
ValueAnimator bezierAnimator = getBezierValueAnimator(iv);
AnimatorSet set = new AnimatorSet();
//按序列执行
set.playSequentially(animatorSet,bezierAnimator);
set.setTarget(iv);
return set;
}
其中上移动画采用的是4阶贝塞尔曲线,自定义了一个估值器,套了下公式,代码如下:
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 pointF0, PointF pointF3) {
//t百分比:0~1
// b(t)=p0*(1-t)*(1-t)*(1-t)+3*p1*t*(1-t)*(1-t)+3*p2*t*t*(1-t)+p3*t*t*t
PointF pointF = new PointF();
pointF.x = pointF0.x * (1 - t) * (1 - t) * (1 - t) + 3 * pointF1.x * t * (1 - t) * (1 - t) +
3 * pointF2.x * t * t * (1 - t)
+ pointF3.x * t * t * t;
pointF.y = pointF0.y * (1 - t) * (1 - t) * (1 - t) + 3 * pointF1.y * t * (1 - t) * (1 - t) +
3 * pointF2.y * t * t * (1 - t)
+ pointF3.y * t * t * t;
return pointF;
}
}
调用估值器完成上漂动画的细节如下:
//得到一个贝塞尔曲线动画
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 bezierEvaluator = new BezierEvaluator(pointF1, pointF2);
//属性动画不仅仅可以改变view的属性,还可以改变自定义的属性(比如Point)
ValueAnimator valueAnimator = ValueAnimator.ofObject(bezierEvaluator, pointF0, pointF3);
valueAnimator.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());
}
});
valueAnimator.setDuration(4000);
return valueAnimator;
}
2、向外暴露一个方法,能够调用动画的方法
public void addIcon(){
//添加心形,并开始执行动画
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();
}
3、在activity中调用下此方法就好了
mBt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLoveLayout.addIcon();
}
});
三、代码链接
代码已经上传至github,链接为: LoveLayout