Android自定義View之Paint繪製文字和線

xxq2dream發表於2018-07-30

Android自定義View系列

用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得我們自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們先來了解下Paint。

Paint

Paint我們可以簡單理解為畫筆或是PS裡的油漆桶,也就是實際上需要設定比如顏色、粗細、字型大小等屬性的物件。我們在通過繼承View來自定義View時,就是通過設定Paint的屬性來控制我們畫出來的View的一些特性。

Paint的一些常見API

Paint的set相關的API

1.設定文字的對齊方式:setTextAlign()

//設定Paint的文字對齊方式
textPaint = new Paint();
textPaint.setTextAlign(Paint.Align.RIGHT);
...

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Log.e(TAG, "onDraw");
    
    //文字的起點為(getWidth()/2,getHeight()/2)
    canvas.drawText(text, getWidth()/2,getHeight()/2,textPaint);
}
複製程式碼

對齊方式有左中右三種

//對齊方式有左中右三種
public enum Align {
    /**
     * The text is drawn to the right of the x,y origin
     */
    LEFT    (0),
    /**
     * The text is drawn centered horizontally on the x,y origin
     */
    CENTER  (1),
    /**
     * The text is drawn to the left of the x,y origin
     */
    RIGHT   (2);

    private Align(int nativeInt) {
        this.nativeInt = nativeInt;
    }
    final int nativeInt;
}
複製程式碼

需要注意的是,這裡的對齊方式指的是和繪製原點的對齊方式,也就是上面canvas.drawText方法中我們設定的繪製起點。比如我們設定的是右對齊,那就是文字的右邊和繪製起點對齊,具體效果可以看圖

設定了右對齊後

2.設定Paint的顏色、字型大小和字型

//這裡設定Paint的顏色後,如果繪製的是字型那就是字型顏色,如果繪製的線條,那就是線條的顏色
textPaint.setColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
textPaint.setTextSize(80f);
//設定字型加粗
textPaint.setTypeface(Typeface.DEFAULT_BOLD);

複製程式碼

字型樣式有很多,除了常見的加粗,還有斜體等。這些都在Typeface類中

public class Typeface {
    
    //字型樣式,除了加粗,其他的好像看不出多大的區別
    public static final Typeface DEFAULT_BOLD;
    /** The NORMAL style of the default sans serif typeface. */
    public static final Typeface SANS_SERIF;
    /** The NORMAL style of the default serif typeface. */
    public static final Typeface SERIF;
    /** The NORMAL style of the default monospace typeface. */
    public static final Typeface MONOSPACE;
    
    // Style
    public static final int NORMAL = 0;
    //加粗
    public static final int BOLD = 1;
    //傾斜
    public static final int ITALIC = 2;
    //加粗並傾斜
    public static final int BOLD_ITALIC = 3;
}
複製程式碼

設定加粗、傾斜

Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD_ITALIC);
textPaint.setTypeface(font);
複製程式碼

Typeface類還提供了載入自定義字型的API

//從assets資源中載入
Typeface createFromAsset(AssetManager mgr, String path)
//從檔案中載入字型
Typeface createFromFile(String path)
public static Typeface createFromFile(File path)
複製程式碼

3.設定Paint的style

textPaint.setStyle(Paint.Style.FILL);
複製程式碼

style有3種,分別為實心、空心和實心描邊。

public enum Style {
    //實心
    FILL            (0),
    
    //空心
    STROKE          (1),
    
    //實心描邊
    FILL_AND_STROKE (2);

    Style(int nativeInt) {
        this.nativeInt = nativeInt;
    }
    final int nativeInt;
}
複製程式碼

測試發現對文字和線都會有影響,比如如果我們設定了文字的下劃線,那FILL就是實線,而設定STROKE就會變成空心的一條線。如果我們是畫一個矩形的話,那FILL就是實心的矩形,而STROKE就是一個矩形框,FILL_AND_STROKE效果大家可以試一下。文字的話STROKE就是文字的筆畫中間是空的,Fill就是實心的。

下劃線變成空心的了

4.設定縮放:setTextScaleX(float scaleX)。scaleX範圍在0-1之間為縮小,大於1為放大

5.設定下劃線和刪除線有2種方式,一種是直接呼叫Paint的API,一種是直接設定Paint的Flag

1) 直接通過Paint的API設定文字的下劃線和刪除線

//設定下劃線
textPaint.setUnderlineText(true);
//設定文字中間的刪除線
textPaint.setStrikeThruText(true);
複製程式碼

