欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

来一篇跑马灯TextView----拖更太久了

程序员文章站 2022-06-19 14:08:48
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