1. 程式人生 > >Android自定義TextView實現文字自動滾動

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);

第三個引數,垂直方向的中點和文字的基準點並不重合。


需做如下計算: 

        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>

工程檔案: