点赞飘心动画组件

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 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 {

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

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;

}

}

}

android 点赞飘心,点赞飘心动画组件FlyHeartView相关推荐

  1. Android 仿直播特效点赞飘爱心

    一.概述,最后面有完整代码下载地址 老规矩先上图 好了,基本就是这个样子,录完的视频用格式工厂转换完就这个样子了,将就看吧 二.定义我们自己的Layout [java] view plaincopy ...

  2. twitter视频下载php,Twitter推特红心点赞H5动画按钮特效

    Twitter推特红心点赞H5动画按钮特效 代码片段: $('body').on("click",'.heart',function() { var A=$(this).attr( ...

  3. android 心形上漂动画,PowerPoint Viewer制作一个漂亮心形飞出动画的操作教程

    各位使用PowerPoint Viewer的同学们,你们知道怎么制作一个漂亮心形飞出动画吗?在这篇教程内小编就为各位呈现了PowerPoint Viewer制作一个漂亮心形飞出动画的操作教程. Pow ...

  4. 心形源码HTML,纯CSS实现心形加载动画(附源码)

    本篇文章给大家介绍一下纯CSS实现心形加载动画的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. 废话不多说上代码,代码很简答,研究一下就明白了,有不明白的可以问我. .hear ...

  5. css 实现心形加载动画

    文章目录 前言 一.html代码 二.css 总结 前言 心形加载动画 提示:以下是本篇文章的代码内容,供大家参考,相互学习 一.html代码 代码如下: <html><head&g ...

  6. android点赞取消赞功能吗,Android 仿微博的点赞功能的实现原理(持续点赞再取消)...

    搜索热词 产品需求,实现类似微博的持续点赞再取消功能,因为自己也偶尔刷微博,对这功能有一定的使用上的了解, 至于微博点赞的具体实现我并不知道,微博点赞在断网的情况下依然能点赞,不会提示网络异常,等有网 ...

  7. 安卓学习专栏——安卓报错android.support.v4.widget.SwipeRefreshLayout飘红

    步骤 系列文章 1.报错效果 2.修改方法使用新的标签名 总结 系列文章 提示:转到安卓学习专栏,观看更多内容! 点我直达–>安卓学习专栏 1.报错效果 第一行代码酷欧天气实战时飘红 安卓报错a ...

  8. 程序员表白神器。安卓程序员表白软件。程序员追女友利器=android+雪花效果+彩色气泡+心形花园+心形玫瑰花+相爱天数计时器

    程序员表白神器.安卓程序员表白软件.程序员追女友利器=android+雪花效果+彩色气泡+心形花园+心形玫瑰花 +相爱天数计时器. APK下载(把这个给女朋友,她一定会高兴的):http://down ...

  9. 安卓学习专栏——安卓报错android.support.v4.widget.DrawerLayout飘红

    步骤 系列文章 1.报错效果 2.修改方法使用新的标签名 总结 系列文章 提示:转到安卓学习专栏,观看更多内容! 点我直达–>安卓学习专栏 1.报错效果 第一行代码酷欧天气实战时飘红 安卓报错a ...

  10. 20P67 PR模板预设14组MOGRT预设卡通彩色爱心情人节浪漫婚礼心形状元素图形动画

    20P67 PR模板预设14组MOGRT预设卡通彩色爱心情人节浪漫婚礼心形状元素图形动画下载 [Premiere Pro 模板介绍] 分辨率:4k/1920×1080 软件要求:Premiere Pr ...

最新文章

  1. 深度解析|基于 eBPF 的 Kubernetes 一站式可观测性系统
  2. 路由器的修改特权密码、还原出厂设置、备份配置文件和升级操作系统实际操作...
  3. html 改变文本框字体颜色,CSS更改文本框的字体颜色
  4. 跨平台异步IO库 libuv 源代码接口详解
  5. linux用命令启动达梦数据库,达梦数据库DM7的命令行安装
  6. php制作成绩单,PHP学生成绩查询及分析系统制作
  7. 计算机开机后 打不开软件,电脑开机后软件打不开怎么回事
  8. 原生 js html 开发成桌面应用 以及打包
  9. wei dian 使用的一点小技巧
  10. 我用这个小程序,副业赚钱轻松日入300+
  11. hacks cheats injection
  12. Puppet自动化分发服务 - 视频课程-龙小威-专题视频课程
  13. 支付宝电脑网页支付接口的调用
  14. 三星SAIT China Lab招聘研究员(校招/社招)及实习生
  15. 【龙芯1c库】封装硬件pwm接口和使用示例
  16. 使用Meterperter会话获取目标屏幕与键盘记录
  17. 四台主机模拟实现静态路由
  18. 我的旅途-嵌入式软件工程师成长之路(一)
  19. 4.11日网易互娱笔试
  20. Verilog实现---时钟信号的90°相移

热门文章

  1. Ingenuous Cubrency——完全背包
  2. android imageview 图片模糊,android – Imageview变得模糊
  3. 如何在PDF文件中提取图片?PDF图片提取教程
  4. vb.net设置分辨率和缩放比例_配置高不一定性能强,Win 10做好这些设置才能“6到飞起”!...
  5. hp laserjet1010 win7 64打印慢问题
  6. 树莓派能做什么?如何使用树莓派
  7. python给批量图片添加文字_Python之利用PIL批量给图片添加文字
  8. 【C补充】qsort函数 —— 数组元素排序
  9. 旧手机改造成Linux服务器
  10. 人工智能的主要优势是什么?