自定义View实现文字跑马灯效果(垂直滚动和水平滚动)
2017-10-22 20:57
2396 查看
话不多说,直接上效果图
由于录屏的原因,画面有点卡顿,实际效果很流畅,滚动速度和方向等属性已经封装好,可以直接设置。 因为最近一个项目中要反复使用到跑马灯效果,所以直接把这个自定义View封装好,方便项目中直接调用。
在这里顺便讲一下怎样去封装自定义控件
1、首先新建一个模块吧!
2、为了使用到新建模块里面的内容,在自己的app模块点鼠标右键选择Open Module Settings把新建的模块添加到app模块里面。
3、选择 Module dependency]
4、 选中你自己新建的模块,点击ok,进行添加。当你在Project Structure里面的Modules下看到了你自己新建的模块,那么模块就添加成功了。
5、接着开始写代码,新建一个类ScrollingText继承View
首先直接上所有代码,然后慢慢分析吧!
public class ScrollingText extends View { //开始绘制文本的X轴坐标 private int startDrawX=0; //画笔 private Paint drawPaint; < 4000 span class="hljs-comment">//文本的宽度和高度 private int TextWidth,TextHeight; //文本长度是否超过文本框的宽度 private boolean isOutSide; //要绘制的文本 private String drawText; //字体的颜色 private int textColor; //字体的大小 private float textSize; //滚动的速度 private int scrollingSpeed; //长文本时,文本显示之间的间距 private String orientation; //两个长文本之间的空白距离 private int spacing; //判断是否第首次启动界面 private boolean isJudge=true; //Y轴初始化 private int baseY=0; //存储中间变量 private int temp=0; /** * java文件构造函数 * @param context */ public ScrollingText(Context context) { super(context); initView(); } /** * 初始化View */ private void initView() { Paint p=new Paint(); p.setColor(textColor); p.setTextSize(sp2px(textSize)); drawPaint=p; Rect rect=new Rect(); //把一个文本包裹起来的矩形框,来获取文本的宽高 drawPaint.getTextBounds(drawText,0,drawText.length(),rect); TextWidth=rect.width(); TextHeight=rect.height(); spacing=100;//长文本时中间两段文字中间空白的距离 } /** * XML文件构造函数 * @param context * @param attrs */ public ScrollingText(Context context, AttributeSet attrs) { super(context, attrs); //从XML文件中读取属性 TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.ScrollingText); textColor=a.getColor(R.styleable.ScrollingText_textColor, Color.BLACK); textSize=a.getDimension(R.styleable.ScrollingText_textSize,25); scrollingSpeed=a.getInteger(R.styleable.ScrollingText_scrollingSpeed,5); drawText=a.getString(R.styleable.ScrollingText_text); orientation=a.getString(R.styleable.ScrollingText_orientation); initView(); } /** * 测量View大小 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //测量宽度 int width=getMySize(TextWidth,widthMeasureSpec); //测量高度 int height=getMySize(TextHeight,heightMeasureSpec); //保存测量宽度和测量高度 setMeasuredDimension(width,height); //判断文本宽度是否超过布局宽度 if(TextWidth>=getMeasuredWidth()){ isOutSide =true; }else { isOutSide =false; } //设置组件初始化的布局方式是水平布局 if (orientation==null || "".equals(orientation)){ orientation="horizontal"; } } /** * 绘制组件 * @param canvas */ @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); if(isJudge){ //绘制文本居中的位置,并把这个值保存下来 temp = (int) ((canvas.getHeight() / 2) - ((drawPaint.descent() + drawPaint.ascent()) / 2)); baseY=temp; } //文字水平方向滚动,从右往左 if ("horizontal".equals(orientation)){ //两种情况,一种文字超过屏幕宽度,另一种文字没有超过宽度,针对两种情况做处理 if(isOutSide){ //当绘制的文本长度超过了文字长度时 if(startDrawX<-TextWidth){ startDrawX=spacing; } //文本绘制超出的区域,也就是在左边移动时超出的区域 int outSide=startDrawX; //文本的宽度减去屏幕的宽度,就是文本超出屏幕的长度,当文本绘制超出的区域大于文本超出的宽度时,开始绘制第二条文字, //这样才能起到无限滚动的效果(由于文字是从右往左,所以outSide是负数) if(outSide<-(TextWidth-getMeasuredWidth())){ canvas.drawText(drawText,TextWidth+outSide+spacing,baseY,drawPaint); } //绘制第一条文本 canvas.drawText(drawText,startDrawX,baseY,drawPaint); //设置onDraw()刷新的速度 postInvalidateDelayed(scrollingSpeed); startDrawX-=5; }else { //文本未出文本框的宽度 //判断文本是否完全超出区域 if (startDrawX < -TextWidth) { startDrawX = getMeasuredWidth() - TextWidth; } int outSide = startDrawX; //超出区域往右边绘制超出部分 if (outSide < 0) { canvas.drawText(drawText, getMeasuredWidth() + outSide, baseY, drawPaint); } canvas.drawText(drawText, startDrawX, baseY, drawPaint); //scrollingSpeed值越大界面更新越慢 postInvalidateDelayed(scrollingSpeed); //横向滚动时每次X轴-1 startDrawX -= 1; } } //垂直反向滚动 else if("vertical".equals(orientation)){ //计算出文字开始绘制的位置,文字水平居中 int baseX = (int) (canvas.getWidth() / 2 -TextWidth/2); //在中间这个范围内的时候休眠3秒再开始绘制,这样垂直滚动才会在中间停一下 if (baseY>=temp-10 && baseY<=temp+10){ canvas.drawText(drawText,baseX,temp,drawPaint); new Thread(){ @Override public void run() { try { Thread.sleep(3000); handler.sendEmptyMessage(0x111); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }else { //直接绘制 canvas.drawText(drawText,baseX,baseY,drawPaint); postInvalidateDelayed(scrollingSpeed); } //每次Y轴加10 baseY += 10; //当Y轴完全不可见时,设置baseY=0,从头开始绘制 if(baseY>getMeasuredHeight()+TextHeight){ baseY=0; } isJudge=false; } } private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what == 0x111){ postInvalidateDelayed(scrollingSpeed); } } }; //测量 private int getMySize(int defaultSize, int measureSpec) { int mySize = defaultSize; int mode = MeasureSpec.getMode(measureSpec); int size = MeasureSpec.getSize(measureSpec); switch (mode) { case MeasureSpec.UNSPECIFIED: {//如果没有指定大小,就设置为默认大小 mySize = defaultSize; break; } case MeasureSpec.AT_MOST: {//如果测量模式是最大取值为size //WRAP_CONTENT //我们将大小取最小值,也可以取其他值 mySize = Math.min(size, defaultSize); break; } case MeasureSpec.EXACTLY: { //如果是固定的大小,那就不要去改变它,MatchParent或者明确的数值 mySize = size; break; } } return mySize; } //sp转px private int sp2px(float spValue) { float fontScale = getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } }
里面注释还算详细,但理解起来可能有点困难,不着急,先用吧!
/** * XML文件构造函数 * @param context * @param attrs */ public ScrollingText(Context context, AttributeSet attrs) { super(context, attrs); //从XML文件中读取属性 TypedArray a=context.obtainStyledAttributes(attrs,R.styleable.ScrollingText); textColor=a.getColor(R.styleable.ScrollingText_textColor, Color.BLACK); textSize=a.getDimension(R.styleable.ScrollingText_textSize,25); scrollingSpeed=a.getInteger(R.styleable.ScrollingText_scrollingSpeed,5); drawText=a.getString(R.styleable.ScrollingText_text); orientation=a.getString(R.styleable.ScrollingText_orientation); initView(); }
这是XML文件中会使用的方法,当你把这个自定义View放到XML文件中时,就会调用这个构造方法,因为是自定义View,要把它封装好,所以也要封装好它的几个属性,方便以后直接在XML文件中设置它的属性。在这个方法中我们要获取它的属性,然后对这些值做处理。
当然这些值是要先定义的,不然报错,显示找不到。
在新建模块的string资源下定义属性
先定义这几个属性
<declare-styleable name="ScrollingText"> <attr name="textColor" format="color"/> <attr name="textSize" format="dimension"/> <attr name="scrollingSpeed" format="integer"/> <attr name="text" format="string"/> <attr name="orientation" format="string"/> </declare-styleable>
name名字,format类型。
接着我们就可以在app模块下使用这个自定义View
在你的项目资源文件下直接使用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.zhengpeng.myviewdemotext.ScrollinnTextActivity"> <com.zhengpeng.uikit.ScrollinnText.ScrollingText android:layout_width="match_parent" android:layout_height="180dp" android:background="#00eeee" app:text="@string/text" app:orientation="horizontal" app:textColor="@color/colorAccent" app:scrollingSpeed="10" /> </RelativeLayout>
其实主要的核心代码在onDraw( )方法里面
先看这个方法里面水平滚动的代码
//文字水平方向滚动,从右往左 if ("horizontal".equals(orientation)){ //两种情况,一种文字超过屏幕宽度,另一种文字没有超过宽度,针对两种情况做处理 //这是第一种情况,文字超过屏幕宽度 if(isOutSide){ //当绘制的文本长度超过了文字长度时 if(startDrawX<-TextWidth){ startDrawX=spacing; } //文本绘制超出的区域,也就是在左边移动时超出的区域 int outSide=startDrawX; //文本的宽度减去屏幕的宽度,就是文本超出屏幕的长度,当文本绘制超出的区域大于文本超出的宽度时,开始绘制第二条文字, //这样才能起到无限滚动的效果(由于文字是从右往左,所以outSide是负数) if(outSide<-(TextWidth-getMeasuredWidth())){ canvas.drawText(drawText,TextWidth+outSide+spacing,baseY,drawPaint); } //绘制第一条文本 canvas.drawText(drawText,startDrawX,baseY,drawPaint); //设置onDraw()刷新的速度 postInvalidateDelayed(scrollingSpeed); startDrawX-=5; }else { //文本未出文本框的宽度 //判断文本是否完全超出区域 if (startDrawX < -TextWidth) { startDrawX = getMeasuredWidth() - TextWidth; } int outSide = startDrawX; //超出区域往右边绘制超出部分 if (outSide < 0) { canvas.drawText(drawText, getMeasuredWidth() + outSide, baseY, drawPaint); } canvas.drawText(drawText, startDrawX, baseY, drawPaint); //scrollingSpeed值越大界面更新越慢 postInvalidateDelayed(scrollingSpeed); //横向滚动时每次X轴-1 startDrawX -= 1; } }
水平滚动有两种情况
第一种文本宽度大于屏幕宽度,startDrawX是递减的,这样才能往左边移动,把每次startDrawX的值,赋值给outSide,当判断到outSide大于文本宽度减去屏幕宽度的值之后,也就是文本的最后端已经显示出来了,这是开始绘制第二条文本,绘制的起始点是TextWidth+outSide+spacing这个位置,TextWidth文本宽度时大于屏幕的,outSide是超出的位置,是负值,spacing是与前文本之间的空格。
//当绘制的文本长度超过了文字长度时
if(startDrawX<-TextWidth){
startDrawX=spacing;
}
这时候第一条文字初始化位置是100,spacing值等于100,是设置的空白距离,第二条不再绘制。
第二种是文字长度小于屏幕宽度,首先绘制第一条文字,从startDrawX =0的位置开始绘制,因为我们要实现的效果是从右往左滚动,所以startDrawX 要 -=1,每刷新一次往左边移动一点。往左边移出来的这一些,应该在右边显示出来,这时我们就在右边绘制第二条文本,当第一条文本移动到startDrawX >=文本的宽度,就完全出了屏幕,这时把第一条文本绘制到第二条文本的位置,这是outSide >0所以第二条文本没有绘制,这样启到了一个循环的滚动的效果,理解起来可能有点困难。
看个图吧
最好自己动手去试下,改下参数啥的!运行下,这样有助于理解!
后面还有个垂直滚动的,代码里面有注释,这里不再做说明了,相信理解了水平滚动原理,垂直滚动也不在话下!
对于自定义View不怎么了解的可以看下这个系列的博客
Android自定义View(一、初体验自定义TextView)
这个系列比较的详细,个人觉得很不错,强烈推荐!
相关文章推荐
- Android TextView实现垂直滚动的跑马灯效果
- (原创)自定义view(view的绘制过程)、无限轮播并触碰停止轮播的viewpage、水平和垂直滚动的TextView、仿QQ滑动删除、下拉刷新上拉加载view、毛玻璃效果、低版本水波纹、圆环头像图
- TextView实现文字水平手动滚动和垂直手动滚动
- Android中TextView实现垂直滚动(轮换效果,非跑马灯)
- TextView实现跑马灯效果(文字滚动)
- 一分钟实现 Android textview 跑马灯文字滚动效果
- Android开发之TextView文字水平滚动效果实现
- Android 自定义View实现文本水平方向的跑马灯效果
- IOS跑马灯效果,实现文字水平无间断滚动
- Android中TextView实现文字跑马灯效果(滚动轮播)
- jquery实现marquee效果(文字或者图片的水平垂直滚动)
- Android自定义textview实现竖直滚动跑马灯效果
- jquery实现marquee效果(文字或者图片的水平垂直滚动)
- 使用 scroll-view 标签,实现文字水平方向(从左至右)跑马灯效果
- 自定义View—实现滚动TextView(跑马灯)效果
- Android实现文字垂直滚动、纵向走马灯效果的实现方式汇总
- 自定义View实现水平滚动控件
- 在Toast中实现文字滚动功能(也就是跑马灯效果)
- Android使用Recyclerview实现图片水平自动循环滚动效果
- android TextView 实现自定义文字点击效果