1. 程式人生 > >Android自定義控制元件——點贊效果(仿Twitter)

Android自定義控制元件——點贊效果(仿Twitter)

前言

通過自定義控制元件,意欲模仿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,既然原始碼裡有,就不客氣了。

歡迎指正