来一篇跑马灯TextView----拖更太久了
程序员文章站
2022-03-06 21:37:22
TextView增加跑马灯效果,相信很多朋友都用过了,但是如果使用android自带的跑马灯效果,还是有很多时候无法满足项目需求的。比如系统自带的跑马灯效果需要获得焦点才能启动,如果当前页面中存在多个TextView都需要跑马灯效果,那就懵逼了,根本没法一起跑。所以我决定自己基于TextView写一个。。。废话不多说上代码/** * 设计思路就是通过UI线程轮询更新绘制Text的坐标,从而实现滚动 */public class MarqueeTextViewN extends Tex...
TextView增加跑马灯效果,相信很多朋友都用过了,但是如果使用android自带的跑马灯效果,还是有很多时候无法满足项目需求的。
比如系统自带的跑马灯效果需要获得焦点才能启动,如果当前页面中存在多个TextView都需要跑马灯效果,那就懵逼了,根本没法一起跑。
所以我决定自己基于TextView写一个。。。
废话不多说上代码
/**
* 设计思路就是通过UI线程轮询更新绘制Text的坐标,从而实现滚动
*/
public class MarqueeTextViewN extends TextView {
private Paint mPaint, Ppaint;
private Rect rect = new Rect();//本文本域的一个矩形对象
//以下是设置文本的参照值
private int mSize;//字体大小
private CharSequence mText;//要显示的文本
private int mColor;//文本颜色
private int mPColor;//padding边距的颜色,默认是白色
private int mPAlpha;//padding边距的透明度,默认不透明
private int mWidth,mHeight;//整个文本框的长宽
private float mTextX = 0;//被绘制文本的X轴起始位
private float mSpeed;//文字滚动的速度,默认为1,数值越大滚动越快
private int DEFAULT_COLOR_WHITE = ContextCompat.getColor(getContext(),R.color.white);
private Runnable runnable;
public MarqueeTextViewN(Context context) {
super(context);
init();
}
public MarqueeTextViewN(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MarqueeTextViewN(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mSize = (int) getTextSize();
mText = getText();
//如果这里没有在xml中配置text属性,那么这里就使用默认属性
mColor = getCurrentTextColor();
mPColor = DEFAULT_COLOR_WHITE;
mPAlpha = 0;
mSpeed = 3f;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(mColor);
mPaint.setTextSize(mSize);
mPaint.getTextBounds(mText.toString(),0, mText.length(),rect);//这里是第一次刷新rect的数据
runnable = () -> {
moveText();
postDelayed(runnable, 3L);
};
postDelayed(runnable, 3L);
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
onDrawText(canvas);
}
private void onDrawText(Canvas canvas){
mPaint.setColor(mColor);
mPaint.setTextSize(mSize);
mPaint.getTextBounds(mText.toString(),0, mText.length(),rect);
//这里是在每一次绘制之前刷新。。。(防止再跑马灯效果中,字符串发生改变,造成跑灯效果错乱
//楼下部分则开始绘制
canvas.drawText(mText.toString(),mTextX + getPaddingLeft(), Math.abs(rect.top) + getPaddingTop(),mPaint);
//本句的第三个参数尚不明确,填这个参数就能使字符串居中
//以下为了解决padding参数造成的边框效果
Ppaint = new Paint();
Ppaint.setColor(mPColor);
Ppaint.setAlpha(mPAlpha);
canvas.drawRect(0,0,mWidth,getPaddingTop(), Ppaint);
canvas.drawRect(0,getPaddingTop(),getPaddingLeft(),mHeight-getPaddingBottom(), Ppaint);
canvas.drawRect(mWidth-getPaddingRight(),getPaddingTop(),mWidth,mHeight-getPaddingBottom(), Ppaint);
canvas.drawRect(0,mHeight-getPaddingBottom(),mWidth,mHeight, Ppaint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 这里要计算一下控件的实际大小,然后调用setMeasuredDimension来设置
mWidth = this.getMeasuredSize(widthMeasureSpec, true);
mHeight = this.getMeasuredSize(rect.height(), false);//这里把文本域的高度设置为本View的最终高度
setMeasuredDimension(mWidth,mHeight);
}
/**
* 每被线程执行一次,对应的更新数据
*/
private synchronized void moveText(){
if (mTextX > -rect.width()){
mTextX-=mSpeed;//每一次移动一个单位距离
}else {
mTextX = mWidth-1-getPaddingRight();//减掉getPaddingRight是为了适配padding的边距属性
}
requestLayout();
invalidate();
}
/**
* 计算控件的实际大小
* @param length onMeasure方法的参数,widthMeasureSpec或者heightMeasureSpec
* @param isWidth 是宽度还是高度
* @return int 计算后的实际大小
*/
private int getMeasuredSize(int length, boolean isWidth){
// 模式
int specMode = MeasureSpec.getMode(length);
// 尺寸
int specSize = MeasureSpec.getSize(length);
// 计算所得的实际尺寸,要被返回
int retSize = 0;
// 得到两侧的padding(留边)
int padding = (isWidth? getPaddingLeft()+getPaddingRight():getPaddingTop()+getPaddingBottom());
// 对不同的指定模式进行判断
if(specMode==MeasureSpec.EXACTLY){ // 显式指定大小,如40dp或fill_parent
retSize = specSize;
}else{ // 如使用wrap_content
retSize = (isWidth? rect.width()+padding : rect.height()+padding);
if(specMode==MeasureSpec.AT_MOST){
retSize = Math.min(retSize, specSize);
}
}
return retSize;
}
}
本文地址:https://blog.csdn.net/w366549434/article/details/107985977