2) 用設定Paint的Flag的方式設定文字的下劃線和刪除線

設定下劃線
textPaint.setFlags(Paint.UNDERLINE_TEXT_FLAG);
//設定文字中間的刪除線
textPaint.setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
複製程式碼

需要注意的是Paint的Flag相互之間是衝突的,如果設定了多個Flag,那只有最後一個Flag會生效。像上面的例子中,通過Flag來設定文字的下劃線和刪除線,最後只有刪除線會生效。而通過Paint的API就不會有這個問題,刪除線和下劃線都會同時生效。

6.設定Paint的抗鋸齒Flag。一般我們都要設定這個抗鋸齒效果,否則畫出來的線和文字會坑坑窪窪的,設定了以後就會很平滑。

textPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
複製程式碼

這裡也就引申出一個問題:那就是我們一搬都會設定Paint的Flag為抗鋸齒效果,所以如果我們要設定文字的下劃線和刪除線,就只能通過Paint的API來設定了。

文字居中的問題

不知道大家發現沒有,上面圖片中的文字儘管繪製的時候設定的位置是 (getWidth()/2,getHeight()/2) 但實際效果卻並沒有居中

(1)水平方向居中問題

  • 水平方向沒有居中是因為文字本身有寬度,所以要先獲取文字的寬度
float textWidth = textPaint2.measureText(text);
複製程式碼
  • 然後重新計算繪製的水平座標
canvas.drawText(text, (getWidth()-textWidth)/2,getHeight()/2,textPaint2);
複製程式碼

(2)豎直方向居中問題

文字的繪製規則跟Paint的一個內部類有關:FontMetrics

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;
}
複製程式碼

FontMetrics引數值含義

從圖中我們可以看出,文字繪製以baseline為標準,我們將baseline設定為getHeight()/2後,文字勢必會往上偏,所以我們要想讓文字在豎直方向上居中,baseline需要往下一點。

Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float y = getHeight()/2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent)/2;
canvas.drawText(text, (getWidth()-textWidth)/2,y,textPaint);

複製程式碼

需要注意的是Paint的TextAlign屬性需要去掉,否則無法準確居中

這樣子文字就居中了

文字終於居中啦

用Paint繪製線

PathEffect setPathEffect(PathEffect effect)
複製程式碼

1)通過setPathEffect方法可以設定路徑效果,一共可以設定7種路徑效果,比較常見的有虛線和拐角圓滑效果

//CornerPathEffect用於增加點與點之間的圓弧效果,CornerPathEffect中的參數列示圓弧效果的半徑
brokenLinePaint.setPathEffect(new CornerPathEffect(5));
複製程式碼
//DashPathEffect用於繪製虛線,第一個參數列示線段各個點之間的偏移,第二個參數列示繪製時陣列的偏移量
brokenLinePaint.setPathEffect(new DashPathEffect(new float[]{5f,5f,5f,5f}, 1f));
複製程式碼

需要注意的是Paint的PathEffect也只能設定一個,設定多個時只有最後一個設定有效

2)Paint繪製折線需要用到一個Path類,用於存放折線的各個點

1)初始化

Path mPath = new Path();
複製程式碼

2)設定折線的起點

//2個引數分別為起點的X座標和Y座標
mPath.moveTo(mPaddingLeft, mHeight-mPaddingBottom);
複製程式碼

3)新增折線上的其他點

 mPath.lineTo(pointX, pointy);
複製程式碼

4)繪製

canvas.drawPath(mPath, brokenLinePaint);
複製程式碼

總結

  • Paint是繪製View必須要掌握的,我們只需要掌握Paint的常見的API就行了,包括繪製文字的和繪製線的。
  • Paint繪製文字時需要注意文字居中的問題,水平居中需要考慮文字的寬度,豎直居中需要考慮文字繪製的baseline,這就需要注意理解FontMetrics,如果不理解的話直接記結論也行
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float y = getHeight()/2 + (Math.abs(fontMetrics.ascent) - fontMetrics.descent)/2;
canvas.drawText(text, (getWidth()-textWidth)/2,y,textPaint);
複製程式碼
  • Paint的Flag只能設定一個,設定多個時也只有最後一個有效。
  • Paint的PathEffect和Flag也一樣,只能設定一個,設定多個時只有最後一個有效

今天你進步了嘛?歡迎關注我的微信公眾號,和我一起每天進步一點點!

AntDream

相關文章