效果图
原理是通过自定义TypeEvaluator,来实现三阶贝塞尔曲线的运动轨迹,重点是找到控制点,套入公式。
三阶贝塞尔曲线公式:
- t:为时间
- p0:起点
- p1:控制点1
- p2:控制点2
- p3:始点,终点
代码如下
public class LikeLayout extends RelativeLayout {
private Random mRandom;
private int[] mImageRes;
private int mDrawableHeight;
private int mDrawableWidth;
private int mHeight;
private int mWidth;
private Interpolator[] mInterpolators;
public LikeLayout(Context context) {
this(context,null);
}
public LikeLayout(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public LikeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mRandom = new Random();
mInterpolators = new Interpolator[]{new AccelerateDecelerateInterpolator(), new AccelerateInterpolator(),
new DecelerateInterpolator(), new LinearInterpolator()};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获取控件的宽高
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
/**
* 设置图片资源
* @param imageRes
*/
public void setImagRes(int... imageRes){
mImageRes = imageRes;
if(mImageRes.length>0){
Drawable drawable = ContextCompat.getDrawable(getContext(), mImageRes[0]);
mDrawableHeight = drawable.getIntrinsicHeight();
mDrawableWidth = drawable.getIntrinsicWidth();
}
}
//展示
public void show(){
if(mImageRes.length==0){
return;
}
addImage();
}
//添加图片
private void addImage() {
final ImageView likeIv=new ImageView(getContext());
int i = mImageRes.length - 1;
likeIv.setImageResource(mImageRes[mRandom.nextInt(mImageRes.length)]);
RelativeLayout.LayoutParams params=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(ALIGN_PARENT_BOTTOM);
params.addRule(CENTER_HORIZONTAL);
likeIv.setLayoutParams(params);
addView(likeIv);
//播放动画
AnimatorSet animatorSet = startAnimator(likeIv);
animatorSet.start();
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
//移除
removeView(likeIv);
}
});
}
private AnimatorSet startAnimator(ImageView likeIv) {
AnimatorSet allAnimatorSet=new AnimatorSet();
//开始时的动画集合
AnimatorSet beginAnimatorSet=new AnimatorSet();
ObjectAnimator alphaAnimator=ObjectAnimator.ofFloat(likeIv,"alpha",0.3f,10.f);
ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(likeIv, "scaleX", 0.3f, 1.0f);
ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(likeIv, "scaleY", 0.3f, 1.0f);
ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(likeIv, "rotation",0,mRandom.nextInt(60));
beginAnimatorSet.playTogether(alphaAnimator,scaleXAnimator,scaleYAnimator,rotationAnimator);
beginAnimatorSet.setDuration(350);
beginAnimatorSet.setInterpolator(new BounceInterpolator());
//按顺序执行
allAnimatorSet.playSequentially(beginAnimatorSet,getBetheAnimator(likeIv));
return allAnimatorSet;
}
private Animator getBetheAnimator(final ImageView likeIv) {
//起点,底部正中间,记得减去图片宽度的一半
PointF p0=new PointF(mWidth/2-mDrawableWidth/2,mHeight-mDrawableHeight);
//控制点1 下半部分
PointF p1=new PointF(mRandom.nextInt(mWidth-mDrawableWidth),mRandom.nextInt(mHeight/2)+mHeight/2);
//控制点2 上半部分
PointF p2=new PointF(mRandom.nextInt(mWidth-mDrawableWidth),mRandom.nextInt(mHeight/2));
//始点 防止露出来
PointF p3=new PointF(mRandom.nextInt(mWidth-mDrawableWidth),0);
BetheTypeEvaluator betheEvaluator=new BetheTypeEvaluator(p1,p2);
ValueAnimator animator = ObjectAnimator.ofObject(betheEvaluator, p0, p3);
animator.setDuration(2000);
animator.setInterpolator(mInterpolators[mRandom.nextInt(mInterpolators.length)]);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF= (PointF) animation.getAnimatedValue();
likeIv.setX(pointF.x);
likeIv.setY(pointF.y);
// 透明度
float t = animation.getAnimatedFraction();
likeIv.setAlpha(1 - t + 0.3f);
}
});
return animator;
}
/**
* 自定义贝塞尔曲线动画
* 三阶贝塞尔曲线公式:
*
*/
class BetheTypeEvaluator implements TypeEvaluator<PointF> {
//控制点p1,p2
private PointF mP1;
private PointF mP2;
public BetheTypeEvaluator(PointF p1, PointF p2){
mP1 = p1;
mP2 = p2;
}
@Override
public PointF evaluate(float t, PointF p0, PointF p3) {
PointF pointF=new PointF();
pointF.x=p0.x*(1-t)*(1-t)*(1-t)
+3*mP1.x*t*(1-t)*(1-t)
+3* mP2.x*t*t*(1-t)
+p3.x*t*t*t;
pointF.y=p0.y*(1-t)*(1-t)*(1-t)
+3*mP1.y*t*(1-t)*(1-t)
+3* mP2.y*t*t*(1-t)
+p3.y*t*t*t;
return pointF;
}
}
}
调用
mLikeLayout.setImagRes(R.drawable.zan1,R.drawable.zan3,R.drawable.zan2);
findViewById(R.id.cliclk).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLikeLayout.show();
}
});