上篇文章介紹了自定義View的創建流程,從巨集觀上給出了一個自定義View的創建步驟,本篇是上一篇文章的延續,介紹了自定義View中兩個必不可少的工具Canvas和Paint,從細節上更進一步的講解自定義View的詳細繪製方法。如果把自定義View比作蓋一座房子,那麼上篇文章就相當於教會了我們怎麼一步 ...
上篇文章介紹了自定義View的創建流程,從巨集觀上給出了一個自定義View的創建步驟,本篇是上一篇文章的延續,介紹了自定義View中兩個必不可少的工具Canvas和Paint,從細節上更進一步的講解自定義View的詳細繪製方法。如果把自定義View比作蓋一座房子,那麼上篇文章就相當於教會了我們怎麼一步步的搭建房子的骨架,而本篇文章將要教會我們的是為房子的骨架添磚加瓦直至成型,甚至是怎麼裝修。
Canvas
為了後文更為方便的講解Canvas的常用方法的使用,我們先來做一些準備工作,創建一個自定義View框架,先初始化一下Paint畫筆,並設置相關方法:
public class StudyView extends View {
private Paint mPaint;
private Context mContext;
public StudyView(Context context) {
super(context);
init(context);
}
public StudyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mContext = context;
mPaint = new Paint();
mPaint.setAntiAlias(true); // 消除鋸齒
mPaint.setStrokeWidth(5); // 設置筆尖寬度
mPaint.setStyle(Paint.Style.STROKE); // 不填充
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
繪製圓弧和扇形
Canvas提供drawArc()方法,通過傳遞不同的參數可用來繪製圓弧和扇形,此方法有兩個重載方法,詳細參數如下:
- drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
- left:扇形或圓弧所占區域的左邊界線x坐標
- top:扇形或圓弧所占區域的上邊界線y坐標
- right:右邊界線x坐標
- bottom:下邊界線y坐標
- startAngle:扇形或圓弧的起始角度
- sweepAngle:掃過的角度
- userCenter:此參數可以理解為true就是畫扇形,false就是畫圓弧
- paint:畫筆
- drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
此方法第一個參數是一個RectF類,也是邊界,就是把一個方法的left,top,right,bottom封裝到了RectF類中,剩餘參數與上一個方法一致。
接下來用著兩個重載方法分別繪製兩個90°的扇形和兩個90°的圓弧:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 繪製扇形
canvas.drawArc(0, 0, 200, 200, 0, 90, true, mPaint);
RectF rectF = new RectF(0, 0, 200, 200);
canvas.drawArc(rectF, 180, 90, true, mPaint);
// 繪製圓弧
canvas.drawArc(300, 0, 500, 200, 0, 90, false, mPaint);
RectF rectF1 = new RectF(300, 0, 500, 200);
canvas.drawArc(rectF1, 180, 90, false, mPaint);
}
繪製效果如下圖所示,另外需要說明的一點是,drawArc的第五個參數startAngle中的角度,0°是指坐標系中第四象限中與x重合的角度,順時針方向代表角度增大的方向,如下圖中紅色線條所示。
繪製Bitmap
在Canvas中提供了drawBitmap方法,此方法可以讓我們直接獲取一張圖片繪製到畫布上,有了它可以讓我們的自定義View錦上添花,同時也讓我們實現一些複雜效果有了一個更加方便的途徑。下麵是drawBitmap的幾個比較常用的重載方法:
- drawBitmap(Bitmap bitmap, float left, float top, Paint paint)
- bitmap:Bitmap資源文件
- left和top:代表了圖片左上角落入的位置坐標。
- top:看2
- paint:畫筆
- drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
- src:在Bitmap圖片上截取一部分作為繪製源,可null
- det:將繪製目標拉伸平鋪到det指定的矩形中
drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
同第二個重載方法,幾乎一毛一樣。drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
- matrix:Matrix的參數傳入是的drawBitmap功能變得異常強大,讓此方法有意思了許多,通過matrix可以實現圖片的平移(postTranslate())、縮放(postScale())、旋轉(postRotate())、錯切(postSkew())等等花式炫酷效果,由於Matrix的用法稍微多一些,篇幅限制,這裡就先一帶而過了,感興趣的朋友可以自行探索。
在onDraw方法中drawBitmap的以上重載方法,註意在使用完Bitmap之後記得用Bitmap.recycle()來回收掉資源,以防止oom。
/** drawBitmap */
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), android.R.mipmap.sym_def_app_icon);
// 繪製圖片
canvas.drawBitmap(bitmap, 0, 300, null);
// 將圖片拉伸平鋪在RectF矩形內
canvas.drawBitmap(bitmap, null, new RectF(200, 300, 500, 500), null);
// 截取圖片的四分之一拉伸平鋪在RectF矩形內
canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2), new RectF(500, 300, 800, 500), null);
Matrix matrix = new Matrix();
matrix.postTranslate(800, 300); // 將bitmap平移到此位置
canvas.drawBitmap(bitmap, matrix, mPaint);
// 為防止oom,及時回收bitmap
bitmap.recycle();
效果如下圖(紅框內)。
繪製圓形
- drawCircle(float cx, float cy, float radius, Paint paint)
- cx:圓心x坐標
- cy:圓心y坐標
- radius:半徑
canvas.drawCircle(100, 700, 100, mPaint);
效果如下圖:
繪製點
- drawPoint(float x, float y, Paint paint)
- x:點的x坐標
- y:點的y坐標
- drawPoints(float[] pts, Paint paint) 繪製一組點
- pts:float數組,兩位為一組,兩兩結合代表x、y坐標,例如:pts[0]、pts[1]代表第一個點的x、y坐標,pts[2]、pts[3]代表第二個點的x、y坐標,依次類推。
- drawPoints(float[] pts, int offset, int count, Paint paint) 繪製一組點
- pts:float數組,兩位為一組,兩兩結合代表x、y坐標,例如:pts[0]、pts[1]代表第一個點的x、y坐標,pts[2]、pts[3]代表第二個點的x、y坐標,依次類推。
- offset:代表數組開始跳過幾個只開始繪製點,註意這裡不是指數組的下標,而是代表跳過幾個值。
- count:在跳過offset個值後,處理幾個值,註意這裡的count不是代表點的個數,而是代表數組中值的個數。
canvas.drawPoint(100, 700, mPaint); // 繪製一個點
float[] points = new float[] {
130, 700,
160, 700,
190, 700,
210, 700,
240, 700
};
canvas.drawPoints(points, 2, 4, mPaint); // 繪製一組點(代表跳過前兩個值,處理4個值,也就是實際繪製2個點)
效果如下圖:
繪製橢圓
- drawOval(float left, float top, float right, float bottom, Paint paint)
- left
- top
- right
- bottom
在left、top、right、bottom圍成的區域內繪製一個橢圓。
- drawOval(RectF oval, Paint paint)
- 將第一個重載方法的left、top、right、bottom封裝到RectF類中,與扇形的重載方法異曲同工。
RectF rectF2 = new RectF(300, 600, 700, 800); // 創建一個RectF
canvas.drawOval(rectF2, mPaint);
效果如下圖:
繪製矩形
- drawRect(float left, float top, float right, float bottom, Paint paint)
- drawRect(Rect r, Paint paint)
- drawRect(RectF rect, Paint paint)
drawRect的參數非常好理解,這裡就不啰嗦了,直接上代碼看效果:
canvas.drawRect(rectF2, mPaint);
註:這裡的rectF2即上文繪製橢圓時創建的RectF對象。
繪製圓角矩形
- drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, Paint paint)
- drawRoundRect(RectF rect, float rx, float ry, Paint paint)
drawRoundRect是繪製圓角矩形,用法和drawRect類似,唯一不同的是多了兩個參數:
- rx:x軸方向的圓角弧度
- ry:y軸方向的圓角弧度
上代碼,看效果:
canvas.drawRoundRect(rectF2, 60, 30, mPaint);
這裡為了突出兩個方向的圓角弧度,特地將rx和ry設置差距比較大,效果如下圖:
繪製直線
- drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
- drawLines(float[] pts, int offset, int count, Paint paint)
- drawLines(float[] pts, Paint paint)
drawLine和drawLines一個是繪製一個點,一個是繪製一組點,其中drawLines中的float數組中四個值為一組點,其用法可以參照drawPoints。
canvas.drawLine(100, 820, 800, 820, mPaint);
float[] lines = new float[]{
100f, 850f, 800f, 850f,
100f, 900f, 800f, 900f,
100f, 950f, 800f, 950f
};
canvas.drawLines(lines, mPaint); // 按floats數組中,四個數為1組,繪製多條線
效果如下圖:
drawPath() 繪製不規則圖形
上面的這些Canvas方法固然已經很強大了,但是我們如果想要繪製一些不規則的圖形怎麼辦,這時候就要用到強大的drawPath()方法了,通過對Path進行設置不同的坐標、添加不同圖形,最後傳入drawPath方法中可以繪製出複雜的且不規則的形狀。以下是drawPath的方法及參數:
- drawPath(Path path, Paint paint)
這裡的關鍵參數就是Path,Path類的方法較多,大部分用法類似,這裡挑幾個說一下:
- Path類
- addArc(RectF oval, float startAngle, float sweepAngle) 往path裡面添加一個圓弧
- addCircle(float x, float y, float radius, Path.Direction dir) 添加一個圓形
- addOval(RectF oval, Path.Direction dir) 添加一個橢圓
- addRect(RectF rect, Path.Direction dir) 添加一個矩形
- lineTo(float x, float y) 連線到坐標(x,y)
- moveTo(float x, float y) 將path繪製點移動到坐標(x,y)
- close() 用直線閉合圖形,調用此方法後,path會將最後一處點與起始用直線連接起來,path起始點為moveTo()方法的坐標上,如果沒有調用moveTo()起始點將預設為(0,0)坐標。
- ...
接下來使用drawPath繪製一個樓梯:
// 使用 Path 繪製一個樓梯
Path path = new Path();
path.lineTo(0, 1000);
path.lineTo(100, 1000);
path.lineTo(100, 1100);
path.lineTo(200, 1100);
path.lineTo(200, 1200);
path.lineTo(300, 1200);
path.lineTo(300, 1300);
path.lineTo(400, 1300);
path.lineTo(400, 1400);
path.lineTo(0, 1400);
path.lineTo(0, 1000);
path.close();
canvas.drawPath(path, mPaint);
效果如下圖:
再用drawPath方法繪製一個Android小機器人:
/ 使用 Path 繪製一個Android機器人
// 繪製兩個觸角
path.reset();
path.moveTo(625, 1050);
path.lineTo(650, 1120);
path.moveTo(775, 1050);
path.lineTo(750, 1120);
path.addArc(new RectF(600, 1100, 800, 1300), 180, 180); // 繪製頭部
path.addCircle(666.66f, 1150, 10, Path.Direction.CW); // 繪製眼睛,CW:順時針繪製, CCW:逆時針繪製
path.addCircle(733.33f, 1150, 10, Path.Direction.CW);
path.addRect(new RectF(600, 1200, 800, 1300), Path.Direction.CW); // 身體
canvas.drawPath(path, mPaint);
效果圖如下:
最後,上文中Canvas示例的全部代碼如下:
public class StudyView extends View {
private Paint mPaint;
private Context mContext;
public StudyView(Context context) {
super(context);
init(context);
}
public StudyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mContext = context;
mPaint = new Paint();
mPaint.setAntiAlias(true); // 消除鋸齒
mPaint.setStrokeWidth(5); // 設置筆尖寬度
mPaint.setStyle(Paint.Style.STROKE); // 不填充
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/** 1、drawArc */
// 繪製扇形
canvas.drawArc(0, 0, 200, 200, 0, 90, true, mPaint);
RectF rectF = new RectF(0, 0, 200, 200);
canvas.drawArc(rectF, 180, 90, true, mPaint);
// 繪製圓弧
canvas.drawArc(300, 0, 500, 200, 0, 90, false, mPaint);
RectF rectF1 = new RectF(300, 0, 500, 200);
canvas.drawArc(rectF1, 180, 90, false, mPaint);
/** 2、drawBitmap */
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), android.R.mipmap.sym_def_app_icon);
// 繪製圖片
canvas.drawBitmap(bitmap, 0, 300, null);
// 將圖片拉伸平鋪在RectF矩形內
canvas.drawBitmap(bitmap, null, new RectF(200, 300, 500, 500), null);
// 截取圖片的四分之一拉伸平鋪在RectF矩形內
canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2), new RectF(500, 300, 800, 500), null);
Matrix matrix = new Matrix();
matrix.postTranslate(800, 300); // 將bitmap平移到此位置
canvas.drawBitmap(bitmap, matrix, mPaint);
// 為防止oom,及時回收bitmap
bitmap.recycle();
/** 3、drawCircle */
canvas.drawCircle(100, 700, 100, mPaint);
/** 4、繪製一個點 */
canvas.drawPoint(100, 700, mPaint); // 繪製一個點
float[] points = new float[] {
130, 700,
160, 700,
190, 700,
210, 700,
240, 700
};
canvas.drawPoints(points, 2, 4, mPaint); // 繪製一組點(代表跳過前兩個值,處理4個值,也就是實際繪製2個點)
RectF rectF2 = new RectF(300, 600, 700, 800); // 創建一個RectF
/** 5、drawOval 繪製橢圓 */
canvas.drawOval(rectF2, mPaint);
/** 6、drawRect 繪製矩形*/
canvas.drawRect(rectF2, mPaint);
canvas.drawRoundRect(rectF2, 60, 30, mPaint);
/** 7、drawLine */
canvas.drawLine(100, 820, 800, 820, mPaint);
float[] lines = new float[]{
100f, 850f, 800f, 850f,
100f, 900f, 800f, 900f,
100f, 950f, 800f, 950f
};
canvas.drawLines(lines, mPaint); // 按floats數組中,四個數為1組,繪製多條線
/** 8、drawPath */
// 使用 Path 繪製一個樓梯
Path path = new Path();
path.moveTo(0, 1000);
path.lineTo(100, 1000);
path.lineTo(100, 1100);
path.lineTo(200, 1100);
path.lineTo(200, 1200);
path.lineTo(300, 1200);
path.lineTo(300, 1300);
path.lineTo(400, 1300);
path.lineTo(400, 1400);
path.close();
canvas.drawPath(path, mPaint);
// 使用 Path 繪製一個Android機器人
// 繪製兩個觸角
path.reset();
path.moveTo(625, 1050);
path.lineTo(650, 1120);
path.moveTo(775, 1050);
path.lineTo(750, 1120);
path.addArc(new RectF(600, 1100, 800, 1300), 180, 180); // 繪製頭部
path.addCircle(666.66f, 1150, 10, Path.Direction.CW); // 繪製眼睛,CW:順時針繪製, CCW:逆時針繪製
path.addCircle(733.33f, 1150, 10, Path.Direction.CW);
path.addRect(new RectF(600, 1200, 800, 1300), Path.Direction.CW); // 身體
canvas.drawPath(path, mPaint);
}
}
完整效果圖如下:
其實Canvas除了可以繪製圖形之外,還可以繪製文字,Canvas的繪製文字的方法有drawText()、drawTextOnPath()、drawTextRun()等方法,在繪製文字是和Paint的結合更為緊密,所以講繪製文字的方法放在下文和Paint一起講可能效果會更好一些,好了,廢話不多說了,接下來咱們就開始Paint的篇章。
Paint
為了更為清晰的講解Paint的用法,先來新建一個自定義類,暫叫PaintStudyView,接下來創建一個它的大體骨架,在此類中定義了一些變數,變數的意義請見註釋:
public class PaintStudyView extends View {
private Paint mTextPaint; // 繪製文字的Paint
private Paint mPointPaint; // 繪製參考點的Paint
private Context mContext;
private final static float Y_SPACE = 100; // y軸方向的間距
public PaintStudyView(Context context) {
super(context);
init(context);
}
public PaintStudyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mContext = context;
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true); // 消除鋸齒
mTextPaint.setStrokeWidth(1); // 設置筆尖寬度
mTextPaint.setStyle(Paint.Style.FILL); // 填充
mTextPaint.setTextSize(30);
mPointPaint = new Paint();
mPointPaint.setAntiAlias(true);
mPointPaint.setStrokeWidth(5);
mPointPaint.setColor(Color.RED); // 將參考點的Paint設置為紅色
mPointPaint.setStyle(Paint.Style.STROKE);// 不填充
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}
Canvas的繪製文字的相關方法:
drawText()的重載方法
drawText() 是Canvas的繪製文字中的最長用的方法,它只能按照從左至右的普通方式來繪製文字。
- drawText(String text, float x, float y, Paint paint)
- text:待繪製的文字內容
- x:文字繪製位置的x坐標
- y:文字繪製位置的y坐標
- paint:Paint畫筆,可以通過Paint.setTextAlign()來決定文字的方位,有:Paint.Align.LEFT(居左),Paint.Align.RIGHT(居右),Paint.Align.CENTER(居中)三個位置。
- drawText(String text, int start, int end, float x, float y, Paint paint)
- start:代表從text中的第幾個字元開始截取繪製,包含第start個字元。
- end:代表截取到text的第幾個字元,不包含第end個字元。
例如:我是一個自定義View的控制項,start=1,end=6,截取後為:是一個自定
下麵兩個重載方法可以參考第二個很容易就能理解:
- drawText(CharSequence text, int start, int end, float x, float y, Paint paint)
- drawText(char[] text, int index, int count, float x, float y, Paint paint)
以下示例說明瞭文字的不同位置,同時也說明瞭第二個和第四個重載方法對字元串截取時的用法:
String str = "我是一個自定義View的控制項";// 待繪製文字
float x = getWidth() / 2;
float y = 100;
canvas.drawPoint(x, y, mPointPaint); // 繪製參考點,便於觀察文字處於x,y坐標的位置,從而來學習setTextAlign()方法
mTextPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText(str, x, y, mTextPaint);
y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
mTextPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText(str, 0, 6, x, y, mTextPaint);
y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(str.toCharArray(), 1, 6, x, y, mTextPaint);
效果圖如下:
其中的紅點為額外添加的參考坐標,目的是為了突出setTextAlign中參數的位置。
drawTextOnPath()的重載方法
drawTextOnPath() 由方法名字我們就可以看出來他可以按照Path的走向來繪製文字,例如我們在path中傳入一個圓弧,那麼繪製出來的文字走向就是圓弧狀的,是不是很酷,來看一下它的重載方法:
- drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
- text:同drawText的第一個參數。
- path:Path參數,用法在前文已經說過了。
- hOffset:水平方向的偏移量。
- vOffset:垂直方向的偏移量。
關鍵點:有一點一定要提的就是,這裡的hOffset是相對於path路徑的水平偏移量,而vOffset也是相對於path路徑的垂直偏移量,這麼說可能還有點不清楚,結合下麵的示例來說明,請仔細體會這裡的意思:
// 1、下開口圓弧方向繪製文字
mTextPaint.setTextAlign(Paint.Align.LEFT);
y += Y_SPACE;
Path path = new Path();
path.addArc(new RectF(x - 150, y, x + 150, y + 300), 180,180);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint); // 按照path路徑繪製文字,不偏移
canvas.drawTextOnPath(str, path, 30, 30, mTextPaint);// 向水平、垂直方向各偏移30
canvas.drawTextOnPath(str, path, 60, 60, mTextPaint);// 向水平、垂直方向各偏移60
// 2、上開口圓弧方向繪製文字
path.reset();
y += Y_SPACE;
path.addArc(new RectF(x - 150, y, x + 150, y + 300), 0, 180);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint);
canvas.drawTextOnPath(str, path, 30, 30, mTextPaint);
canvas.drawTextOnPath(str, path, 60, 60, mTextPaint);
path.close();
// 3、豎直方向繪製文字
path.reset();
path.moveTo(200, y);
path.lineTo(200, y + 4 * Y_SPACE);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint);
canvas.drawTextOnPath(str, path, 30, 60, mTextPaint);
y += Y_SPACE;
y += Y_SPACE;
y += Y_SPACE;
y += Y_SPACE;
// 4、水平方向繪製文字
path.reset();
path.moveTo(x, y);
path.lineTo(x + 4 * Y_SPACE, y);
canvas.drawPath(path, mPointPaint); // 參考弧度線
canvas.drawTextOnPath(str, path, 0, 0, mTextPaint);
canvas.drawTextOnPath(str, path, 30, 60, mTextPaint);
如下是效果圖,註意看圖片中的紅色部分,紅色的線是用代碼繪製出來的path參考線,紅色的箭頭是path的水平和垂直方向的走向,結合下圖可以更好的理解drawTextOnPath的hOffset和vOffset參數。
- drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)
這個方法的套路想必不用解釋了。
drawTextRun()的重載方法
- drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount, float x, float y, boolean isRtl, Paint paint)
- drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint paint)
drawTextRun()可以文字的是從左到右還是從右到左的順序來繪製,其中倒數第二個參數isRtl就是用來控制方向的,true就是倒序繪製,false就是正序繪製,其他的參數就沒啥好說的了,這個方法用法比較簡單,這裡就不貼代碼了。另外這個方法是在API 23才開始添加的,使用時要註意。
到目前為止,Canvas的常用用法基本介紹完了,接下來就可以著重來看Paint的使用了,Paint和Canvas兩者是不可分離的,兩者協作,相輔相成。所以在下麵的用法示例中不免要用到Canvas的相關方法。
使用Paint測量文字的尺寸,定位文字
我們在開發自定義控制項時,免不了要精確定位文字的文字,例如必須把文字放在某個區域的正中間,或者必須讓一行文字的幾何中心精確的處於某個點上,這時我們如果不懂這裡的竅門可能就要盲目的試位置了,這樣一點一點試出來的位置很不可靠,可能換個屏幕尺寸位置就不對了,接下來怎麼來看看怎麼樣用最優雅的姿勢來精確的定位文字。
其實在水平方向的定位還比較好說,直接使用Paint.setTextAlign()就能搞定大多需求,主要是在水平方向上稍稍複雜一點,想要定位位置,首先需要先獲取文字的高度,要用到Paint的以下兩個方法:
- float ascent():根據文字大小獲取文字頂端到文字基線的距離(返回的是負值)
- float descent():根據文字大小獲取文字底部到文字基線的距離(返回的事正值)
有了這兩個方法那就非常好辦了,首先用代碼結合效果圖說明一下基線、ascent、descent和文字的關係:
y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
canvas.drawLine(x - 300, y, x+300, y, mPointPaint);
mTextPaint.setTextAlign(Paint.Align.CENTER);// 水平方向上讓文字居中
float ascent = mTextPaint.ascent(); // 根據文字大小獲取文字頂端到文字基線的距離(返回的是負值)
float descent = mTextPaint.descent(); // 根據文字大小獲取文字底部到文字基線的距離(返回的事正值)
canvas.drawLine(x - 300, y + ascent, x+300, y + ascent, mPointPaint);
canvas.drawLine(x - 300, y + descent, x+300, y + descent, mPointPaint);
canvas.drawText(str, x, y, mTextPaint);
效果圖如下,它們之間的關係註意看圖片裡面的說明。(註:在這裡感謝園友在截圖中指出的一處錯誤,現已修正)
接下來就讓文字的中心落在參考點上:
// 將文字的中心定位在參考點上
y += Y_SPACE;
canvas.drawPoint(x, y, mPointPaint);
canvas.drawText(str, x, y - ascent / 2 - descent / 2, mTextPaint);
效果圖如下,仔細看參考點(紅點)和文字的位置:
利用Paint.setShader()(著色器)繪製漸變色
使用 setShader() 方法可以添加漸變顏色也可以使用圖片作為背景,其參數是一個Shader類,傳入不同的Shader子類可以實現不同的漸變效果或者添加背景圖片,其子類有一下幾種:
- LinearGradient:線性漸變
- RadialGradient:放射狀漸變
- SweepGradient:掃描漸變
- BitmapShader:添加背景圖片
- ComposeShader:多種Shader組合
上面接個Shader的子類在使用方式上都差不多,這裡只用LinearGradient為例說明一下,並註意對LinearGradient構造器的最後一個參數傳入不同的參數對應的效果圖:
/* Shader 漸變 */
y = 100;
Shader shader = new LinearGradient(x - 50, y - 80, x + 50, y + 80,
Color.parseColor("#FFCCBB"), Color.parseColor("#FF0000"), Shader.TileMode.CLAMP);
mTextPaint.setShader(shader);
canvas.drawRect(x - 500, y - 80, x + 500, y + 80, mTextPaint);
y += 3 * Y_SPACE;
Shader shader1 = new LinearGradient(x - 50, y - 80, x + 50, y + 80,
Color.parseColor("#FFCCBB"), Color.parseColor("#FF0000"), Shader.TileMode.REPEAT);
mTextPaint.setShader(shader1);
canvas.drawRect(x - 500, y - 80, x + 500, y + 80, mTextPaint);
y += 3 * Y_SPACE;
Shader shader2 = new LinearGradient(x - 50, y - 80, x + 50, y + 80,
Color.parseColor("#FFCCBB"), Color.parseColor("#FF0000"), Shader.TileMode.MIRROR);
mTextPaint.setShader(shader2);
canvas.drawRect(x - 500, y - 80, x + 500, y + 80, mTextPaint);
效果圖如下:
除了以上這些,Paint的用法還有很多很多,一時半會也列不完,博主在這也只能拋磚引玉,感興趣的朋友可查看官方API自行探索。
最後想說的是,本系列文章為博主對Android知識進行再次梳理,查缺補漏的學習過程,一方面是對自己遺忘的東西加以複習重新掌握,另一方面相信在重新學習的過程中定會有巨大的新收穫,如果你也有跟我同樣的想法,不妨關註我一起學習,互相探討,共同進步!
參考文獻:
- *Android Developers:https://developer.android.com/index.html*