上一篇安卓自定義view-Paint 畫筆 已經對畫筆的經常使用 api 進行進行闡述總結,這一片主要討論安卓關於文字的處理方法。也是屬於畫筆的範疇。canvas
. 構造方法api
在安卓中繪製文字相關,有專門的處理畫筆。bash
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
複製代碼
. 設置文字大小dom
mTextPaint.setTextSize(30);
複製代碼
. 設置文本對齊方式post
文本對齊方式有三中分別是: LEFT(左對齊)、CENTER(居中對齊)、RIGHT(右對齊), 用過 office 辦公軟件的應該都不會陌生。測試
mTextPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(text, (float)(mWidth / 2 - width / 2) ,
400 + 2 * height,
mTextPaint);
複製代碼
. 設置地區文字語言字體
不過我在真機上測試並無任何變化,個人真機只有簡體、繁體、中文切換。模擬器測試野咩用。 有大佬知道的好心講解一下👍。ui
mTextPaint.setTextLocale(Locale.CHINA);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setTextLocale(Locale.TAIWAN);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
複製代碼
. 設置文字水平縮放spa
大於 1 邊寬,小於 1 變窄, 等於 1 沒有變化。3d
mTextPaint.setTextScaleX(3.0f);
複製代碼
. 設置文字錯切
即讓文字有必定傾斜角度, 大於 0 逆時針, 小於 0 順時針。
mTextPaint.setTextSkewX(0.5f);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setTextSkewX(-0.5f);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
複製代碼
. 設置下劃線
mTextPaint.setUnderlineText(true);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
複製代碼
. 設置刪除線
mTextPaint.setStrikeThruText(true);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
複製代碼
. 設置文字加粗
mTextPaint.setFakeBoldText(true);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
mTextPaint.setFakeBoldText(false);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400 + height,
mTextPaint);
複製代碼
. 設置文字間距
mTextPaint.setLetterSpacing(1.5f);
複製代碼
. drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,float vOffset, @NonNull Paint paint)
根據路徑繪製文本內容。
mPath = new Path();
for (int i = 0; i < 10; i++) {
mPath.lineTo((float)(Math.random() * 500 + i * 35), (float)(Math.random() * 500 + 250));
}
canvas.drawPath(mPath, mPaint);
canvas.drawTextOnPath(text, mPath, 100, 0, mTextPaint);
複製代碼
. StaticLayout
用來顯示多行文本,它是一個容器,經過自身的 draw() 進行文本的繪製。StaticLayout 支持換行,它既能夠爲文字設置寬度上限來讓文字自動換行,也會在換行符 \n 處主動換行。
private String text = "滾滾長江東逝水, 浪花淘盡英雄,是非成敗轉頭, 青山依舊在,幾度夕陽紅";
layout = new StaticLayout(text, mTextPaint,
mWidth,
Layout.Alignment.ALIGN_NORMAL,
1.0f,
0.0f,
false);
layout.draw(canvas);
複製代碼
. setTypeface(Typeface typeface) 設置字體
private String text = "滾滾長江東逝水, 浪花淘盡英雄,是非成敗轉頭, 青山依舊在,幾度夕陽紅";
mTextPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "ygyqianmt.ttf"));
layout = new StaticLayout(text, mTextPaint,
mWidth,
Layout.Alignment.ALIGN_NORMAL,
1.0f,
0.0f,
false);
layout.draw(canvas);
複製代碼
既然是測量文字,必然要知道安卓中是怎樣表示文字的,這個世界上不一樣的文字繁多,必然須要瞭解安卓系統是怎麼知足不一樣的文字的顯示問題的。請看下面的圖,這就是安卓系統文字的描述,對應的類是 Paint 的內部類 FontMetrics。其描述以下:
/**
* Class that describes the various metrics for a font at a given text size.
* Remember, Y values increase going down, so those values will be positive,
* and values that measure distances going up will be negative. This class
* is returned by getFontMetrics().
*/
public static class FontMetrics {
/**
* The maximum distance above the baseline for the tallest glyph in
* the font at a given text size.
*/
public float top;
/**
* The recommended distance above the baseline for singled spaced text.
*/
public float ascent;
/**
* The recommended distance below the baseline for singled spaced text.
*/
public float descent;
/**
* The maximum distance below the baseline for the lowest glyph in
* the font at a given text size.
*/
public float bottom;
/**
* The recommended additional space to add between lines of text.
*/
public float leading;
}
複製代碼
來解釋說明下這幾個值的含義,首先看 Baseline,這個叫基準線,爲何要設置這個呢?說白了就是爲了排版更加美觀。咱們最後一張圖,基準線紅色線到上面的綠色線之間的距離爲上坡度 ascent, 基準線到下面藍色線的距離爲下坡度 descent。可是有的文字有相似與中文拼音的標點符號,因此須要額外預留一個空間,在上面的部分從 ascent 到頂部還有一個空間稱爲 top, 下面也是同樣稱爲 bottom。這就說明了 top 應該是要比 ascent 要大一點。bottom 比 descent 要大。leading 爲上一個文字的 bottom 到當前文字的 top 之間的距離。 咱們能夠在代碼中打印看看。
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
Log.i(TAG, "top: " + fontMetrics.top);
Log.i(TAG, "ascent: " + fontMetrics.ascent);
Log.i(TAG, "descent: " + fontMetrics.descent);
Log.i(TAG, "bottom: " + fontMetrics.bottom);
canvas.drawText(text, (float)(mWidth / 2 - width / 2),
400,
mTextPaint);
複製代碼
是否是跟描述的一致,還發如今安卓中基準線如下爲正,基準線以上爲負數。
. float getFontSpacing()
獲取文本行間距,它的計算爲 descent - ascent 之間的距離,而不是 bottom - top + leading,這個值是要比前面計算的要大的。爲了讓文字排版更加好看,而不至於間距過大,因此選擇前面的方式計算。
private String text1 = "少小離家老大回";
private String text2 = "鄉音未改鬢毛衰";
private String text3 = "兒童相見不相識";
private String text4 = "笑問客從何處來";
canvas.drawText(text1, 300, 300, mTextPaint);
canvas.drawText(text2, 300, 300 + mTextPaint.getFontSpacing(), mTextPaint);
canvas.drawText(text3, 300, 300 + 2 * mTextPaint.getFontSpacing(), mTextPaint);
canvas.drawText(text4, 300, 300 + 3 * mTextPaint.getFontSpacing(), mTextPaint);
複製代碼
咱們能夠看看 getFontSpacing() 的源碼,能夠發現它內部是經過 getFontMetrics 來計算的。
public float getFontSpacing() {
return getFontMetrics(null);
}
複製代碼
.FontMetircs getFontMetrics()
在前面一個方法中已經看到過它的身影啦!也是經過這個方法拿到 descent、ascent、top、bottom、leading 等值。咱們這裏打印一下 bottom - top + leading, descent - ascent 的值。
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
Log.i(TAG, "bottom - top + leading: " + (fontMetrics.bottom - fontMetrics.top + fontMetrics.leading));
Log.i(TAG, "descent - ascent: " + (fontMetrics.descent - fontMetrics.leading));
Log.i(TAG, "getFontSpacing: " + mTextPaint.getFontSpacing());
複製代碼
這樣驗證了前面所說的,getFontSpacing() 的計算爲 descent - ascent
. getTextBounds(String text, int start, int end, Rect bounds)
從字面意義可知,這個是用來獲取文字的範圍的。會將計算的值存儲到一個 rect 中。能夠經過 rect 去獲取文本的寬和高。
mTextPaint.getTextBounds(text1, 0, text1.length(), mTextRect);
canvas.drawText(text1, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
複製代碼
. getTextBounds(char[] text, int index, int count, Rect bounds)
mTextPaint.getTextBounds(chars, 0, chars.length, mTextRect);
canvas.drawText(chars, 0, chars.length - 1, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
複製代碼
. float measureText(String text) 測量文本寬度
mTextPaint.getTextBounds(chars, 0, chars.length, mTextRect);
Log.i(TAG, "width: " + mTextRect.width());
Log.i(TAG, "height: " + mTextRect.height());
float measureTextWidth = mTextPaint.measureText(chars, 0, chars.length);
Log.i(TAG, "measureTextWidth: " + measureTextWidth);
複製代碼
能夠看到使用 measureText 要比 getTextBounds 要大一點,這是由於文字之間有間隙,加上文字間距後再看效果。
mTextPaint.setLetterSpacing(1.5f);
mTextPaint.getTextBounds(chars, 0, chars.length, mTextRect);
Log.i(TAG, "width: " + mTextRect.width());
Log.i(TAG, "height: " + mTextRect.height());
float measureTextWidth = mTextPaint.measureText(chars, 0, chars.length);
Log.i(TAG, "measureTextWidth: " + measureTextWidth);
canvas.drawText(chars, 0, chars.length, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
複製代碼
是否是發現變大了許多,這就是有間距的區別。
. int getTextWidths(char[] text, int index, int count, float[] widths)
獲取指定字符的寬度,至關與對每一個字符執行 measureText。將測量到的字符寬度存放到 widths 中,它的變體方法也是相似的。
mTextPaint.getTextWidths(chars, 0, 3, widths);
Log.i(TAG, "width: " + widths[0]);
複製代碼
. int breakText(char[] text, int index, int count,float maxWidth, float[] measuredWidth)
int number = mTextPaint.breakText(chars, 0, chars.length, 400, measureWidths);
Log.i(TAG, "breakText width: " + measureWidths[0]);
canvas.drawText(chars, number, chars.length - number, 300, 300, mTextPaint);
mPaint.setColor(Color.BLACK);
canvas.drawRect(mTextRect.left + 300,
mTextRect.top + 300,
mTextRect.right + 300,
mTextRect.bottom + 300, mPaint);
複製代碼
. getRunAdvance(CharSequence text, int start, int end, int contextStart, int contextEnd, boolean isRtl, int offset)
計算光標位置, 在 API 23 後引入。這幾個參數意思:start 起始字符位置,end 字符結束位置, contextStart: 上下文的字符其實起始位置, contextEnd: 上下文字符結束位置。 文字方向,從左往右或從右往左, offset: 須要測量的字符個數
必須符合這個條件
0 <= contextStart <= start <= offset <= end <= contextEnd <= text.length
複製代碼
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
float runAdvance = mTextPaint.getRunAdvance(chars,
0,
3,
0,
chars.length,
false,
2);
Log.i(TAG, "runAdvance: " + runAdvance);
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
canvas.drawLine(mTextRect.left + runAdvance + 300, mTextRect.top + 300,
mTextRect.left + runAdvance + 300, mTextRect.bottom + 300, mPaint);
}
複製代碼
mTextPaint.getTextBounds(text, 0, text.length(), mTextRect);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
float runAdvance = mTextPaint.getRunAdvance(text,
0,
text.length(),
0,
text.length(),
false,
text.length());
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(3);
canvas.drawLine(mTextRect.left + 300 + runAdvance,
mTextRect.top + 300,
mTextRect.left + 300 + runAdvance,
mTextRect.bottom + 300,
mPaint);
}
canvas.drawText(text, 300, 300, mTextPaint);
複製代碼
.hasGlyph(String string)
檢查是否相同字形。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean b = mTextPaint.hasGlyph("\uD83C\uDDE8\uD83C\uDDF3");
boolean aa = mTextPaint.hasGlyph("aa");
boolean ab = mTextPaint.hasGlyph("ab");
Log.i(TAG, "b: " + b);
Log.i(TAG, "aa: " + aa);
Log.i(TAG, "ab: " + ab);
}
複製代碼
因爲兩個不一樣字符串組合不算字形。因此是相同的。