Android自定义View实现纵向跑马灯效果详解

所属分类: 软件编程 / Android 阅读数: 62
收藏 0 赞 0 分享

首先看看效果图(录制的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样式和主题之选择器的实例讲解

今天小编就为大家分享一篇关于Android样式和主题之选择器的实例讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Android应用动态修改主题的方法示例

今天小编就为大家分享一篇关于Android应用动态修改主题的方法示例,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Flutter 网络请求框架封装详解

这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Flutter倒计时/计时器的实现代码

这篇文章主要介绍了Flutter倒计时/计时器的实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
收藏 0 赞 0 分享

Android使用google breakpad捕获分析native cash

这篇文章主要介绍了Android使用google breakpad捕获分析native cash 的相关知识,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Flutter WebView与JS互相调用简易指南

这篇文章主要介绍了详解Flutter WebView与JS互相调用简易指南,分为JS调用Flutter和Flutter调用JS,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android开发实现ListView和adapter配合显示图片和文字列表功能示例

这篇文章主要介绍了Android开发实现ListView和adapter配合显示图片和文字列表功能,涉及Android使用ListView结合adapter适配器实现图文显示功能相关的布局、解析、权限控制等操作技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

Android文字基线Baseline算法的使用讲解

今天小编就为大家分享一篇关于Android文字基线Baseline算法的使用讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
收藏 0 赞 0 分享

Flutter自定义实现神奇动效的卡片切换视图的示例代码

这篇文章主要介绍了Flutter自定义实现神奇动效的卡片切换视图的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

Android实现自定义滑动刻度尺方法示例

这篇文章主要给大家介绍了关于Android实现自定义滑动刻度尺的相关资料,文中通过示例代码介绍的非常详细,对各位Android开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
收藏 0 赞 0 分享
查看更多