前段时间在写直播的时候,需要观众在看直播的时候点赞的效果,在此参照了腾讯大神写的点赞(飘心动画效果)。下面是效果图:

1.自定义飘心动画的属性

在attrs.xml 中增加自定义的属性

<!-- 飘心动画自定义的属性 --><declare-styleable name="HeartLayout"><attr name="initX" format="dimension"/><attr name="initY" format="dimension"/><attr name="xRand" format="dimension"/><attr name="animLengthRand" format="dimension"/><attr name="xPointFactor" format="dimension"/><attr name="animLength" format="dimension"/><attr name="heart_width" format="dimension"/><attr name="heart_height" format="dimension"/><attr name="bezierFactor" format="integer"/><attr name="anim_duration" format="integer"/></declare-styleable>

2.定义飘心默认值

2.1 dimens.xml

<!-- 飘星 --><dimen name="heart_anim_bezier_x_rand">50.0dp</dimen><dimen name="heart_anim_init_x">50.0dp</dimen><dimen name="heart_anim_init_y">25.0dp</dimen><dimen name="heart_anim_length">400.0dp</dimen><dimen name="heart_anim_length_rand">350.0dp</dimen><dimen name="heart_anim_x_point_factor">30.0dp</dimen><dimen name="heart_size_height">27.3dp</dimen><dimen name="heart_size_width">32.5dp</dimen>

2.2 integers.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><integer name="heart_anim_bezier_factor">6</integer><integer name="anim_duration">3000</integer></resources>

3.定义飘心动画控制器

3.1 AbstractPathAnimator.java

