安卓自定義view- TextPaint 文本畫筆

上一篇安卓自定義view-Paint 畫筆 已經對畫筆的經常使用 api 進行進行闡述總結,這一片主要討論安卓關於文字的處理方法。也是屬於畫筆的範疇。canvas

1. 畫筆設置API

. 構造方法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);
複製代碼

2. 測量文字相關

既然是測量文字,必然要知道安卓中是怎樣表示文字的,這個世界上不一樣的文字繁多,必然須要瞭解安卓系統是怎麼知足不一樣的文字的顯示問題的。請看下面的圖,這就是安卓系統文字的描述,對應的類是 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);

        }

複製代碼

因爲兩個不一樣字符串組合不算字形。因此是相同的。

相關文章
相關標籤/搜索