点赞飘心动画组件FlyHeartView

点赞飘心动画组件 

 

package com.reone.likepoint;

import android.animation.Animator;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;

import android.animation.AnimatorSet;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * Created by wangxingsheng on 2018/7/24.
 * 点赞飘心动画组件
 */
public class FlyHeartView extends FrameLayout {

    private List<Drawable> mLikeDrawables; // 图片的集合
    private LayoutParams mLayoutParams; // 用于设置动画对象的位置参数
    private Random mRandom; // 用于产生随机数,如生成随机图片

    private int mViewWidth; // 控件的宽度
    private int mViewHeight; // 控件的高度

    private int mPicWidth; // 图片的宽度
    private int mPicHeight; // 图片的高度

    public FlyHeartView(Context context) {
        this(context, null);
    }

    public FlyHeartView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FlyHeartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initParams();
    }

    private void initParams() {
        mLikeDrawables = new ArrayList<>();
        mLikeDrawables.add(generateDrawable(R.drawable.heart0));
        mLikeDrawables.add(generateDrawable(R.drawable.heart1));
        mLikeDrawables.add(generateDrawable(R.drawable.heart2));
        mLikeDrawables.add(generateDrawable(R.drawable.heart3));
        mLikeDrawables.add(generateDrawable(R.drawable.heart4));
        mLikeDrawables.add(generateDrawable(R.drawable.heart5));
        mLikeDrawables.add(generateDrawable(R.drawable.heart6));
        mLikeDrawables.add(generateDrawable(R.drawable.heart7));
        mLikeDrawables.add(generateDrawable(R.drawable.heart8));

        // 获取图片的宽高, 由于图片大小一致,故直接获取第一张图片的宽高
        mPicWidth = dp2px(40);
        mPicHeight = dp2px(38);
        // 初始化布局参数
        mLayoutParams = new LayoutParams(mPicWidth, mPicHeight);
        mLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;

        mRandom = new Random();
    }

    public int dp2px(float dpValue) {
        final float scale = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (dpValue * scale + 0.5f);
    }

    private Drawable generateDrawable(int resID) {
        return ContextCompat.getDrawable(getContext(), resID);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        for (int i = 0, size = getChildCount(); i < size; i++) {
            View childView = getChildAt(i);
            measureChild(childView, widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewWidth = getWidth();
        mViewHeight = getHeight();
    }

    /**
     * 动态添加 FlowView
     */
    public void addLikeView() {
        ImageView likeView = new ImageView(getContext());
        likeView.setImageDrawable(mLikeDrawables.get(mRandom.nextInt(mLikeDrawables.size())));
        likeView.setLayoutParams(mLayoutParams);

        addView(likeView);
        startAnimation(likeView);
    }

    private void startAnimation(View target) {
        // 设置进入动画
        AnimatorSet enterAnimator = generateEnterAnimation(target);
        // 设置路径动画
        ValueAnimator curveAnimator = generateCurveAnimation(target);

        // 设置动画集合, 先执行进入动画,最后再执行运动曲线动画
        AnimatorSet finalAnimatorSet = new AnimatorSet();
        finalAnimatorSet.setTarget(target);
        finalAnimatorSet.playSequentially(enterAnimator, curveAnimator);
        finalAnimatorSet.addListener(new AnimationEndListener(target));
        finalAnimatorSet.start();
    }

    /**
     * 生成进入动画
     *
     * @return 动画集合
     */
    private AnimatorSet generateEnterAnimation(View target) {
        ObjectAnimator alpha = ObjectAnimator.ofFloat(target, "alpha", 0.2f, 1f);
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(target, "scaleX", 0.5f, 1f);
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(target, "scaleY", 0.5f, 1f);
        AnimatorSet enterAnimation = new AnimatorSet();
        enterAnimation.playTogether(alpha, scaleX, scaleY);
        enterAnimation.play(alpha);
        enterAnimation.setDuration(150);
        enterAnimation.setTarget(target);
        return enterAnimation;
    }

    /**
     * 生成曲线运动动画
     *
     * @return 动画集合
     */
    private ValueAnimator generateCurveAnimation(View target) {
        BezierCurveEvaluator evaluator = new BezierCurveEvaluator(generateCTRLPoint(1), generateCTRLPoint(2));
        ValueAnimator valueAnimator = ValueAnimator.ofObject(evaluator,
                new Point((mViewWidth - mPicWidth) / 2, mViewHeight - mPicHeight),
                new Point(mRandom.nextInt(mViewWidth - mPicWidth), 0));
        valueAnimator.setDuration(2500);
        valueAnimator.addUpdateListener(new CurveUpdateLister(target));
        valueAnimator.setTarget(target);
        return valueAnimator;
    }

    /**
     * 动画曲线路径更新监听器, 用于动态更新动画作用对象的位置
     */
    private class CurveUpdateLister implements ValueAnimator.AnimatorUpdateListener {
        private View target;

        public CurveUpdateLister(View target) {
            this.target = target;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            // 获取当前动画运行的状态值, 使得动画作用对象沿着曲线(涉及贝塞儿曲线)运动
            Point point = (Point) animation.getAnimatedValue();
            target.setX(point.x);
            if(point.y < target.getY()){
                target.setY(point.y);
            }
            // 改变对象的透明度
            target.setAlpha(1 - animation.getAnimatedFraction());
            float scale = (1 - animation.getAnimatedFraction() / 2);
            target.setScaleY(scale);
            target.setScaleX(scale);
            target.setRotation(point.rotation);
        }
    }

    /**
     * 生成贝塞儿曲线的控制点
     *
     * @param value 设置控制点 y 轴上取值区域
     * @return 控制点的 x y 坐标
     */
    private Point generateCTRLPoint(int value) {
        Point point = new Point();
        point.x = mRandom.nextInt(mViewWidth - mPicWidth);
        point.y = mRandom.nextInt(mViewHeight / value);
        return point;
    }

    /**
     * 自定义估值算法, 计算对象当前运动的具体位置 Point
     */
    private class BezierCurveEvaluator implements TypeEvaluator<Point> {

        // 由于这里使用的是三阶的贝塞儿曲线, 所以我们要定义两个控制点
        private Point ctrlPoint1;
        private Point ctrlPoint2;

        public BezierCurveEvaluator(Point ctrlPoint1, Point ctrlPoint2) {
            this.ctrlPoint1 = ctrlPoint1;
            this.ctrlPoint2 = ctrlPoint2;
        }

        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {

            // 这里运用了三阶贝塞儿曲线的公式, 请自行上网查阅
            float leftTime = 1.0f - fraction;
            Point resultPoint = new Point();

            // 三阶贝塞儿曲线
            resultPoint.x = (float) Math.pow(leftTime, 3) * startValue.x
                    + 3 * (float) Math.pow(leftTime, 2) * fraction * ctrlPoint1.x
                    + 3 * leftTime * (float) Math.pow(fraction, 2) * ctrlPoint2.x
                    + (float) Math.pow(fraction, 3) * endValue.x;
            resultPoint.y = (float) Math.pow(leftTime, 3) * startValue.y
                    + 3 * (float) Math.pow(leftTime, 2) * fraction * ctrlPoint1.y
                    + 3 * leftTime * fraction * fraction * ctrlPoint2.y
                    + (float) Math.pow(fraction, 3) * endValue.y;

            // 根据生成点的位置设置此点应该旋转的角度
            resultPoint.rotation = (float) (- Math.atan((endValue.x - resultPoint.x)/(endValue.y - resultPoint.y)) * 180 / Math.PI % 360);
            return resultPoint;
        }
    }

    /**
     * 动画结束监听器,用于释放无用的资源
     */
    private class AnimationEndListener extends AnimatorListenerAdapter {
        private View target;

        public AnimationEndListener(View target) {
            this.target = target;
        }

        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            removeView(target);
        }
    }

    class Point{
        public float x;
        public float y;
        public float rotation;

        Point() {
        }

        Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }
}

 

转载于:https://my.oschina.net/reone/blog/1861985

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值