public abstract class AbstractPathAnimator {private final Random mRandom;protected final Config mConfig;public AbstractPathAnimator(Config config) {mConfig = config;mRandom = new Random();}public float randomRotation() {return mRandom.nextFloat() * 28.6F - 14.3F;}public Path createPath(AtomicInteger counter, View view, int factor) {Random r = mRandom;int x = r.nextInt(mConfig.xRand);int x2 = r.nextInt(mConfig.xRand);int y = view.getHeight() - mConfig.initY;int y2 = counter.intValue() * 15 + mConfig.animLength * factor + r.nextInt(mConfig.animLengthRand);factor = y2 / mConfig.bezierFactor;x = mConfig.xPointFactor + x;x2 = mConfig.xPointFactor + x2;int y3 = y - y2;y2 = y - y2 / 2;Path p = new Path();p.moveTo(mConfig.initX, y);p.cubicTo(mConfig.initX, y - factor, x, y2 + factor, x, y2);p.moveTo(x, y2);p.cubicTo(x, y2 - factor, x2, y3 + factor, x2, y3);return p;}public abstract void start(View child, ViewGroup parent);public static class Config {public int initX;public int initY;public int xRand;public int animLengthRand;public int bezierFactor;public int xPointFactor;public int animLength;public int heartWidth;public int heartHeight;public int animDuration;static public Config fromTypeArray(TypedArray typedArray, float x, float y, int pointx, int heartWidth, int heartHeight) {Config config = new Config();Resources res = typedArray.getResources();config.initX = (int) typedArray.getDimension(R.styleable.HeartLayout_initX,x);config.initY = (int) typedArray.getDimension(R.styleable.HeartLayout_initY,y);config.xRand = (int) typedArray.getDimension(R.styleable.HeartLayout_xRand,res.getDimensionPixelOffset(R.dimen.heart_anim_bezier_x_rand));config.animLength = (int) typedArray.getDimension(R.styleable.HeartLayout_animLength,res.getDimensionPixelOffset(R.dimen.heart_anim_length));//动画长度config.animLengthRand = (int) typedArray.getDimension(R.styleable.HeartLayout_animLengthRand,res.getDimensionPixelOffset(R.dimen.heart_anim_length_rand));config.bezierFactor = typedArray.getInteger(R.styleable.HeartLayout_bezierFactor,res.getInteger(R.integer.heart_anim_bezier_factor));config.xPointFactor = pointx;
//            config.heartWidth = (int) typedArray.getDimension(R.styleable.HeartLayout_heart_width,
//                    res.getDimensionPixelOffset(R.dimen.heart_size_width));//动画图片宽度
//            config.heartHeight = (int) typedArray.getDimension(R.styleable.HeartLayout_heart_height,
//                    res.getDimensionPixelOffset(R.dimen.heart_size_height));//动画图片高度config.heartWidth = heartWidth;config.heartHeight = heartHeight;config.animDuration = typedArray.getInteger(R.styleable.HeartLayout_anim_duration,res.getInteger(R.integer.anim_duration));//持续期return config;}}
}

3.2 PathAnimator.java

/*** 飘心路径动画器*/
public class PathAnimator extends AbstractPathAnimator {private final AtomicInteger mCounter = new AtomicInteger(0);private Handler mHandler;public PathAnimator(Config config) {super(config);mHandler = new Handler(Looper.getMainLooper());}@Overridepublic void start(final View child, final ViewGroup parent) {parent.addView(child, new ViewGroup.LayoutParams(mConfig.heartWidth, mConfig.heartHeight));FloatAnimation anim = new FloatAnimation(createPath(mCounter, parent, 2), randomRotation(), parent, child);anim.setDuration(mConfig.animDuration);anim.setInterpolator(new LinearInterpolator());anim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationEnd(Animation animation) {mHandler.post(new Runnable() {@Overridepublic void run() {parent.removeView(child);}});mCounter.decrementAndGet();}@Overridepublic void onAnimationRepeat(Animation animation) {}@Overridepublic void onAnimationStart(Animation animation) {mCounter.incrementAndGet();}});anim.setInterpolator(new LinearInterpolator());child.startAnimation(anim);}static class FloatAnimation extends Animation {private PathMeasure mPm;private View mView;private float mDistance;private float mRotation;public FloatAnimation(Path path, float rotation, View parent, View child) {mPm = new PathMeasure(path, false);mDistance = mPm.getLength();mView = child;mRotation = rotation;parent.setLayerType(View.LAYER_TYPE_HARDWARE, null);}@Overrideprotected void applyTransformation(float factor, Transformation transformation) {Matrix matrix = transformation.getMatrix();mPm.getMatrix(mDistance * factor, matrix, PathMeasure.POSITION_MATRIX_FLAG);mView.setRotation(mRotation * factor);float scale = 1F;if (3000.0F * factor < 200.0F) {scale = scale(factor, 0.0D, 0.06666667014360428D, 0.20000000298023224D, 1.100000023841858D);} else if (3000.0F * factor < 300.0F) {scale = scale(factor, 0.06666667014360428D, 0.10000000149011612D, 1.100000023841858D, 1.0D);}mView.setScaleX(scale);mView.setScaleY(scale);transformation.setAlpha(1.0F - factor);}}private static float scale(double a, double b, double c, double d, double e) {return (float) ((a - b) / (c - b) * (e - d) + d);}
}

4.定义飘心界面

4.1 HeartView.java

/*** 飘心动画的界面*/
public class HeartView extends ImageView{//绘制的时候抗锯齿private static final Paint sPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);private static final Canvas sCanvas = new Canvas();private int mHeartResId = R.drawable.heart0;private int mHeartBorderResId = R.drawable.heart1;private static Bitmap sHeart;private static Bitmap sHeartBorder;public HeartView(Context context) {super(context);}public HeartView(Context context, AttributeSet attrs) {super(context, attrs);}public HeartView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public void setDrawable(int resourceId){Bitmap heart = BitmapFactory.decodeResource(getResources(), resourceId);// Sets a drawable as the content of this ImageView.setImageDrawable(new BitmapDrawable(getResources(),heart));}public void setColor(int color) {Bitmap heart = createHeart(color);setImageDrawable(new BitmapDrawable(getResources(), heart));}public void setColorAndDrawables(int color, int heartResId, int heartBorderResId) {if (heartResId != mHeartResId) {sHeart = null;}if (heartBorderResId != mHeartBorderResId) {sHeartBorder = null;}mHeartResId = heartResId;mHeartBorderResId = heartBorderResId;setColor(color);}public Bitmap createHeart(int color) {if (sHeart == null) {sHeart = BitmapFactory.decodeResource(getResources(), mHeartResId);}if (sHeartBorder == null) {sHeartBorder = BitmapFactory.decodeResource(getResources(), mHeartBorderResId);}Bitmap heart = sHeart;Bitmap heartBorder = sHeartBorder;Bitmap bm = createBitmapSafely(heartBorder.getWidth(), heartBorder.getHeight());if (bm == null) {return null;}Canvas canvas = sCanvas;canvas.setBitmap(bm);Paint p = sPaint;canvas.drawBitmap(heartBorder, 0, 0, p);p.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP));float dx = (heartBorder.getWidth() - heart.getWidth()) / 2f;float dy = (heartBorder.getHeight() - heart.getHeight()) / 2f;canvas.drawBitmap(heart, dx, dy, p);p.setColorFilter(null);canvas.setBitmap(null);return bm;}private static Bitmap createBitmapSafely(int width, int height) {try {return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);} catch (OutOfMemoryError error) {error.printStackTrace();}return null;}
}

