美文网首页
TextView跑马灯

TextView跑马灯

作者: 超人TIGA | 来源:发表于2022-03-19 23:27 被阅读0次
如何实现?

<!--  条件1: android:ellipsize="marquee" -->
<!--  条件2: android:focusable="true" -->
<!--  条件3: android:marqueeRepeatLimit="marquee_forever" -->
<!--  条件4: android:singleLine="true" -->
    <TextView
        android:id="@+id/tvMarquee"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ellipsize="marquee"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever"
        android:scrollHorizontally="true"
        android:singleLine="true"
        android:text="跑马灯效果,必须要同时实现4个条件,跑马灯才会正常地跑起来"
        android:textSize="25sp" />

focusable的值一直为true,那如果同界面的其他控件也需要焦点,结果要么是跑马灯失效,要么其他控件获取不了焦点。

那咋整?

方法一:
①跑马灯的TextView,设置android:focusable="false"。
②代码中,tvMarquee.isSelected = true,设置selected为true
方法二:
自定义TextView控件


class FocusTextView : AppCompatTextView {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        if (focused) {
            super.onFocusChanged(focused, direction, previouslyFocusedRect)
        }
    }

    override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
        if (hasWindowFocus) {
            super.onWindowFocusChanged(hasWindowFocus)
        }
    }

    override fun isFocused(): Boolean {
        return true
    }
}

在布局文件使用的时候,去掉android:focusable="true"这个条件,就可以实现同样的效果了。

我们还能利用surfaceview,自己实现一个跑马灯的控件。
public class VerticalMarqueeView extends SurfaceView implements SurfaceHolder.Callback {
public Context mContext;

private float mTextSize = 100; //字体大小

private int mTextColor = Color.RED; //字体的颜色

private boolean mIsRepeat;//是否重复滚动

private int mStartPoint;// 开始滚动的位置  0是从上面开始   1是从下面开始

private int mDirection;//滚动方向 0 向上滚动   1向下滚动

private int mSpeed;//滚动速度

private SurfaceHolder holder;

private TextPaint mTextPaint;

private MarqueeViewThread mThread;

private String margueeString;

private int textWidth = 0, textHeight = 0;

public int currentY = 0;// 当前y的位置

public double sepY = 1;//每一步滚动的距离

private Point point;//点,没啥用,懒得弄了

private StaticLayout staticLayout;//绘制多行文本所需类

private boolean isFirstDraw = true;//是否为某条文本的第一次绘制~~

private boolean isStop = false;
private int sec = 5000;

public VerticalMarqueeView(Context context) {
    this(context, null);
}

public VerticalMarqueeView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public VerticalMarqueeView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.mContext = context;
    if (isInEditMode()) {
        //防止编辑预览界面报错
        return;
    }
    init(attrs, defStyleAttr);
}

private void init(AttributeSet attrs, int defStyleAttr) {

    TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.VerticalMarqueeTextView, defStyleAttr, 0);
    mTextColor = a.getColor(R.styleable.VerticalMarqueeTextView_VerticalMarqueeTextView_textColor, Color.RED);
    mTextSize = a.getDimension(R.styleable.VerticalMarqueeTextView_VerticalMarqueeTextView_textSize, 48);
    mIsRepeat = a.getBoolean(R.styleable.VerticalMarqueeTextView_VerticalMarqueeTextView_isRepeat, false);
    mStartPoint = a.getInt(R.styleable.VerticalMarqueeTextView_VerticalMarqueeTextView_startPoint, 0);
    mDirection = a.getInt(R.styleable.VerticalMarqueeTextView_VerticalMarqueeTextView_direction, 0);
    mSpeed = a.getInt(R.styleable.VerticalMarqueeTextView_VerticalMarqueeTextView_speed, 20);
    if (mSpeed < 5) {
        mSpeed = 5;
    }
    a.recycle();

    point = new Point(0, 0);
    holder = this.getHolder();
    holder.addCallback(this);
    mTextPaint = new TextPaint();
    mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
    mTextPaint.setTextAlign(Paint.Align.LEFT);
    setZOrderOnTop(true);//使surfaceview放到最顶层
    getHolder().setFormat(PixelFormat.TRANSLUCENT);//使窗口支持透明度
}

