Android Canvas 實現(xiàn)抽屜動畫和翻滾動畫

需求

在開發(fā)中,有這樣的需求,view在某種操作下會有抽屜的展示,而且里面的文字會有翻滾的效果,如下:



即列表向上滑動時,旁邊的氣泡會折疊(并未折疊所有),向下滑動時,氣泡會展開,當(dāng)有新內(nèi)容時,里面的文字內(nèi)容會翻滾顯示。

實現(xiàn)

所有的效果都是通過Canvans來實現(xiàn),也是希望借該博客來和大家來分享一下,當(dāng)開發(fā)時視覺需要實現(xiàn)某些動畫效果時,第一想到的不是通過Google去搜索別人的實現(xiàn),然后去套用,而是沉下心來去思考,去分解動畫,然后通過Canvas去實現(xiàn),當(dāng)你這樣做以后,你會發(fā)現(xiàn)實際上并非這么難,才能以不變應(yīng)萬變。這樣當(dāng)需求改變時,才能很快的解決。

抽屜效果

  • 分解展開動畫


    ?上圖可以看出,動畫從第一幅圖到最后一幅圖右邊的圓角是不變的,這時候很容易就有思路,假設(shè)整個動畫的持續(xù)時間是500ms,我們只需要在500ms之內(nèi)勻速?繪制一個半圓角矩形就可以了。

  • 代碼實現(xiàn)

    繪制初始化圓角矩形

    
    private void drawView(Canvas canvas) {
       mDrawPath.reset();
       mDrawPath.addRoundRect(mExpandViewRect, mRadiusRectF, Path.Direction.CW);
       canvas.drawPath(mDrawPath, mBorderPaint);
       //繪制背景
       mDrawPath.reset();
       mDrawPath.addRoundRect(mExpandViewRect, mRadiusRectF, Path.Direction.CW);
       canvas.drawPath(mDrawPath, mBackPaint);
    }
    
    

    我們通過Path來繪制半圓角矩形,每個角的角度可以自己設(shè)置。如下代碼:

    
    public void setRadius(float leftTop, float rightTop, float rightBottom, float leftBottom) {
        mRadiusRectF[0] = leftTop;
        mRadiusRectF[1] = leftTop;
        mRadiusRectF[2] = rightTop;
        mRadiusRectF[3] = rightTop;
        mRadiusRectF[4] = rightBottom;
        mRadiusRectF[5] = rightBottom;
        mRadiusRectF[6] = leftBottom;
        mRadiusRectF[7] = leftBottom;
    }
    
    

    當(dāng)外界觸發(fā)展開操作時,設(shè)置當(dāng)前狀態(tài)為從展開到折疊,然后在onDraw函數(shù)里進行繪制,如果設(shè)置動畫時間為500ms。

    
    private void drawExpand2CollapseView(Canvas canvas) {
        float ratio = getAnimRatio();
        float distance = mExpandOrCollapseDistance * ratio;
        int left = (int) (mBorderWidth + distance);
    
        mExpandViewRect.set(left, mExpandViewRect.top, mExpandViewRect.right, mExpandViewRect.bottom);
        drawView(canvas);
        drawCollapseText(canvas);
        if (ratio == 1) {
            mCurrentStatus = STATUS_COLLAPSE;
        } else {
            invalidate();
        }
    }
    
    private float getAnimRatio() {
        long now = System.currentTimeMillis();
        float ratio = (now - mStartTime) / mDrawerAnimDuration;
        return ratio >= 1 ? 1 : ratio;
    }
    
    

    其中mStartTime是觸發(fā)展開操作的起始時間,通過當(dāng)前時間和起始時間的差值再來除以動畫持續(xù)的時間,得到當(dāng)前的比例,再把比例乘以需要伸縮的那部分距離,就得到當(dāng)前繪制的長度,如果比例為1,則證明時間用完,動畫結(jié)束。大體的思路是這個樣子。

文字在這個過程中怎么處理,完全隨著自己業(yè)務(wù)需要而修改,比方說字體平移,字體變大都可以,只需要拿到這個比例算出值,然后通過canvas來繪制即可,非常方便

翻轉(zhuǎn)效果

android里面的翻轉(zhuǎn)效果很多童鞋會想到用TextSwitcher來實現(xiàn),但是在這種場景下是做不了的,因為首先這個view是一個圓角矩形,如果把他作為TextSwitcher的子view來進行翻轉(zhuǎn),會發(fā)現(xiàn)有圓角哪一側(cè)有很顯然的漏洞,?如下如所示:



所以還是得用Canvas來實現(xiàn)這個動效,視覺要求,當(dāng)有新內(nèi)容來時,里面的文字垂直滾動即可。

  • 分解滾動動畫

    當(dāng)下一個文字來時,上一個文字向上平移并且透明度變小,新來的文字慢慢平移到中間,并且透明度變大。知道了這么多,代碼寫起來就得心應(yīng)手了。下面截下關(guān)鍵代碼:

  • 實現(xiàn)

    
    private void drawNextTextView(Canvas canvas) {
        drawExpandBack(canvas);
    
        float ratio = getNextTextRatio();
        String text;
        Rect bounds;
        Paint.FontMetricsInt fontMetrics = null;
        int baseline;
        float start;
        if (ratio != 1) {
            bounds = new Rect();
            mTextPaint.getTextBounds(mCurrentText, 0, mCurrentText.length(), bounds);
            start = mExpandViewRect.left + mLeftTopRectRadius * 0.8f + (mExpandViewRect.width() - mLeftTopRectRadius - bounds.width()) / 2;
            fontMetrics = mTextPaint.getFontMetricsInt();
            baseline = (int) ((mHeight * (1 - ratio) - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top);
    
            mTextPaint.setAlpha((int) (255 *  (1 - ratio)));
            canvas.drawText(mCurrentText, start, baseline, mTextPaint);
    
            bounds = new Rect();
            mTextPaint.getTextBounds(mNextText, 0, mNextText.length(), bounds);
            start = mExpandViewRect.left + mLeftTopRectRadius * 0.8f + (mExpandViewRect.width() - mLeftTopRectRadius - bounds.width()) / 2;
            baseline = (int) (((mHeight - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top) + mHeight / 2 * (1 - ratio));
    
            mTextPaint.setAlpha((int) (255 * ratio));
            canvas.drawText(mNextText, start, baseline, mTextPaint);
    
            postInvalidateDelayed(50);
        } else {
            mTextPaint.setAlpha(255);
            bounds = new Rect();
            mTextPaint.getTextBounds(mNextText, 0, mNextText.length(), bounds);
            fontMetrics = mTextPaint.getFontMetricsInt();
            start = mExpandViewRect.left + mLeftTopRectRadius * 0.8f + (mExpandViewRect.width() - mLeftTopRectRadius - bounds.width()) / 2;
            baseline = ((mHeight - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top);
    
            canvas.drawText(mNextText, start, baseline, mTextPaint);
    
            String text1 = mNextText;
            mNextText = mCurrentText;
            mCurrentText = text1;
        }
    }
    
    

    基本思路也是設(shè)置一個動畫時間,算出當(dāng)前變化的比例,通過Canvas來不停的繪制文字達到滾動的效果。如果說需要有更立體的效果,比方說翻滾的過程中字體大小也需要改變,按照這樣的思路,設(shè)置paint的textsize?即可。

好,今天的分享就到這里。這里貼出這個效果的 Github Demo地址。?里面還有其他動畫喲,多多支持,點個贊哈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容