TextView波浪加载效果

开源框架:https://github.com/RomainPiel/Titanic
效果图:
这里写图片描述
实现原理:

1.构造一个BitmapShader,通过水平方向上重复排列一张波浪图片使其衔接成一张能水平方向能覆盖整个控件的波浪图片:
这里写图片描述
(右键查看图像可看到)该图是一个200*300的颜色,上半部分透明色,下半部分灰白色。用上图在水平方向上衔接成一个能覆盖整个控件的波浪图片。

2.给当前TextView的Paint设置该BitmapShader,此时用这个Paint绘制出来的文字就是基于这个BitmapShader绘制的。举个形象一点的例子比喻BitmapShader在这里的作用,桌子上有一幅画(BitmapShader),然后用一层沙子将其完全盖住了(这个沙子就是控件TextView),然后绘制文字的时候相当于你在上面用手在沙子上写一个”Hello Word”,然后这幅画就在你写Hello World的一横一竖的地方部分显示出来了,相当于擦出了部分沙子,显示出了部分图像。

3.然后就是通过重复的属性动画在x方向和y方向去平移BitmapShader达到波浪的效果,就好像桌子上的图像不断的移动一样,当然超出控件的部分就不显示。x方向上做平移运动,y方向上反复的做上下运动。注意:X方向上的图片是通过连接衔接上去的,所以x方向上一次动画的具体必须是图像宽度的整数倍,以保证动画平滑。因为动画一次执行完成之后又是回到圆点重新执行。

我们按照上面步骤分析源码:
第一步:自定义TitanicTextView继承TextView新建createShader()方法

    private void createShader() {

        if (wave == null) {
            wave = getResources().getDrawable(R.drawable.wave);
        }

        int waveW = wave.getIntrinsicWidth();
        int waveH = wave.getIntrinsicHeight();

        final Bitmap b = Bitmap.createBitmap(waveW, waveH, Bitmap.Config.ARGB_8888);

        Canvas c = new Canvas(b);
        c.drawColor(getCurrentTextColor());
        wave.setBounds(0, 0, waveW, waveH);
        wave.draw(c);

        shader = new BitmapShader(b, Shader.TileMode.REPEAT,
                Shader.TileMode.CLAMP);
        getPaint().setShader(shader);
        offsetY = (getHeight() - waveH) / 2;
    }

这个函数的作用就是如其名,创建一个BitmapShader。
中间有几行代码:

Canvas c = new Canvas(b);
c.drawColor(getCurrentTextColor());
wave.setBounds(0, 0, waveW, waveH);
wave.draw(c);

意思是将b填充成当前字体颜色,然后将wave图片覆盖到这个画布上,这个时候得到b就是一个上部分是字体颜色,下部分是wave原来的颜色

shader = new BitmapShader(b,Shader.TileMode.REPEAT,Shader.TileMode.CLAMP);

是通过一个wave.png图片,x方向上重复的衔接,y方向上重复边缘颜色

第二步:设置画笔

getPaint().setShader(shader);

给当前画笔设置BitmapShader,其实这两步也就是一步。

第三步:通过属性动画不断的invalidate()重绘。


        final Runnable animate = new Runnable() {
            @SuppressLint("NewApi")
            @Override
            public void run() {
                textView.setSinking(true);

                // horizontal animation. 200 = wave.png width 一定要是图片的宽度
                ObjectAnimator maskXAnimator = ObjectAnimator.ofFloat(textView,
                        "maskX", 0, 400*4);
                maskXAnimator.setRepeatCount(ValueAnimator.INFINITE);
                maskXAnimator.setDuration(1000*4);
                maskXAnimator.setStartDelay(0);

                int h = textView.getHeight();

                // vertical animation
                // maskY = 0 -> wave vertically centered
                // repeat mode REVERSE to go back and forth
                ObjectAnimator maskYAnimator = ObjectAnimator.ofFloat(textView,
                        "maskY", h / 2, -h / 2);
                maskYAnimator.setRepeatCount(ValueAnimator.INFINITE);
                maskYAnimator.setRepeatMode(ValueAnimator.REVERSE);
                maskYAnimator.setDuration(8000);
                maskYAnimator.setStartDelay(0);

                // now play both animations together
                animatorSet = new AnimatorSet();
                animatorSet.playTogether(maskXAnimator, maskYAnimator);
                animatorSet.setInterpolator(new LinearInterpolator());
                animatorSet.addListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animation) {
                    }

                    @Override
                    public void onAnimationEnd(Animator animation) {
                        textView.setSinking(false);

                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                            textView.postInvalidate();
                        } else {
                            textView.postInvalidateOnAnimation();
                        }
                        animatorSet = null;
                    }

                    @Override
                    public void onAnimationCancel(Animator animation) {

                    }

                    @Override
                    public void onAnimationRepeat(Animator animation) {

                    }
                });
                animatorSet.start();
            }
        };
        // 如果没有布局好
        if (!textView.isSetUp()) {
            textView.setAnimationSetupCallback(new TitanicTextView.AnimationSetupCallback() {
                @Override
                public void onSetupAnimation(final TitanicTextView target) {
                    animate.run();
                }
            });
        } else {
            animate.run();
        }

这里是用过自定义maskX,maskY实现齐get和set方法,通过属性动画不断的调用maskX和maskY的set方法,然后在set方法里面调用invalidate进行重绘。maskX, maskY最终会换算成x方向和y方向的平移运动。

第四步:在onDraw方法里面实现重绘

        // modify text paint shader according to sinking state
        if (sinking && shader != null) {

            // first call after sinking, assign it to our paint
            if (getPaint().getShader() == null) {
                getPaint().setShader(shader);

            }
            // translate shader accordingly to maskX maskY positions
            // maskY is affected by the offset to vertically center the wave
            shaderMatrix.setTranslate(maskX, maskY + offsetY);

            // assign matrix to invalidate the shader
            shader.setLocalMatrix(shaderMatrix);
        } else {
            getPaint().setShader(null);
        }

重要的代码就两行:

shaderMatrix.setTranslate(maskX, maskY + offsetY);
// assign matrix to invalidate the shader
shader.setLocalMatrix(shaderMatrix);

乘以一个平移矩阵实现平移.下面给一张带有生成BitmapShader的图。
这里写图片描述
BitmapShader是基于左上角那副图片生成的,上部分是跟字体一样的颜色,下面部分是白色,整个过程其实都是在平移左上角这张图片,而且只有文字覆盖的地方的图片内容才会显示出来,这也是BitmapShader的作用,以实现波浪效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值