自定义View跑马灯连续滚动,不限长度焦点,首尾有间隔

应产品的需求,需要实现跑马灯的效果,从网上Google、Github上浏览的一个遍,没有找到想要的效果;好不容易有个效果跟我要的一样,却发现了bug,悲催!自己写!nnd

先来推荐三个 GitHub 上实现跑马灯效果很不错的库!(却不是我想要的,不过还是可以借鉴一下):

  • sunfusheng/MarqueeView 这是扩展了 ViewFlipper实现的。实现的效果如下:
  • gongwen/MarqueeViewLibrary 这也是扩展了ViewFlipper控件实现的,实现的效果如下:
  • 385841539/MarqueeView 这是继承了View 去实现,作者说了实现该库的原因用TextView 出现了各种坑啊 , 尤其是在页面中同时存在EditText 的时候,效果图如下:(第三种方式是有bug的,会导致内存一直上升,本来这是我想要的效果,but。。。)

下面贴上我自己实现的跑马灯效果:

我是这样实现的,以下为关键代码:

第一步:先初始化两个Paint,用来画两个文本:

    private void init() {
        mPaint = new Paint();
        mPaint.setTextSize(getContext().getResources().getDimension(R.dimen.sp_15));
        mPaint.setColor(Color.RED);
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setAntiAlias(true);

        mPaint2 = new Paint();
        mPaint2.setTextSize(getContext().getResources().getDimension(R.dimen.sp_15));
        mPaint2.setColor(Color.RED);
        mPaint2.setTextAlign(Paint.Align.LEFT);
        mPaint2.setAntiAlias(true);

    }

第二步:测量文本的baseLine

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mContent == null) {
            super.onMeasure(widthMeasureSpec,heightMeasureSpec);
            return;
        }


        Paint.FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
        int height = fontMetrics.bottom - fontMetrics.top + getPaddingBottom() + getPaddingTop();
        setMeasuredDimension(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));

        int top = getPaddingTop();
        int bottom = top + fontMetrics.bottom - fontMetrics.top;
        int left = getPaddingLeft() + mOffset;
        int right = left + mTextWidth;
        if (mTargetRect == null) {
            mTargetRect = new Rect();
        }
        if (mTargetRect2 == null) {
            mTargetRect2 = new Rect();
        }
        mTargetRect.set(left, top, right, bottom);
        mTargetRect2.set(left, top, right, bottom);
        mBaseLine = (mTargetRect.bottom + mTargetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        mBaseLine2 = (mTargetRect2.bottom + mTargetRect2.top - fontMetrics.bottom - fontMetrics.top) / 2;
        mAnimator.cancel();
        mAnimator2.cancel();
//        if (mTextWidth > getMeasuredWidth()) {
            mAnimator.start();
            mAnimator2.start();
//        }

    }

第三步:重写onDraw方法,将文本画出来

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mContent == null || mTargetRect == null || mTargetRect2 == null) {
            return;
        }

        mTargetRect.left = getPaddingLeft() + mOffset;
        mTargetRect.right = mTargetRect.left + mTextWidth;
        canvas.drawText(mContent, mTargetRect.left, mBaseLine, mPaint);

        if (mTextWidth < viewWidth) {
            mTargetRect2.right = viewWidth + mOffset2;
            mTargetRect2.left = mTargetRect2.right - mTextWidth - getPaddingRight();
        } else {
            mTargetRect2.right = mTextWidth + viewWidth/3 + mOffset2;
            mTargetRect2.left = mTargetRect2.right - mTextWidth - getPaddingRight();
        }
        canvas.drawText(mContent, mTargetRect2.right, mBaseLine2, mPaint2);
    }

第四步:提供外部setText方法,并同时执行两个动画

public void setText(String text) {
        mContent = text;
        mTextWidth = (int) (mPaint.measureText(mContent, 0, mContent.length())+1);
        if(null == mAnimator){
            mAnimator = ValueAnimator.ofFloat(0, mTextWidth);
            mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    mOffset -= 2;
                    if (mTextWidth < getWidth()) {
                        if (mOffset < -getWidth()) {
                            mOffset = getWidth();//定位到最右边
                        }
                    } else {
                        if (mOffset < -(getWidth()/3 + mTextWidth)) {
                            mOffset = mTextWidth + getWidth()/3;//保证与第二个动画同步
                        }
                    }
                    invalidate();
                }
            });
            mAnimator.setRepeatCount(ValueAnimator.INFINITE);
            mAnimator.setRepeatMode(ValueAnimator.REVERSE);
            //5.为ValueAnimator设置目标对象并开始执行动画
            mAnimator.setTarget(this);
            mAnimator.setDuration((long) (mTextWidth));

        }
        if(null == mAnimator2){
            mAnimator2 = ValueAnimator.ofFloat(getWidth(), 0);
            mAnimator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    mOffset2 -= 2;
                    if (mTextWidth < getWidth()) {
                        Log.e("MarqueeTextView", "mOffset = " + mOffset2 + ", getWidth() = " + getWidth() + ", mTextWidth = " + mTextWidth + ", viewWidth = " + viewWidth);
                        if (mOffset2 < -2*getWidth()) {
                            mOffset2 = 0;//定位到最右边
                        }
                    } else {
//                        Log.e("MarqueeTextView", "mOffset = " + mOffset2 + ", getWidth() = " + getWidth() + ", mTextWidth = " + mTextWidth);
                        if (mOffset2 < -2*(getWidth()/3 + mTextWidth)) {
                            mOffset2 = 0;
                        }
                    }
                    invalidate();
                }
            });
            mAnimator2.setRepeatCount(ValueAnimator.INFINITE);
            mAnimator2.setRepeatMode(ValueAnimator.REVERSE);
            //5.为ValueAnimator设置目标对象并开始执行动画
            mAnimator2.setTarget(this);
            mAnimator2.setDuration((long) (mTextWidth));

        }

    }

至此,绘制就完成了,接下来调用,测试一下:

 mV4.setText("让我掉下眼泪的,不止昨夜的酒");
        mV4.start();

项目下载地址:https://download.csdn.net/download/shenhe123/11238068

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值