Android Canvas 实现抽屉动画和翻滚动画

需求

在开发中,有这样的需求,view在某种操作下会有抽屉的展示,而且里面的文字会有翻滚的效果,如下:



即列表向上滑动时,旁边的气泡会折叠(并未折叠所有),向下滑动时,气泡会展开,当有新内容时,里面的文字内容会翻滚显示。

实现

所有的效果都是通过Canvans来实现,也是希望借该博客来和大家来分享一下,当开发时视觉需要实现某些动画效果时,第一想到的不是通过Google去搜索别人的实现,然后去套用,而是沉下心来去思考,去分解动画,然后通过Canvas去实现,当你这样做以后,你会发现实际上并非这么难,才能以不变应万变。这样当需求改变时,才能很快的解决。

抽屉效果

  • 分解展开动画


    �上图可以看出,动画从第一幅图到最后一幅图右边的圆角是不变的,这时候很容易就有思路,假设整个动画的持续时间是500ms,我们只需要在500ms之内匀速�绘制一个半圆角矩形就可以了。

  • 代码实现

    绘制初始化圆角矩形

    
    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来绘制半圆角矩形,每个角的角度可以自己设置。如下代码:

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

    当外界触发展开操作时,设置当前状态为从展开到折叠,然后在onDraw函数里进行绘制,如果设置动画时间为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是触发展开操作的起始时间,通过当前时间和起始时间的差值再来除以动画持续的时间,得到当前的比例,再把比例乘以需要伸缩的那部分距离,就得到当前绘制的长度,如果比例为1,则证明时间用完,动画结束。大体的思路是这个样子。

文字在这个过程中怎么处理,完全随着自己业务需要而修改,比方说字体平移,字体变大都可以,只需要拿到这个比例算出值,然后通过canvas来绘制即可,非常方便

翻转效果

android里面的翻转效果很多童鞋会想到用TextSwitcher来实现,但是在这种场景下是做不了的,因为首先这个view是一个圆角矩形,如果把他作为TextSwitcher的子view来进行翻转,会发现有圆角哪一侧有很显然的漏洞,�如下如所示:



所以还是得用Canvas来实现这个动效,视觉要求,当有新内容来时,里面的文字垂直滚动即可。

  • 分解滚动动画

    当下一个文字来时,上一个文字向上平移并且透明度变小,新来的文字慢慢平移到中间,并且透明度变大。知道了这么多,代码写起来就得心应手了。下面截下关键代码:

  • 实现

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

    基本思路也是设置一个动画时间,算出当前变化的比例,通过Canvas来不停的绘制文字达到滚动的效果。如果说需要有更立体的效果,比方说翻滚的过程中字体大小也需要改变,按照这样的思路,设置paint的textsize�即可。

好,今天的分享就到这里。这里贴出这个效果的 Github Demo地址。�里面还有其他动画哟,多多支持,点个赞哈。

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

推荐阅读更多精彩内容