Android自定义View实现纵向跑马灯效果详解
首先看看效果图(录制的gif有点卡,真实的效果还是很流畅的)
实现思路
通过上面的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); } }
总结
到这里所有的代码已经分析完毕,其实实现这个效果还有很多种方法,通过继承ViewGroup等等都可以实现,大家有兴趣可以自己尝试。希望本文的内容对大家的学习或者工作能有所帮助,如果有疑问大家可以留言交流。
相关文章
浅谈Android AsyncTask内存安全的一种使用方式
这篇文章主要介绍了浅谈Android AsyncTask内存安全的一种使用方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2018-08-08Android TextWatcher内容监听死循环案例详解
这篇文章主要介绍了Android TextWatcher内容监听死循环案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下2021-08-08详解Android权限管理之Android 6.0运行时权限及解决办法
本篇文章主要介绍Android权限管理之Android 6.0运行时权限及解决办法,具有一定的参考价值,有兴趣的可以了解一下。2016-11-11Android下Activity间通信序列化过程中的深浅拷贝浅析
这篇文章主要给大家介绍了关于Android下Activity间通信序列化过程中深浅拷贝的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。2017-10-10Android应用中clearFocus方法调用无效的问题解决
clearFocus()主要用于清除EditText的焦点,Android App开发中很多时候会发现其调用无效,带着这个问题我们就来看一下本文主题、Android应用中clearFocus方法调用无效的问题解决2016-05-05
最新评论