Android自定義View系列
- Android自定義View之Canvas的使用
- Android自定義View注意事項
- Android自定義View之影像的色彩處理
- Android自定義View之圖片外形特效——輕鬆實現圓角和圓形圖片
- Android自定義View之雙緩衝機制和SurfaceView
- Android自定義View之invalidate方法和postInvalidate方法
- Android自定義View之事件分發機制總結
- Android自定義View之requestLayout方法和invalidate方法
用繼承View的方式來自定義View,我們就需要重寫onDraw方法,也就是得我們自己來畫圖了。畫圖就得用到畫筆和畫布,也就是Paint和Canvas。我們先來了解下Paint。
Paint
Paint我們可以簡單理解為畫筆或是PS裡的油漆桶,也就是實際上需要設定比如顏色、粗細、字型大小等屬性的物件。我們在通過繼承View來自定義View時,就是通過設定Paint的屬性來控制我們畫出來的View的一些特性。
Paint的一些常見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;
}
複製程式碼
從圖中我們可以看出,文字繪製以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也一樣,只能設定一個,設定多個時只有最後一個有效
今天你進步了嘛?歡迎關注我的微信公眾號,和我一起每天進步一點點!