android 点赞飘心,点赞飘心动画组件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 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相关推荐
- Android 仿直播特效点赞飘爱心
一.概述,最后面有完整代码下载地址 老规矩先上图 好了,基本就是这个样子,录完的视频用格式工厂转换完就这个样子了,将就看吧 二.定义我们自己的Layout [java] view plaincopy ...
- twitter视频下载php,Twitter推特红心点赞H5动画按钮特效
Twitter推特红心点赞H5动画按钮特效 代码片段: $('body').on("click",'.heart',function() { var A=$(this).attr( ...
- android 心形上漂动画,PowerPoint Viewer制作一个漂亮心形飞出动画的操作教程
各位使用PowerPoint Viewer的同学们,你们知道怎么制作一个漂亮心形飞出动画吗?在这篇教程内小编就为各位呈现了PowerPoint Viewer制作一个漂亮心形飞出动画的操作教程. Pow ...
- 心形源码HTML,纯CSS实现心形加载动画(附源码)
本篇文章给大家介绍一下纯CSS实现心形加载动画的方法.有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助. 废话不多说上代码,代码很简答,研究一下就明白了,有不明白的可以问我. .hear ...
- css 实现心形加载动画
文章目录 前言 一.html代码 二.css 总结 前言 心形加载动画 提示:以下是本篇文章的代码内容,供大家参考,相互学习 一.html代码 代码如下: <html><head&g ...
- android点赞取消赞功能吗,Android 仿微博的点赞功能的实现原理(持续点赞再取消)...
搜索热词 产品需求,实现类似微博的持续点赞再取消功能,因为自己也偶尔刷微博,对这功能有一定的使用上的了解, 至于微博点赞的具体实现我并不知道,微博点赞在断网的情况下依然能点赞,不会提示网络异常,等有网 ...
- 安卓学习专栏——安卓报错android.support.v4.widget.SwipeRefreshLayout飘红
步骤 系列文章 1.报错效果 2.修改方法使用新的标签名 总结 系列文章 提示:转到安卓学习专栏,观看更多内容! 点我直达–>安卓学习专栏 1.报错效果 第一行代码酷欧天气实战时飘红 安卓报错a ...
- 程序员表白神器。安卓程序员表白软件。程序员追女友利器=android+雪花效果+彩色气泡+心形花园+心形玫瑰花+相爱天数计时器
程序员表白神器.安卓程序员表白软件.程序员追女友利器=android+雪花效果+彩色气泡+心形花园+心形玫瑰花 +相爱天数计时器. APK下载(把这个给女朋友,她一定会高兴的):http://down ...
- 安卓学习专栏——安卓报错android.support.v4.widget.DrawerLayout飘红
步骤 系列文章 1.报错效果 2.修改方法使用新的标签名 总结 系列文章 提示:转到安卓学习专栏,观看更多内容! 点我直达–>安卓学习专栏 1.报错效果 第一行代码酷欧天气实战时飘红 安卓报错a ...
- 20P67 PR模板预设14组MOGRT预设卡通彩色爱心情人节浪漫婚礼心形状元素图形动画
20P67 PR模板预设14组MOGRT预设卡通彩色爱心情人节浪漫婚礼心形状元素图形动画下载 [Premiere Pro 模板介绍] 分辨率:4k/1920×1080 软件要求:Premiere Pr ...
最新文章
- 深度解析|基于 eBPF 的 Kubernetes 一站式可观测性系统
- 路由器的修改特权密码、还原出厂设置、备份配置文件和升级操作系统实际操作...
- html 改变文本框字体颜色,CSS更改文本框的字体颜色
- 跨平台异步IO库 libuv 源代码接口详解
- linux用命令启动达梦数据库,达梦数据库DM7的命令行安装
- php制作成绩单,PHP学生成绩查询及分析系统制作
- 计算机开机后 打不开软件,电脑开机后软件打不开怎么回事
- 原生 js html 开发成桌面应用 以及打包
- wei dian 使用的一点小技巧
- 我用这个小程序,副业赚钱轻松日入300+
- hacks cheats injection
- Puppet自动化分发服务 - 视频课程-龙小威-专题视频课程
- 支付宝电脑网页支付接口的调用
- 三星SAIT China Lab招聘研究员(校招/社招)及实习生
- 【龙芯1c库】封装硬件pwm接口和使用示例
- 使用Meterperter会话获取目标屏幕与键盘记录
- 四台主机模拟实现静态路由
- 我的旅途-嵌入式软件工程师成长之路(一)
- 4.11日网易互娱笔试
- Verilog实现---时钟信号的90°相移
热门文章
- Ingenuous Cubrency——完全背包
- android imageview 图片模糊,android – Imageview变得模糊
- 如何在PDF文件中提取图片?PDF图片提取教程
- vb.net设置分辨率和缩放比例_配置高不一定性能强,Win 10做好这些设置才能“6到飞起”!...
- hp laserjet1010 win7 64打印慢问题
- 树莓派能做什么?如何使用树莓派
- python给批量图片添加文字_Python之利用PIL批量给图片添加文字
- 【C补充】qsort函数 —— 数组元素排序
- 旧手机改造成Linux服务器
- 人工智能的主要优势是什么?