public void setText(String msg) {
    if (!TextUtils.isEmpty(msg)) {
        measurementsText(msg);
    }
}

protected void measurementsText(String msg) {
    margueeString = msg;
    mTextPaint.setTextSize(mTextSize);
    mTextPaint.setColor(mTextColor);
    mTextPaint.setStrokeWidth(0.5f);
    mTextPaint.setFakeBoldText(true);
    textWidth = (int) mTextPaint.measureText(margueeString);
    int height = getHeight() - getPaddingTop() - getPaddingBottom();
    if (mStartPoint == 0)
        currentY = 50;
    else
        currentY = height;
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    this.holder = holder;
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    if (mThread != null)
        mThread.isRun = true;
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if (mThread != null)
        mThread.isRun = false;
}

/**
 * 线程是否在运行
 *
 * @return 结果
 */
public boolean isThreadRunning() {
    return mThread != null && mThread.isRun && !mThread.isInterrupted();
}

/**
 * 开始滚动
 *
 * @param isStop 是否停止显示
 * @param sec    停止显示时间
 */
public void startScroll(boolean isStop, int sec) {
    if (mThread != null) {
        return;
    }
    this.isStop = isStop;
    this.sec = sec * 1000;
    /*
     * 设置绘制多行文本所需参数
     *
     * @param string      字符串
     * @param textPaint   画笔对象
     * @param canvas      canvas
     * @param point       点
     * @param outerWidth     layout宽度,超出时换行
     * @param align       layout的对齐方式,有ALIGN_CENTER, ALIGN_NORMAL, ALIGN_OPPOSITE 三种。
     * @param spacingmult 相对行间距,相对字体大小,1.5f表示行间距为1.5倍的字体高度。
     * @param spacingadd  在基础行距上添加多少
     * @param includepad  是否在文本上下添加额外空间,避免出界
     */
    staticLayout = new StaticLayout(margueeString, mTextPaint, getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.5f, 0, false);

    //获取所有字的累加高度
    textHeight = staticLayout.getHeight();
    isFirstDraw = true;
    mThread = new MarqueeViewThread(holder);//创建一个绘图线程
    mThread.isRun = true;
    mThread.start();
}

/**
 * 停止滚动
 */
public void stopScroll() {
    if (mThread != null) {
        mThread.isRun = false;
    }
    mThread = null;
}

/**
 * 暂停播放
 */
public void pauseScroll() {
    if (mThread != null) {
        mThread.isRun = false;
        mThread = null;
    }
}

/**
 * 恢复播放
 */
public void restartRoll() {
    mThread = new MarqueeViewThread(holder);
    mThread.isRun = true;
    mThread.start();
}

/**
 * 请空内容
 */
public void clearText() {
    if (mThread != null && mThread.isRun) {
        margueeString = "";
    }
}

/**
 * 是否继续滚动
 */
private boolean isGo = true;

/**
 * 线程
 */
class MarqueeViewThread extends Thread {

    private final SurfaceHolder holder;

    public boolean isRun;//是否在运行


    public MarqueeViewThread(SurfaceHolder holder) {
        this.holder = holder;
        isRun = true;
    }

