Android自定义View程序员Android技术知识

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

2018-03-29  本文已影响184人  八怪不姓丑

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

于是作为一个代码的搬运工,本着搬运的精神,把点赞这部分的源码给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"));
上一篇下一篇

猜你喜欢

热点阅读