4.2 飘心动画路径布局

HeartLayout.java

/*** 飘心动画路径*/
public class HeartLayout extends RelativeLayout implements View.OnClickListener {private AbstractPathAnimator mAnimator;private AttributeSet attrs = null;private int defStyleAttr = 0;private OnHearLayoutListener onHearLayoutListener;private static HeartHandler heartHandler;private static HeartThread heartThread;public void setOnHearLayoutListener(OnHearLayoutListener onHearLayoutListener) {this.onHearLayoutListener = onHearLayoutListener;}public interface OnHearLayoutListener {boolean onAddFavor();}public HeartLayout(Context context) {super(context);findViewById(context);}public HeartLayout(Context context, AttributeSet attrs) {super(context, attrs);this.attrs = attrs;findViewById(context);}public HeartLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.attrs = attrs;this.defStyleAttr = defStyleAttr;findViewById(context);}private Bitmap bitmap;private void findViewById(Context context) {LayoutInflater.from(context).inflate(R.layout.ly_periscope, this);bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_like);dHeight = bitmap.getWidth()/2;dWidth = bitmap.getHeight()/2;textHight = sp2px(getContext(), 20) + dHeight / 2;pointx = dWidth;//随机上浮方向的x坐标bitmap.recycle();}private int mHeight;private int mWidth;private int textHight;private int dHeight;private int dWidth;private int initX;private int pointx;public static int sp2px(Context context, float spValue) {final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale + 0.5f);}public class HeartHandler extends Handler {public final static int MSG_SHOW = 1;WeakReference<HeartLayout> wf;public HeartHandler(HeartLayout layout) {wf = new WeakReference<HeartLayout>(layout);}@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);HeartLayout layout = wf.get();if (layout == null) return;switch (msg.what) {case MSG_SHOW:addFavor();break;}}}public class HeartThread implements Runnable {private long time = 0;private int allSize = 0;public void addTask(long time, int size) {this.time = time;allSize += size;}public void clean() {allSize = 0;}@Overridepublic void run() {if (heartHandler == null) return;if (allSize > 0) {heartHandler.sendEmptyMessage(HeartHandler.MSG_SHOW);allSize--;}postDelayed(this, time);}}private void init(AttributeSet attrs, int defStyleAttr) {final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.HeartLayout, defStyleAttr, 0);if (pointx <= initX && pointx >= 0) {pointx -= 10;} else if (pointx >= -initX && pointx <= 0) {pointx += 10;} else pointx = initX;mAnimator = new PathAnimator(AbstractPathAnimator.Config.fromTypeArray(a, initX, textHight, pointx, dWidth, dHeight));a.recycle();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//获取本身的宽高 这里要注意,测量之后才有宽高mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();initX = mWidth / 2 - dWidth / 2;}public AbstractPathAnimator getAnimator() {return mAnimator;}public void setAnimator(AbstractPathAnimator animator) {clearAnimation();mAnimator = animator;}public void clearAnimation() {for (int i = 0; i < getChildCount(); i++) {getChildAt(i).clearAnimation();}removeAllViews();}private static int[] drawableIds = new int[]{R.drawable.heart0, R.drawable.heart1, R.drawable.heart2, R.drawable.heart3, R.drawable.heart4, R.drawable.heart5, R.drawable.heart6, R.drawable.heart7, R.drawable.heart8,};private Random random = new Random();public void addFavor() {HeartView heartView = new HeartView(getContext());heartView.setDrawable(drawableIds[random.nextInt(8)]);init(attrs, defStyleAttr);mAnimator.start(heartView, this);}private long nowTime, lastTime;final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999,99999999, 999999999, Integer.MAX_VALUE};public static int sizeOfInt(int x) {for (int i = 0; ; i++)if (x <= sizeTable[i])return i + 1;}public void addFavor(int size) {switch (sizeOfInt(size)) {case 1:size = size % 10;break;default:size = size % 100;}if (size == 0) return;nowTime = System.currentTimeMillis();long time = nowTime - lastTime;if (lastTime == 0)time = 2 * 1000;//第一次分为2秒显示完time = time / (size + 15);if (heartThread == null) {heartThread = new HeartThread();}if (heartHandler == null) {heartHandler = new HeartHandler(this);heartHandler.post(heartThread);}heartThread.addTask(time, size);lastTime = nowTime;}public void addHeart(int color) {HeartView heartView = new HeartView(getContext());heartView.setColor(color);init(attrs, defStyleAttr);mAnimator.start(heartView, this);}public void addHeart(int color, int heartResId, int heartBorderResId) {HeartView heartView = new HeartView(getContext());heartView.setColorAndDrawables(color, heartResId, heartBorderResId);init(attrs, defStyleAttr);mAnimator.start(heartView, this);}@Overridepublic void onClick(View v) {int i = v.getId();if (i == R.id.img) {if (onHearLayoutListener != null) {boolean isAdd = onHearLayoutListener.onAddFavor();if (isAdd) addFavor();}}}public void clean() {if (heartThread != null) {heartThread.clean();}}public void release() {if (heartHandler != null) {heartHandler.removeCallbacks(heartThread);heartThread = null;heartHandler = null;}}
}