    public void onDraw() {
        try {
            synchronized (holder) {
                if (TextUtils.isEmpty(margueeString)) {
                    Thread.sleep(1000);//睡眠时间为1秒
                    return;
                }
                if (isGo) {
                    final Canvas canvas = holder.lockCanvas();
                    int paddingTop = getPaddingTop();
                    int paddingBottom = getPaddingBottom();

                    int contentHeight = getHeight() - paddingTop - paddingBottom;

                    if (mDirection == 0) {//向上滚动
                        if (currentY <= -textHeight) {
                            currentY = contentHeight;
                            if (!mIsRepeat) {//如果是不重复滚动
                                mHandler.sendEmptyMessage(ROLL_OVER);
                                holder.unlockCanvasAndPost(canvas);//结束锁定画图,并提交改变。
                                return;
                            }
                        } else {
                            currentY -= sepY;
                        }
                        currentY -= sepY;
                    } else {//  向下滚动
                        if (currentY >= textHeight + sepY + 10) {
                            currentY = 0;
                            if (!mIsRepeat) {//如果是不重复滚动
                                mHandler.sendEmptyMessage(ROLL_OVER);
                                holder.unlockCanvasAndPost(canvas);//结束锁定画图,并提交改变。
                                return;
                            }
                        } else {
                            currentY += sepY;
                        }
                    }

                    if (canvas != null) {
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);//绘制透明色
                        textCenter(canvas, currentY);
                        holder.unlockCanvasAndPost(canvas);//结束锁定画图,并提交改变。
                        if (isFirstDraw) {
                            mHandler.sendEmptyMessageDelayed(STOP_ROLL, 50);//暂停显示5秒
                            isFirstDraw = false;
                        }
                    }
                    Thread.sleep(mSpeed);//睡眠时间为移动的频率~~
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        while (isRun) {
            onDraw();
        }
    }
}

/**
 * 绘制多行文本
 *
 * @param canvas canvas
 * @param height 绘制高度
 */
private void textCenter(Canvas canvas, int height) {
    canvas.save();
    canvas.translate(0, height);
    staticLayout.draw(canvas);
    canvas.restore();
}

public static final int ROLL_OVER = 100;//一条播放完毕
public static final int STOP_ROLL = 200;//停止滚动
public static final int START_ROLL = 300;//开始滚动
public static final int STOP_THREAT = 400;//停止线程a
Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);

        switch (msg.what) {
            case ROLL_OVER:
                stopScroll();
                if (mOnMargueeListener != null) {
                    mOnMargueeListener.onRollOver();
                }
                break;
            case STOP_ROLL:
                isGo = false;
                mHandler.sendEmptyMessageDelayed(START_ROLL, sec);
                break;
            case START_ROLL:
                isGo = true;
                break;
            case STOP_THREAT:
                stopScroll();
                break;
        }
    }
};

/**
 * dip转换为px
 *
 * @param context
 * @param dpValue
 * @return
 */
public static int dip2px(Context context, float dpValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
}

public void reset() {
    int contentWidth = getWidth() - getPaddingLeft() - getPaddingRight();
    int contentHeight = getHeight() - getPaddingTop() - getPaddingBottom();
    if (mStartPoint == 0)
        currentY = 0;
    else
        currentY = contentHeight;
}

/**
 * 滚动回调
 */
public interface OnMargueeListener {
    void onRollOver();//滚动完毕
}

OnMargueeListener mOnMargueeListener;

public void setOnMargueeListener(OnMargueeListener mOnMargueeListener) {
    this.mOnMargueeListener = mOnMargueeListener;
}

自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="VerticalMarqueeTextView">
        <attr name="VerticalMarqueeTextView_textColor" format="color"/>
        <attr name="VerticalMarqueeTextView_textSize" format="dimension"/>
        <attr name="VerticalMarqueeTextView_startPoint" format="integer"/>
        <attr name="VerticalMarqueeTextView_direction" format="integer"/>
        <attr name="VerticalMarqueeTextView_speed" format="integer"/>
        <attr name="VerticalMarqueeTextView_isRepeat" format="boolean"/>
    </declare-styleable>
</resources>

相关文章

网友评论

      本文标题:TextView跑马灯

      本文链接:https://www.haomeiwen.com/subject/xkkydrtx.html