Android开发经验谈Android开发Android技术知识

一篇文本跳动控件,为你打开一扇大门,学会这两点心得,控件你也会写

2018-11-29  本文已影响38人  文淑

做开发已经3年有余了,一事无成,一直静不下心来安心做好一件事情,很多时候内心很浮躁,迷失了前行的方向,我该当何去何从?一个人闲下来的时候,总想放纵自己,二个人在一起总会吵吵闹闹,在物质驱使的年代里,活得一点不像自己,像我这样的人,还有多少人?

心里的话一直想找人说,让大家见笑了。本篇是讲技术的,而不是来听我的感慨。希望看完本篇能对你自定义控件有所帮助,大牛大神级别的人物可以绕过。先看看效果图,图文并茂,有助于文章的理解:

跳动文本控件

据群里很大一部分童鞋反应,觉得写自定义控件好难,害怕去写。那我谈谈自己的心得,有两点:

  1. 第一,拆分

  2. 第二,模仿

在平面(2D)动画中我们一般只考虑横纵坐标,那么就可以拆分为 xy 方向的动画,动画无非就是平移,旋转,缩放以及透明度变换中的一种或者几种,化繁为简。

腾讯,小米一直在模仿,一直很成功,那么写控件一样,也需要模仿,尤其对于初学者,更需要模仿。先不论你写的控件是不是最优方案,性能是否有过渡损耗,动手模仿写一写,可以找些简单例子练练手,强力推荐 启舰大牛,写的很细很全面。

看到这里,你有信心写好上方跳动文本控件吗?如果是你拿到这个控件需求,很想听听大家的分析思路,欢迎留言?很多时候实现方案往往不会是一种,你的答案,可能对他人有所帮助,接下来具体讲解跳动文本控件的实现。

跳动文本

需求分析,红色字体的 wen 为已经绘制的文本(旧文本),白色的 tianxia 为即将绘制的文本(新文本),新旧文本相同的字符为 n ,针对新旧文本拆分 x,y 轴方向的动画:

旧文本

新文本

跳动文本控件

拆分后你会发现就只有简单的平移与透明度动画,是不是一下就觉得简单了许多,先实现旧文本的动画。

旧文本

在 x 轴方向字符 n 的平移动画,根据数学公式(n 字符x坐标 = 起点 + (终点 - 起点)x 进度),起点与终点分别对应旧新文本字符 n 所在的 x 轴坐标,进度的取值范围为[0~1];y 轴方向的平移与透明动画就相对简单,请参考代码理解。

获取n 字符x坐标相关方法:

    /**
     * 获取旧文本字符n的x坐标
     *
     * @param from      旧文本字符n的索引位置
     * @param move      新文本字符n的索引位置
     * @param progress  当前进度
     * @param startX    新文本baseline的x坐标
     * @param oldStartX 旧文本baseline的y坐标
     * @param gaps      新文本每个字符对应的x坐标集合
     * @param oldGaps   旧文本每个字符对应的x坐标集合
     * @return
     */
    public static float getOffset(int from, int move, float progress, float startX, float oldStartX,
                                  List<Float> gaps, List<Float> oldGaps) {
                                  
        float dist = startX;
        for (int i = 0; i < move; i++) {
            dist += gaps.get(i);
        }

        float cur = oldStartX;
        for (int i = 0; i < from; i++) {
            cur += oldGaps.get(i);
        }

        return cur + (dist - cur) * progress;
    }

绘制旧文本相关代码,有不懂的地方,请留言或结合demo理解:

        float startX = mHTextView.getLayout().getLineLeft(0);
        float startY = mHTextView.getBaseline();

        float offset = startX;
        float oldOffset = oldStartX;

        int maxLength = Math.max(mText.length(), mOldText.length());

        for (int i = 0; i < maxLength; i++) {

            // draw old text
            if (i < mOldText.length()) {

                // pp = progress; 0~1  progress * duration / (charTime + charTime / mostCount * (mText.length() - 1))
                float pp = progress;

                mOldPaint.setTextSize(mTextSize);
                int move = CharacterUtils.needMove(i, differentList);

                if (move != -1) {
                    // 需要移动的字符 左右平移运动 视觉欺骗并没有上下的运动
                    mOldPaint.setAlpha(255);
                    // * 2f 平移速度
                    float p = pp * 2f;
                    p = p > 1 ? 1 : p;
                    float distX = CharacterUtils.getOffset(i, move, p, startX, oldStartX, gapList, oldGapList);
                    canvas.drawText(mOldText.charAt(i) + "", 0, 1, distX, startY, mOldPaint);
                } else {
                   //  y轴方向的透明度动画
                    mOldPaint.setAlpha((int) ((1 - pp) * 255));
                    //  y轴方向的平移动画
                    float y = startY - pp * mTextHeight;
                    float width = mOldPaint.measureText(mOldText.charAt(i) + "");
                    // (oldGapList.get(i) - width) / 2 值为0   oldOffset + (oldGapList.get(i) - width) / 2
                    canvas.drawText(mOldText.charAt(i) + "", 0, 1, oldOffset, y, mOldPaint);
                }
                oldOffset += oldGapList.get(i);
            }
        }

新文本

新文本只有简单的平移,透明度动画,相关代码如下:

            if (i < mText.length()) {
                if (!CharacterUtils.stayHere(i, differentList)) {
                    // 渐显效果 延迟
                    int alpha = (int) (255f / charTime * (progress * duration - charTime * i / mostCount));
                    alpha = alpha > 255 ? 255 : alpha;
                    alpha = alpha < 0 ? 0 : alpha;
                    mPaint.setAlpha(alpha);
                    mPaint.setTextSize(mTextSize);

                    //   float pp = progress * duration / (charTime + charTime / mostCount * (mText.length() - 1));
                    float pp = progress;
                    // y方向的平移动画
                    float y = mTextHeight + startY - pp * mTextHeight;

                    float width = mPaint.measureText(mText.charAt(i) + "");
                    canvas.drawText(mText.charAt(i) + "", 0, 1, offset + (gapList.get(i) - width) / 2, y, mPaint);

                }
                offset += gapList.get(i);
            }

写好控件在于分析与拆分,再复杂的动画也是由简单的动画组合而成,先化繁为简,后以简组繁,多练多写,没有任何捷径,自我觉得 “开发就像积累经验一样,开发越久,经验越多,你就是老手。”

小编正在维护 MeiWidgetView 库,有炫酷的控件可以推荐,万分感谢,给小编一颗 star,才是最好最美的回报。

源码地址

参考地址:

HTextView

启舰

上一篇 下一篇

猜你喜欢

热点阅读