美文网首页Android架构
Android自定义控件:文字闪光移动效果

Android自定义控件:文字闪光移动效果

作者: houtengzhi | 来源:发表于2018-09-26 16:04 被阅读575次

    如图的文字闪光效果,下面分别用两种方式来实现。


    shimmer_text.gif

    实现方式 一

    由于要实现文字的闪光移动,自定义控件直接继承TextView。在文字上面绘制一个矩形框,矩形框和文件相交处显示矩形框的颜色,不断移动矩形框的位置,从而实现闪光不断移动的效果。

    public class BlinkTextView extends TextView {
    ...
    }
    

    通常字符串并不会完全填充View,因此需要计算字符串实际所占区域位置,在onMeasure方法中计算绘制内容的实际区域。需要的绘制的前景闪光效果矩形框初始位置在字符串区域的左边,然后移动两倍的字符串宽度到右边。

    @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            // 基线位置
            int baseLine = getBaseline();
            // 绘制内容所占的区域
            mBlinkPaint.getTextBounds(getText().toString(), 0, getText().length(), textRect);
            // 传入基线位置,计算实际的位置区域
            textRectf.set(textRect.left, textRect.top + baseLine, textRect.right, textRect.bottom + baseLine);
            L.d(TAG, "text region: " + textRectf.toString());
            
            // 前景闪光效果的矩形框
            rectF.set(textRectf.left - mMoveWidth, textRectf.top,
                    textRectf.left, textRectf.bottom);
            L.d(TAG, "draw region: " + rectF.toString());
        }
    

    onDraw方法中实现绘制逻辑。采用PorterDuffXfermode(图像过度模式)实现字符串内容和前景闪光合成效果。这里需要用到PorterDuff.Mode.SRC_IN模式,在两者相交的地方绘制源图像,并且绘制的效果会受到目标图像对应地方透明度的影响(可参考https://www.jianshu.com/p/d11892bbe055)。

    这里保持前景闪光矩形框的位置参数不变,通过设置canvas的位移矩阵从而实现矩形框的移动效果。

    @Override
        protected void onDraw(Canvas canvas) {
            // 离屏缓冲
            int saveCount = canvas.saveLayer(dstRect, mBlinkPaint, Canvas.ALL_SAVE_FLAG);
            // 调用父方法绘制字符串
            super.onDraw(canvas);
    
            mBlinkPaint.setAntiAlias(true);
            // 设置画笔的颜色混合模式
            mBlinkPaint.setXfermode(mXfermode);
    
            mBlinkPaint.setColor(getResources().getColor(R.color.green));
            mBlinkPaint.setStyle(Paint.Style.FILL);
            // 设置矩阵左右位移dx,dx随着时间变化
            matrix.reset();
            matrix.setTranslate(dx, 0);
    
    //        canvas.skew(0.5f, 0);
            // 画布设置位置矩阵
            canvas.setMatrix(matrix);
            // 绘制前景矩形框
            canvas.drawRect(rectF, mBlinkPaint);
            // 清除Xfermode
            mBlinkPaint.setXfermode(null);
            canvas.restoreToCount(saveCount);
        }
    

    使用属性动画ValueAnimator来实现动画效果,每一帧输出一个位移值dx,调用invalidate()重新绘制View,dx值从0逐渐增加到两倍的字符串宽度。

    public void start() {
            if (valueAnimator != null && valueAnimator.isStarted()) {
                return;
            }
            valueAnimator = ValueAnimator.ofFloat(0, 2 * (textRectf.right - textRectf.left));
            valueAnimator.setDuration(1000);
            valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    dx = (float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            valueAnimator.start();
        }
    

    onWindowFocusChanged方法中开启或者取消动画播放。需要注意的是View的生命周期,View只有在Activity执行完onResume方法之后才会调用onMeasure(),在onResume方法中主动调用start()开启动画,此时字符串宽度值为0,会导致动画效果不显示

    @Override
        public void onWindowFocusChanged(boolean hasWindowFocus) {
            super.onWindowFocusChanged(hasWindowFocus);
            L.d(TAG, "onWindowFocusChanged(), hasWindowFocus = %b", hasWindowFocus);
            if (hasWindowFocus) {
                start();
            } else {
                cancel();
            }
        }
    

    实现方式 二

    blink_shimmer_text_3s.gif

    将动画周期调大一些会发现,左边的动画当闪光移动比较慢时,方式一实现的的前景矩形框和背景字符串过度比较突兀,这里我们用LinearGradient颜色梯度来实现右边带有颜色渐变的动画效果。

    @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int baseLine = getBaseline();
            mPaint.getTextBounds(getText().toString(), 0, getText().length(), textRect);
    
            textRectf.set(textRect.left, textRect.top + baseLine, textRect.right, textRect.bottom + baseLine);
    
            textWidth = getMeasuredWidth();
            int textHeight = getMeasuredHeight();
    
            L.d(TAG, "onMeasure(), width = %d, height = %d", textWidth, textHeight);
            L.d(TAG, "onMeasure(), " + textRectf.toString());
            linearGradient = new LinearGradient(-(textRectf.right- 2 * textRectf.left), textRectf.top,
                    textRectf.left, textRectf.bottom,
                    new int[] {getCurrentTextColor(), 0xff00ff00, getCurrentTextColor()},
                    new float[] {0, 0.5f, 1f}, Shader.TileMode.CLAMP);
        }
    

    onMeasure方法中,计算好字符串的实际位置后,新建一个LinearGradient对象,颜色渐变方式是左、中、右的位置分别对应字体的颜色、闪光的颜色、字体的颜色。

    @Override
        protected void onDraw(Canvas canvas) {
            L.d(TAG, "onDraw(), dx = " + dx);
            matrix.reset();
            matrix.setTranslate(dx, 0);
            linearGradient.setLocalMatrix(matrix);
            mPaint.setShader(linearGradient);
            super.onDraw(canvas);
        }
    

    重写onDraw方法,设置linearGradient的位置矩阵matrixdx是矩阵的左右位移。然后设置mPaint的着色器,这里的mPaint即当前绘制的TextView的Paint对象,可通过调用getPaint()获得。最后调用父方法绘制字符串。

    public void start() {
            L.d(TAG, "start()");
            if (valueAnimator != null && valueAnimator.isStarted()) {
                return;
            }
            valueAnimator = ValueAnimator.ofFloat(0, 2 * (textRectf.right - textRectf.left));
            valueAnimator.setDuration(3000);
            valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    dx = (Float) animation.getAnimatedValue();
                    invalidate();
                }
            });
            valueAnimator.start();
        }
    

    LinearGradient的位移dx从0变化到两倍的字符串宽度距离。

    参考文章

    相关文章

      网友评论

        本文标题:Android自定义控件:文字闪光移动效果

        本文链接:https://www.haomeiwen.com/subject/opfdoftx.html