5.飘心动画的使用

5.1 activity_heart_animal.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/grey"android:alpha="0.5"><TextView
            android:id="@+id/member_send_good"android:layout_width="40dp"android:layout_height="40dp"android:layout_gravity="center"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginRight="30dp"android:layout_marginBottom="10dp"android:background="@drawable/live_like_icon"/><!-- 飘心的路径 -->   <com.myapplication2.app.newsdemo.view.heartview.HeartLayout
            android:id="@+id/heart_layout"android:layout_width="100dp"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_alignParentBottom="true"/></RelativeLayout>

5.2 activity 中的使用

heartLayout = (HeartLayout)findViewById(R.id.heart_layout);findViewById(R.id.member_send_good).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {heartLayout.addFavor();}});

heartLayout.addFavor(); 就是触发飘心动画效果的关键代码

6.参看资料

https://github.com/zhaoyang21cn/Android_Suixinbo

android 飘心动画(直播点赞)效果相关推荐

  1. android 飘心动画(直播点赞)效果(二)---贝塞尔曲线的实现

    上篇文章 android 飘心动画(直播点赞)效果 只有代码,没有相关的说明.因为我自己也没有看懂,所以参照网上另一篇关于贝塞尔曲线实现 飘心动画的效果,目的就是 便于理解上篇文章代码的思路,然后写个 ...

  2. 直播app源代码 直播软件开发Android UI动画 仿直播点赞飘心动画效果

    直播app源代码 直播软件开发Android UI动画 仿直播点赞飘心动画效果 一个飘心的小动画,之前看也看到网上有很多轮子,但是感觉不是很符合我的需求,所以自己就凑活凑活搞出来一个,废话不多说先看图 ...

  3. 高级UI特效仿直播点赞效果—一个优美炫酷的点赞动画

    一个优美炫酷的点赞效果 效果图如下: 攻克难点: 心形图片的路径等走向 心形图片的控制范围 部分代码如下: 通过AbstractPathAnimator定义飘心动画控制器 @Overridepubli ...

  4. android+直播点赞,Android自定义View实现直播点赞特效的方法

    Android自定义View实现直播点赞特效的方法 发布时间:2020-07-30 09:24:13 来源:亿速云 阅读:77 作者:小猪 这篇文章主要讲解了Android自定义View实现直播点赞特 ...

  5. android红心点赞动画,Android控件实现直播App特效之点赞飘心动画

    现在市面上直播类的应用可以说是一抓一大把,随随便便就以什么主题来开发个直播App,说白了就想在这领域分杯羹.在使用这些应用过程中其实不难发现,在所有的直播界面,少不了的就是各种打赏.各种点赞.今天自己 ...

  6. android红心点赞动画,Android控件实现直播App点赞飘心动画

    现在市面上直播类的应用可以说是一抓一大把,随随便便就以什么主题来开发个直播App,说白了就想在这领域分杯羹.在使用这些应用过程中其实不难发现,在所有的直播界面,少不了的就是各种打赏.各种点赞.今天自己 ...

  7. Android/安卓仿淘宝直播点赞效果/qq空间点赞效果动画

    之前玩淘宝误入它的直播频道,发现它的直播界面的点赞效果挺好看,然后发现QQ控件点赞有类似动画,于是趁有空花了点时间玩玩. 先上个效果图: 添加了一个按钮模拟点赞,点击多少次就出现多个水果,他们的运动轨 ...

  8. android 直播评论动画,Android自定义View实现直播点赞特效

    由于开发的需要,需要开发类似直播点赞特效的需求,于是自定义View来实现这种效果 案例图: 1.自定义View import android.animation.Animator; import an ...

  9. 自定义View:仿抖音直播点赞效果

    目录 一.效果图 1.第一版本:在屏幕底部开始显示 2.第二版本:点击任意位置都可以显示 3.第三版本:给任意控件添加点赞效果 二.代码 1.第一版本代码 源码: 示例: 2.第二版本代码 源码(主要 ...

最新文章

  1. VS2010测试功能之旅:编码的UI测试(6)- 提高UI测试稳定性的8个方法(下)
  2. Extjs 4.0.7 中模式窗口的CURD
  3. matlab cdf,Matlab 简单计算PDF和CDF | 学步园
  4. ubuntu下制作u盘镜像_deepin下制作win10启动U盘
  5. ansible软件部署
  6. 算法(三):图解广度优先搜索算法
  7. 计算机辅助初中数学教学,计算机辅助数学教学的优势
  8. java自行车火多重,多重继承的演变
  9. 【PL/SQL】测试函数时,日期参数的输入格式
  10. 8051单片机Proteus仿真与开发实例-RS485协议通信仿真
  11. TensorFlow 莫烦python
  12. EXCEL下拉菜单怎么弄
  13. 10、JDBC(重点)
  14. MySQLworkbench中PK,NN,UQ意思详解
  15. Java学习——File类
  16. docker搭建zabbix服务
  17. bert的兄弟姐妹梳理——Roberta、DeBerta、Albert、Ambert、Wobert等
  18. 某手机在-20度环境下,只能读到电池温度为-18℃
  19. RPLIDAR A2 rviz显示雷达数据教程
  20. 通达信接口的登录调试步骤

热门文章

  1. 关注老年健康雨中情为养老院维修渗漏
  2. Jedis的简单介绍和基本使用
  3. Arduino使用ESP8266+点灯科技+小爱音响实现语音控制开关
  4. 微财富兜底映射互联网金融弊端
  5. 小马哥---高仿苹果6s 主板型号 T618 9900真实6571芯片刷机拆机主板多图展示
  6. 遍历qvector_顺序储存容器QVector | 学步园
  7. 如何在图片上添加贴图?试试这几种方法
  8. windows系统关闭开机启动项
  9. 8种常见的HTTP请求方式你知道多少?
  10. iPhone上的CPU架构,核数以及运行内存