实现思路
通过上面的gif图可以得出结论,其实它就是同时绘制两条文本信息,然后通过动画不断的改变两条文本信息距离顶部的高度,以此来实现滚动的效果。
具体实现
首先定义一些要用到的属性
<declare-styleable name="MarqueeViewStyle"><attr name="textSize" format="dimension" /><attr name="textColor" format="color" /><attr name="paddingLeft" format="dimension" /><attr name="paddingTop" format="dimension" /><attr name="paddingBottom" format="dimension" /><attr name="paddingTopBottom" format="dimension"/><attr name="startDelayTime" format="integer"/> 动画开始延迟时间<attr name="reRepeatDelayTime" format="integer"/> 动画重复延迟时间 <attr name="itemAnimationTime" format="integer"/> 单个动画的执行时间</declare-styleable>接下来解析属性值
private void init(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MarqueeViewStyle); mTextColor = typedArray.getColor(R.styleable.MarqueeViewStyle_textColor, Color.BLACK); mTextSize = typedArray.getDimensionPixelSize(R.styleable.MarqueeViewStyle_textSize, 45); mPaddingLeft = typedArray.getDimensionPixelSize(R.styleable.MarqueeViewStyle_paddingLeft, 15); mPaddingTop = mPaddingBottom = typedArray.getDimensionPixelSize(R.styleable.MarqueeViewStyle_paddingTopBottom, 25); mPaddingTop = typedArray.getDimensionPixelSize(R.styleable.MarqueeViewStyle_paddingTop, mPaddingTop); mPaddingBottom = typedArray.getDimensionPixelSize(R.styleable.MarqueeViewStyle_paddingBottom, mPaddingBottom); itemAnimationTime = typedArray.getInteger(R.styleable.MarqueeViewStyle_itemAnimationTime, 1000); reRepeatDelayTime = typedArray.getInteger(R.styleable.MarqueeViewStyle_reRepeatDelayTime, 1000); startDelayTime = typedArray.getInteger(R.styleable.MarqueeViewStyle_startDelayTime, 500); typedArray.recycle();mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setTextSize(mTextSize);mPaint.setColor(mTextColor);mPaint.setTextAlign(Paint.Align.LEFT);}重写onMeasure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if(mTextArray == null || mTextArray.length == 0) { super.onMeasure(widthMeasureSpec, heightMeasureSpec);} else {int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec); ViewGroup.LayoutParams lp = getLayoutParams();setMeasuredDimension(getViewWidth(lp, width), getViewHeight(lp, height));}}数据为空时调用父类的方法,设置数据以后根据不同的布局计算宽高。
public void setTextArray(String[] textArray) {if(textArray == null || textArray.length <= 1) return;mTextArray = textArray;initTextRect();setTextCurrentOrNextStatus(0, 1, true);startAnimation();}
initTextRect()
方法是计算出单个文本的高度和数组中最大文本的宽度,文本的高度用来计算绘制文本时的位置,文本的最大宽度在onMeasure()
方法的时候会用到。setTextCurrentOrNextStatus()
方法设置当前的position,文本以及下一个position,文本。还有文本距离顶部的初始化高度。startAnimation()
方法 开始执行动画。private void startAnimation() { va = ValueAnimator.ofFloat(0, 1); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mProgress = (float) animation.getAnimatedValue();. int moveOffset = (int) (mTextMoveOffset * mProgress);mCurrentTextMoveMarginTop = mCurrentTextInitMarginTop - moveOffset; mNextTextMoveMarginTop = mNextTextInitMarginTop - moveOffset; postInvalidate(); } }); va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationRepeat(Animator animation) { va.pause();setTextCurrentOrNextStatus(mNextTextPosition, mNextTextPosition + 1, false);handler.postDelayed(new Runnable() { @Override public void run() {va.resume(); } }, reRepeatDelayTime); } }); va.setRepeatCount(-1); va.setDuration(itemAnimationTime); va.setStartDelay(startDelayTime); va.start();}
va.setRepeatCount(-1);
设置动画无限重复onAnimationUpdate()
方法得到动画执行的进度,计算出text距离顶部的距离,调用postInvalidate()
方法刷新界面。onAnimationRepeat()
方法,通过handler延迟reRepeatDelayTime时间,再重新执行动画。protected void onDraw(Canvas canvas) { if(mTextArray == null || mTextArray.length == 0) { super.onDraw(canvas); } else {canvas.drawText(mCurrentText, mPaddingLeft, mCurrentTextMoveMarginTop, mPaint);canvas.drawText(mNextText, mPaddingLeft, mNextTextMoveMarginTop, mPaint); }}总结