应产品的需求,需要实现跑马灯的效果,从网上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