Android自定义控件——点赞效果(仿Twitter)

来源:互联网 发布:知乎手机版怎么收藏 编辑:程序博客网 时间:2024/06/15 01:14

前言

通过自定义控件,意欲模仿Twitter的点赞效果。
主要涉及:
1.三次贝塞尔曲线应用;
2.属性动画的综合应用;
3.自定义View流程.

拆解原效果

我们先看一下Twitter上的原版效果是怎样的.
放大后:

好吧!原速的看不太清楚,逐帧延迟后:

因为这个效果有需要使用多个动画杂糅而成,为了更确切得出每个子动画阶段所占比例还是用PS大法把它打开,根据该阶段的帧数以及总帧数来确定动画时长如何分配。

实现

1.动画控制

这里使用ValueAnimator并设置插值器为LinearInterpolator来获得随时间正比例变化的逐渐增大的整数值。这个整数值在这里有三个作用。

  1. 每监听到一个整数值变化重绘一次View.
  2. 根据整数值的大小范围来划分所处的不同阶段,这里共划分为五个状态.
    • 绘制心形并伴随缩小和颜色渐变.
    • 绘制圆并伴随放大和颜色渐变.
    • 绘制圆环并伴随放大和颜色渐变.
    • 圆环减消失、心形放大、周围环绕十四圆点.
    • 环绕的十四圆点向外移动并缩小、透明度渐变、渐隐.
  3. 以整数值为基础来实现其他动画效果避免出现大量的ObjectAnimator.
 /**     * 展现View点击后的变化效果     */    private void startViewMotion() {        if (animatorTime != null && animatorTime.isRunning())            return;        resetState();        animatorTime = ValueAnimator.ofInt(0, 1200);        animatorTime.setDuration(mCycleTime);        animatorTime.setInterpolator(new LinearInterpolator());//需要随时间匀速变化        animatorTime.start();        animatorTime.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                int animatedValue = (int) animation.getAnimatedValue();                if (animatedValue == 0) {                    if (animatorArgb == null || !animatorArgb.isRunning()) {                        animatorArgb = ofArgb(mDefaultColor, 0Xfff74769, 0Xffde7bcc);                        animatorArgb.setDuration(mCycleTime * 28 / 120);                        animatorArgb.setInterpolator(new LinearInterpolator());                        animatorArgb.start();                    }                } else if (animatedValue <= 100) {                    float percent = calcPercent(0f, 100f, animatedValue);                    mCurrentRadius = (int) (mRadius - mRadius * percent);                    if (animatorArgb != null && animatorArgb.isRunning())                        mCurrentColor = (int) animatorArgb.getAnimatedValue();                    mCurrentState = HEART_VIEW;                    invalidate();                } else if (animatedValue <= 280) {                    float percent = calcPercent(100f, 340f, animatedValue);//此阶段未达到最大半径                    mCurrentRadius = (int) (2 * mRadius * percent);                    if (animatorArgb != null && animatorArgb.isRunning())                        mCurrentColor = (int) animatorArgb.getAnimatedValue();                    mCurrentState = CIRCLE_VIEW;                    invalidate();                } else if (animatedValue <= 340) {                    float percent = calcPercent(100f, 340f, animatedValue);//半径接上一阶段增加,此阶段外环半径已经最大值                    mCurrentPercent = 1f - percent + 0.2f > 1f ? 1f : 1f - percent + 0.2f;//用于计算圆环宽度,最小0.2,与动画进度负相关                    mCurrentRadius = (int) (2 * mRadius * percent);                    if (animatorArgb != null && animatorArgb.isRunning())                        mCurrentColor = (int) animatorArgb.getAnimatedValue();                    mCurrentState = RING_VIEW;                    invalidate();                } else if (animatedValue <= 480) {                    float percent = calcPercent(340f, 480f, animatedValue);//内环半径增大直至消亡                    mCurrentPercent = percent;                    mCurrentRadius = (int) (2 * mRadius);//外环半径不再改变                    mCurrentState = RING_DOT__HEART_VIEW;                    invalidate();                } else if (animatedValue <= 1200) {                    float percent = calcPercent(480f, 1200f, animatedValue);                    mCurrentPercent = percent;                    mCurrentState = DOT__HEART_VIEW;                    if (animatedValue == 1200) {                        animatorTime.cancel();                        animatorTime.removeAllListeners();                        state = true;                    }                    invalidate();                }            }        });    }

2.图形绘制

心形

这里使用贝塞尔曲线来绘制心形,通过四组控制点的改变来拟合心形。当然项目中为了方便此处的绘制可以用图片代替。

    //绘制心形    private void drawHeart(Canvas canvas, int radius, int color) {        initControlPoints(radius);        mPaint.setColor(color);        mPaint.setAntiAlias(true);        mPaint.setStyle(Paint.Style.FILL);        Path path = new Path();        path.moveTo(tPointB.x, tPointB.y);        path.cubicTo(tPointC.x, tPointC.y, rPointA.x, rPointA.y, rPointB.x, rPointB.y);        path.cubicTo(rPointC.x, rPointC.y, bPointC.x, bPointC.y, bPointB.x, bPointB.y);        path.cubicTo(bPointA.x, bPointA.y, lPointC.x, lPointC.y, lPointB.x, lPointB.y);        path.cubicTo(lPointA.x, lPointA.y, tPointA.x, tPointA.y, tPointB.x, tPointB.y);        canvas.drawPath(path, mPaint);    }

其他

还有一些 圆、圆点、圆环的绘制比较简单这里不再列出,重点是这些图形叠加交错的动画变化。

3.点击事件

对外提供点击事件监听,以便处理点赞与取消点赞的逻辑。

  @Override    public boolean onTouchEvent(MotionEvent event) {        int x = (int) event.getX();        int y = (int) event.getY();        int action = event.getAction();        switch (action) {            case MotionEvent.ACTION_DOWN:                break;            case MotionEvent.ACTION_MOVE:                break;            case MotionEvent.ACTION_UP:                if (x + getLeft() < getRight() && y + getTop() < getBottom()) {//点击在View区域内                    if (state) {                        deselectLike();                    } else {                        startViewMotion();                    }                    if (mListener != null)                        mListener.onClick(this);                }                break;        }        return true;    }

对外提供设置监听的方法

    @Override    public void setOnClickListener(@Nullable OnClickListener l) {        mListener = l;    }

获取是否已点赞的状态

  /**     * Indicates whether this LikeView is  selected  or not.     *     * @return true if the LikeView is selected now, false is deselected     */    public boolean getState() {        return this.state;    }

4.最终效果

总结

这里大致实现了Twitter的点赞效果。虽然是根据原效果图像帧比例来确定动画应分配时间的,放慢观察似乎还是不太理想。另有些状态确定不了是颜色渐变还是透明度变化,临界消失时缩放有没有伴随移动,这些都从简处理了。

需要强调一下的是这里用到了颜色渐变动画,而这个方法系统是API21才提供的,
这里直接拷贝系统源码的ArgbEvaluator到项目里了,其实就相当于属性动画自定义TypeEvaluator,既然源码里有,就不客气了。

源码:https://github.com/qkxyjren/LikeView

欢迎指正

阅读全文
2 0
原创粉丝点击