一个来自腾讯云直播的点赞飘心效果

最近在看直播相关的文档,在使用腾讯直播,发现了这个点赞效果。

于是作为一个代码的搬运工,本着搬运的精神,把点赞这部分的源码给copy了出来。
并且独立创建成一个 Library 托管到了jitpack.io。以后要使用的话直接compile导入就行了。
仓库地址:https://github.com/wapchief/LikeStarAnimation

  • 想直接使用,请看文章结尾

源码介绍

代码并不多,除了一些资源,总共代码加起来也不到一千行。
分别由四个类来分工控制视图、路径、参数设置。

TCHeartView

继承自 ImageView,点击时产生的动画图片,每次点击都会创建一个。所以在不停的点击的时候就会创建多个 ImageView。

自定义 ImageView 向外提供两个方法,来分别设置图片和图片颜色。但如果都设置的情况下,默认图片会都变色,setDrawable 就会失效。

   public void setDrawable(BitmapDrawable bitmap) {
        setImageDrawable(bitmap);
    }
   public void setColor(int color) {
        Bitmap heart = createHeart(color);
        setImageDrawable(new BitmapDrawable(getResources(), heart));
    }

createHeart 是设置图片的颜色的方法,在使用setColor后,会将图片使用 Canvas 和 Paint 对其重新绘制,改变图片颜色。所以在使用 setColor 后原有的 img 资源都会失效。


 private 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;
    }

TCAbstractPathAnimator 飘心动画属性

动画需要用到的参数:

        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;
        //动画持续时间(TCHeartView)
        public int animDuration;

根据参数去绘制动画飘动的路径。

    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;
    }

TCPathAnimator 动画控制器

    private final static int MAX_PATH_COUNTS = 10;      //最多生成的路径数目
    private int mCurrentPathCounts = 0;                 //已经生成的路径数目

在这里面需要自定义 MyAnimation 继承自 Animation ,重写 applyTransformation 方法,主要是为了控制 View 的透明度,将默认的通过 interpolatedTime 差值器 转换成自己想要的效果。
这里需要自己算法实现。

static class MyAnimation extends Animation {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            super.applyTransformation(interpolatedTime, t);
            //设置旋转角度
            mView.setRotation(mRotation * interpolatedTime);
            //设置缩放范围
            mView.setScaleX(scale);
            mView.setScaleY(scale);
            //透明度
            transformation.setAlpha(1.0F - interpolatedTime);
        }
    }

applyTransformation 文档说明

/**
     * Helper for getTransformation. Subclasses should implement this to apply
     * their transforms given an interpolation value.  Implementations of this
     * method should always replace the specified Transformation or document
     * they are doing otherwise.
     *
     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
     *        after it has been run through the interpolation function.
     * @param t The Transformation object to fill in with the current
     *        transforms.
     */

然后是启动动画,设置参数,并监听。
通过监听,来检测当动画结束的时候移除掉 View.

FloatAnimation anim = new FloatAnimation(path, randomRotation(), parent, child);
        //设置动画持续时间
        anim.setDuration(mConfig.animDuration);
        anim.setInterpolator(new LinearInterpolator());
        anim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationEnd(Animation animation) {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        parent.removeView(child);
                    }
                });
                mCounter.decrementAndGet();
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

            @Override
            public void onAnimationStart(Animation animation) {
                mCounter.incrementAndGet();
            }
        });
        child.startAnimation(anim);

TCHeartLayout 布局继承 RelativeLayout

用于展示和参数设置。

在这个页面可以封装一些公共参数给外部调用,比如自定义 动画持续时间、动画颜色、ImageView 图片资源。
注意要在设置参数的时候重新初始化一下设置,否则不生效。

初始化的方法分两部分:
一部分是针对 Animation ,可以重新设置一些 变量,数值,用来控制动画的改变。

TCAbstractPathAnimator.Config config = TCAbstractPathAnimator.Config
                .fromTypeArray(a, initX, textHight, pointx, dWidth, dHeight);
        config.animDuration = getAnimalTime();
        mAnimator = new TCPathAnimator(config);

另一部分是初始化 View
包括设置 Drawable 资源

 private void initHeartDrawable() {
        int size = drawableIds.length;
        sDrawables = new Drawable[size];
        for (int i = 0 ; i < size; i++) {
            sDrawables[i] =getResources().getDrawable(drawableIds[i]);
        }
        resourceLoad();
    }

最后就是点赞最终执行的代码
每次点击都要重新绘制一个 自定义的 TCHeartView,然后分别给 TCHeartView 设置 Drawable 资源,当然要修改颜色的话也在这里修改,当前只是设置所有的 View 颜色,后续可以完善,分别设置不同的颜色。


    public void addFavor() {
        TCHeartView heartView = new TCHeartView(getContext());
        heartView.setDrawable(mHeartsDrawable[mRandom.nextInt(getDrawableIds().length-1)]);
        if (imgColor!=0) {
            heartView.setColor(getImgColor());
        }
        mAnimator.start(heartView, this);
    }

具体使用

在原有的基础上,添加了几个自定义的参数配置,方便修改。

  1. 在项目 Project 根目录 下 build.gradle 中添加
    allprojects {
        repositories {
            ...
            maven { url 'https://www.jitpack.io' }
        }
    }

  1. 在项目 modle/app 下的build.gradle 中引入
    dependencies {
            compile 'com.github.wapchief:LikeStarAnimation:1.0.4'
    }
  1. 添加布局文件
        <com.wapchief.likestarlibrary.like.TCHeartLayout
            android:id="@+id/heart_layout"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:focusable="true" />

  1. 如果不需要自定义,直接调用 addFavor 就可以了
            public void onClick(View v) {
                mHeartLayout.addFavor();
            }

其它支持自定义的方法

        //图片数组资源
        mHeartLayout.setDrawableIds(new int[]{R.drawable.img});
        //动画持续时长
        mHeartLayout.setAnimalTime(2000);
        //单个图片的颜色
        mHeartLayout.setImgColor(Color.parseColor("#000000"));
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,565评论 25 707
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 4,998评论 5 13
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,321评论 6 30
  • 作者:晴妞Q妈 1. “美人儿!” 晚九点半,准时进门的晴妞,一边换鞋一边叫着她给Q妈起的爱称。 后边跟着去校车停...
    晴妞Q妈阅读 185评论 1 1
  • 绿萝沉寂了半年,开始冒出新芽,拔出一枝超出花盆很远的枝叶,又重新找了一个玻璃瓶插着,昨天摘的栀子花还是冒着绿的花苞...
    婆婆纳的婆言婆语阅读 158评论 0 0