Android自定义TextView实现文字自动滚动

效果:循环滚动,类似广告条



思路:

开启定时器刷新绘制文本的位置即可达到效果。

步骤1:新建ScrollTextView类继承自TextView。代码如下:

public class ScrollTextView extends TextView {

    private static final String TAG = "ScrollTextView";
    private String mText = "蒹葭苍苍,白露为霜。所谓伊人,在水一方。";
    private int mOffsetX = 0;
    private Rect mRect;
    private Timer mTimer;
    private TimerTask mTimerTask;
	
	    /**
     * 速度,负数左移,正数右移。
     */
    private int mSpeed = -10;
    private static final int PFS = 24;
	
    public ScrollTextView(Context context) {
        this(context, null);
    }
    public ScrollTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRect = new Rect();
        mTimer = new Timer();
        mTimerTask = new MyTimerTask();
        mTimer.schedule(mTimerTask, 0, 1000 / 24);
    }

    private class MyTimerTask extends TimerTask {
        @Override
        public void run() {
            //如果View能容下所有文字,直接返回
            if (mRect.right < getWidth()){
                return;
            }
            if (mOffsetX < - mRect.right - getPaddingEnd()){
                //左移时的情况
                mOffsetX = getPaddingStart();
            } else if (mOffsetX > getPaddingStart()){
                //右移时的情况
                mOffsetX = - mRect.right;
            }
            mOffsetX += mSpeed;
            postInvalidate();
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //获取文本区域大小,保存在mRect中。
        getPaint().getTextBounds(mText, 0, mText.length(), mRect);
        if (mRect.right < getWidth()){
            canvas.drawText(mText, 0, getHeight() / 2, getPaint());
        }else {
            canvas.drawText(mText, mOffsetX, getHeight() / 2, getPaint());
        }
    }

    /**
     * 视图移除时销毁任务和定时器
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.e(TAG, "killTimer");
        if (mTimerTask != null){
            mTimerTask.cancel();
            mTimerTask = null;
        }
        if (mTimer != null){
            mTimer.cancel();
            mTimer = null;
        }
    }
}

完成上面代码可以的到如下效果:

以上是硬编码文字,如果尝试在XML布局或代码设置文字,将会显示的一塌糊涂:

    <com.fanhongfei.scrolltext.ScrollTextView
        android:id="@+id/scrollTextView"
        android:layout_width="150dp"
        android:layout_height="60dp"
        android:background="#EE4000"
        android:text="开关磁阻电机的优缺点及使用"
        android:textColor="#FFFF" />
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ScrollTextView textView = findViewById(R.id.scrollTextView);
        textView.setText("开关磁阻电机的优缺点及使用");
    }


步骤2:需要对onDraw()做一些更改,如下:

    @Override
    protected void onDraw(Canvas canvas) {
        //此处去掉了super.onDraw(Canvas canvas);
        mText = getText().toString();
        TextPaint textPaint = getPaint();
        textPaint.setColor(getCurrentTextColor());
        //获取文本区域大小,保存在mRect中。
        textPaint.getTextBounds(mText, 0, mText.length(), mRect);
        if (mRect.right < getWidth()){
            canvas.drawText(mText, 0, getHeight() / 2, textPaint);
        }else {
            canvas.drawText(mText, mOffsetX, getHeight() / 2, textPaint);
        }
    }

一切都很正常,把字体调大再看看,发现并没有居中显示,而是稍稍偏上。


问题就在于:

canvas.drawText(mText, mOffsetX, getHeight() / 2, textPaint);

第三个参数,垂直方向的中点和文字的基准点并不重合。


图片引用自 android canvas drawText()文字居中   感谢原作,侵权立删。

需做如下计算: 

        float mTextCenterVerticalToBaseLine =
                ( - textPaint.ascent() + textPaint.descent()) / 2 - textPaint.descent();

这样就能文字居中了:

canvas.drawText(mText, mOffsetX, getHeight() / 2 + mTextCenterVerticalToBaseLine, textPaint);

别忘了在布局XML中设置单行显示,因为没有重写测量方法,布局使用wrap_content时得到的并不是我们想要的尺寸。

 android:maxLines="1"

到此就完成了。

最后附上完整代码:

自定义的ScrollTextView类:

public class ScrollTextView extends TextView {

    private static final String TAG = "ScrollTextView";
    private String mText = "蒹葭苍苍,白露为霜。所谓伊人,在水一方。";
    private int mOffsetX = 0;
    private Rect mRect;
    private Timer mTimer;
    private TimerTask mTimerTask;
    /**
     * 速度,负数左移,正数右移。
     */
    private int mSpeed = -10;
    private static final int PFS = 24;

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

    public ScrollTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRect = new Rect();
        mTimer = new Timer();
        mTimerTask = new MyTimerTask();
        //更新帧率24
        mTimer.schedule(mTimerTask, 0, 1000 / PFS);
    }

    private class MyTimerTask extends TimerTask {
        @Override
        public void run() {
            //如果View能容下所有文字,直接返回
            if (mRect.right < getWidth()){
                return;
            }
            if (mOffsetX < - mRect.right - getPaddingEnd()){
                //左移时的情况
                mOffsetX = getPaddingStart();
            } else if (mOffsetX > getPaddingStart()){
                //右移时的情况
                mOffsetX = - mRect.right;
            }
            mOffsetX += mSpeed;
            postInvalidate();
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        //此处去掉了super.onDraw(Canvas canvas);
        mText = getText().toString();
        TextPaint textPaint = getPaint();
        textPaint.setColor(getCurrentTextColor());
        //获取文本区域大小,保存在mRect中。
        textPaint.getTextBounds(mText, 0, mText.length(), mRect);
        float mTextCenterVerticalToBaseLine =
                ( - textPaint.ascent() + textPaint.descent()) / 2 - textPaint.descent();
        if (mRect.right < getWidth()){
            canvas.drawText(mText, 0, getHeight() / 2 + mTextCenterVerticalToBaseLine, textPaint);
        }else {
            canvas.drawText(mText, mOffsetX, getHeight() / 2 + mTextCenterVerticalToBaseLine, textPaint);
        }
    }

    /**
     * 视图移除时销毁任务和定时器
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.e(TAG, "killTimer");
        if (mTimerTask != null){
            mTimerTask.cancel();
            mTimerTask = null;
        }
        if (mTimer != null){
            mTimer.cancel();
            mTimer = null;
        }
    }

    public void setSpeed(int speed){
        this.mSpeed = speed;
    }
}

MainActivity:

public class MainActivity extends Activity {

    String text1 = "桑之未落,其叶沃若。于嗟鸠兮,无食桑葚;\n" +
            "于嗟女兮,无与士耽。士之耽兮,犹可说也;\n" +
            "女之耽兮,不可说也。";

    String text2 = "桑之落矣,其黄而陨。自我徂尔,三岁食贫。\n" +
            "淇水汤汤,渐车帷裳。女也不爽,士贰其行。\n" +
            "士也罔极,二三其德。";

    String text3 = "三岁为妇,靡室劳矣;夙兴夜寐,靡有朝矣。\n" +
            "言既遂矣,至于暴矣。兄弟不知,咥其笑矣。\n" +
            "静言思之,躬自悼矣。";

    String text4 = "及尔偕老,老使我怨。淇则有岸,隰则有泮。\n" +
            "总角之宴,言笑晏晏。信誓旦旦,不思其反。\n" +
            "反是不思,亦已焉哉!";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ScrollTextView textView1 = findViewById(R.id.scrollTextView1);
        textView1.setText(text1);
        textView1.setSpeed(-5);

        ScrollTextView textView2 = findViewById(R.id.scrollTextView2);
        textView2.setText(text2);
        textView2.setSpeed(10);

        ScrollTextView textView3 = findViewById(R.id.scrollTextView3);
        textView3.setText(text3);
        textView3.setSpeed(-15);

        ScrollTextView textView4 = findViewById(R.id.scrollTextView4);
        textView4.setText(text4);
        textView4.setSpeed(20);
    }
}

布局文件activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    android:gravity="center"
    android:orientation="vertical"
    tools:context="com.fanhongfei.scrolltext.MainActivity">

    <com.fanhongfei.scrolltext.ScrollTextView
        android:id="@+id/scrollTextView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#EE4000"
        android:maxLines="1"
        android:textColor="#FFFF"
        android:textSize="14sp" />

    <com.fanhongfei.scrolltext.ScrollTextView
        android:id="@+id/scrollTextView2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="#EE4000"
        android:maxLines="1"
        android:textColor="#DAA520"
        android:textSize="24sp" />

    <com.fanhongfei.scrolltext.ScrollTextView
        android:id="@+id/scrollTextView3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="#EE4000"
        android:maxLines="1"
        android:textColor="#FFFF"
        android:textSize="28sp" />

    <com.fanhongfei.scrolltext.ScrollTextView
        android:id="@+id/scrollTextView4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:background="#EE4000"
        android:maxLines="1"
        android:textColor="#CDCD00"
        android:textSize="32sp" />

</LinearLayout>

工程文件:

https://github.com/peoples-mountain-peoples-sea/ScrollText

  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值