【Android自定義View】- 文字跑馬燈效果
簡介
有些時候,文字過長,或者有多條需要展示的文字時,我們需要將文字進行左右滾動,多條文字時,還得上下滾動以實現展示不同的文字內容。這時候就需要我們自定義view來實現文字跑馬燈效果了。
效果圖

jjj.gif
Android文字引數 - FontMetrics
當我們要自己繪製文字的時候,怎麼去計算文字的寬高,有些時候將本文的繪製起點設定為文字控制元件的中心點,卻發現繪製的文字並不是居中的,下面講解Android中文字是怎麼樣確定文字的繪製起點的。
-
基準點是baseline
-
Ascent是baseline之上至字元最高處的距離
-
Descent是baseline之下至字元最低處的距離
-
Leading文件說的很含糊,其實是上一行字元的descent到下一行的ascent之間的距離
-
Top指的是指的是最高字元到baseline的值,即ascent的最大值
-
bottom指的是最下字元到baseline的值,即descent的最大值
詳情圖

204735397.png
簡化版圖

ll.gif
在Android中,文字的繪製都是從Baseline處開始的,Baseline往上至字元最高處的距離我們稱之為ascent(上坡度),Baseline往下至字元最底處的距離我們稱之為descent(下坡度),而leading(行間距)則表示上一行字元的descent到該行字元的ascent之間的距離,top和bottom文件描述地很模糊,其實這裡我們可以借鑑一下TextView對文字的繪製,TextView在繪製文字的時候總會在文字的最外層留出一些內邊距,為什麼要這樣做?因為TextView在繪製文字的時候考慮到了類似讀音符號,可能大家很久沒寫過拼音了已經忘了什麼叫讀音符號了吧……下圖中的A上面的符號就是一個拉丁文的類似讀音符號的東西:

gg.png
文字居中繪製:
// 計算Baseline繪製的起點X軸座標 baseX = (int) (canvas.getWidth() / 2 - textPaint.measureText(TEXT) / 2); // 計算Baseline繪製的Y座標 baseY = (int) ((canvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2)); canvas.drawText(TEXT, baseX, baseY, textPaint);
思路
- 單條文字
private void doSingleLineText(Canvas canvas){ viewHeight = getMeasuredHeight() + 10; viewWidth = getMeasuredWidth(); contentBound.set(0,0,0,0); contentPaint.getTextBounds(singleText, 0, singleText.length(), contentBound); xOffset = contentBound.width() - viewWidth; Paint.FontMetrics fontMetrics = contentPaint.getFontMetrics(); int textHeight = (int) ((-fontMetrics.ascent - fontMetrics.descent) / 2); yStartPos = viewHeight / 2 + maxContentHeight / 4 + textHeight / 4; if (!hasInited) { hasInited = true; currentX = 0; xStartPos = currentX; } if (xOffset > 0) { xOffset += contentTextSize * 2; if (!isHorizontalRunning) { isHorizontalRunning = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { startHorizontalLoop(); } } } canvas.drawText(singleText, currentX, yStartPos, contentPaint); } private void startHorizontalLoop() { ValueAnimator horizontalScrollAnimator; if (horizontalOriLeft) { horizontalScrollAnimator = ValueAnimator.ofFloat(0, 1); } else { horizontalScrollAnimator = ValueAnimator.ofFloat(0, -1); } if (horizontalScrollSpeed * xOffset / contentTextSize < 0) { isHorizontalRunning = false; return; } horizontalScrollAnimator.setDuration(horizontalLoopSpeed * xOffset / contentTextSize); horizontalScrollAnimator.setInterpolator(mLinearInterpolator); horizontalScrollAnimator.start(); horizontalScrollAnimator.addUpdateListener(mHorizontalLoopUpdateListener); horizontalScrollAnimator.addListener(mHorizontalLoopListenerAdapter); }
上面的邏輯其實很簡單,可以自己檢視,我這裡隨便說一下,Android文字相關的知識
- 多條文字
private void doMultiLineText(Canvas canvas){ if (currentIndex >= contentList.size()) { currentIndex = 0; } viewHeight = getMeasuredHeight(); viewWidth = getMeasuredWidth(); String currentString = contentList.get(currentIndex); int nextIndex = currentIndex + 1; if (currentIndex + 1 >= contentList.size()) { nextIndex = 0; } String nextString = contentList.get(nextIndex); contentBound.set(0,0,0,0); contentPaint.getTextBounds(currentString, 0, currentString.length(), contentBound); xOffset = contentBound.width() - viewWidth; Paint.FontMetrics fontMetrics = contentPaint.getFontMetrics(); int textHeight = (int) ((-fontMetrics.ascent - fontMetrics.descent) / 2); yStartPos = viewHeight / 2 + maxContentHeight / 4 + textHeight / 4; if (!hasVerticalInited) { hasVerticalInited = true; currentY = yStartPos; } if (xOffset > 0) { //另外加點留白.設留白兩個字寬 xOffset += contentTextSize * 2; if (!isHorizontalRunning && !isVerticalRunning) { isHorizontalRunning = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { startHorizontalScroll(); } currentX = 0; } } else { if (!isVerticalRunning) { isVerticalRunning = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { startVerticalInterval(); } currentX = 0; } } canvas.drawText(currentString, currentX, currentY, contentPaint); canvas.drawText(nextString, 0, currentY + viewHeight, contentPaint); }
多條文字,其實是在單條文字的基礎上,加了一個向上滾動的動畫,當像左滾動結束後,開啟向上滾動動畫,同時,當前滾動動畫的文字index加一,詳細程式碼請檢視原始碼,下面會給出完成的原始碼地址。