Android繪圖之Paint的使用方法詳解
前言
大家都知道在Android中,自定義控件是一個非常重要的知識點,而自定義控件對Android開發(fā)者是一個難點,考驗開發(fā)人員對View原理的理解,對于繼承View的自定義控件,一般都需要重寫onDraw方法,而且往往需要開發(fā)人員能夠掌握Paint這個類。
簡介
The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
Paint:該類保存了繪制幾何圖形、文本和位圖的樣式和顏色信息。也就是說我們可以使用Paint保存的樣式和顏色,來繪制圖形、文本和bitmap,這就是Paint的強大之處。接下來我們使用Paint來繪圖,并且看看該類有哪些樣式和顏色。
Paint的使用
使用Paint之前需要初始化
mPaint = new Paint();
設(shè)置筆(Paint)的顏色和alpha值:
mPaint.setColor(Color.BLUE); mPaint.setAlpha(255);
注意:alpha的范圍是[0..255],而不是[0..1],是一個int值。
設(shè)置畫筆的樣式:通過mPaint.setStyle()來設(shè)置樣式。
public enum Style {
/**
* Geometry and text drawn with this style will be filled, ignoring all
* stroke-related settings in the paint.
*/
FILL (0),
/**
* Geometry and text drawn with this style will be stroked, respecting
* the stroke-related fields on the paint.
*/
STROKE (1),
/**
* Geometry and text drawn with this style will be both filled and
* stroked at the same time, respecting the stroke-related fields on
* the paint. This mode can give unexpected results if the geometry
* is oriented counter-clockwise. This restriction does not apply to
* either FILL or STROKE.
*/
FILL_AND_STROKE (2);
Style(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
總共有三種畫筆的樣式
FILL:填充內(nèi)容;
STROKE:描邊;
FILL_AND_STROKE:填充內(nèi)容并描邊。
設(shè)置畫筆的寬度
mPaint.setStrokeWidth(50);
設(shè)置畫筆的線帽
通過mPaint.setStrokeCap來設(shè)置線帽,總共有三種線帽
/**
* The Cap specifies the treatment for the beginning and ending of
* stroked lines and paths. The default is BUTT.
*/
public enum Cap {
/**
* The stroke ends with the path, and does not project beyond it.
*/
BUTT (0),
/**
* The stroke projects out as a semicircle, with the center at the
* end of the path.
*/
ROUND (1),
/**
* The stroke projects out as a square, with the center at the end
* of the path.
*/
SQUARE (2);
private Cap(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
BUTT:沒有線帽,默認模式
ROUND:圓形
SQUARE:方形
三種線帽對比:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.BLUE);
mPaint.setAlpha(255);
//設(shè)置畫筆的樣式
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
//畫筆的寬度
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直線
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(300, 100);
canvas.drawPath(path, mPaint);
mPaint.reset();//重置
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.ROUND);//圓形
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直線
Path path1 = new Path();
path1.moveTo(100, 200);
path1.lineTo(300, 200);
canvas.drawPath(path1, mPaint);
mPaint.reset();//重置
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.BUTT);//沒有
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直線
Path path2 = new Path();
path2.moveTo(100, 300);
path2.lineTo(300, 300);
canvas.drawPath(path2, mPaint);
}
上面代碼中有個重置畫筆,這時候需要重新設(shè)置畫筆。

線帽對比
設(shè)置Join
使用setStrokeJoin方法來設(shè)置Join,Join有三種類型:
BEVEL:直線
ROUND:圓角
MITER:銳角
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.BLUE);
mPaint.setAlpha(255);
mPaint.setStyle(Paint.Style.STROKE);//設(shè)置畫筆的樣式
mPaint.setStrokeWidth(50);//畫筆的寬度
mPaint.setStrokeCap(Paint.Cap.BUTT);//線帽
mPaint.setStrokeJoin(Paint.Join.BEVEL);
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(300, 100);
path.lineTo(100, 300);
path.close();
canvas.drawPath(path, mPaint);
mPaint.reset();//重置
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.BUTT);//圓形
mPaint.setStrokeJoin(Paint.Join.ROUND);//圓弧
Path path1 = new Path();
path1.moveTo(100, 400);
path1.lineTo(300, 400);
path1.lineTo(100, 700);
path1.close();
canvas.drawPath(path1, mPaint);
mPaint.reset();//重置
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.BUTT);//沒有
mPaint.setStrokeJoin(Paint.Join.MITER);//銳角
Path path2 = new Path();
path2.moveTo(100, 800);
path2.lineTo(300, 800);
path2.lineTo(100, 1100);
path2.close();
canvas.drawPath(path2, mPaint);
}

Join對比
以上就是Join三種類型對比。
設(shè)置防鋸齒
mPaint.setAntiAlias(true);
如果設(shè)置防鋸齒,會損失一定的性能
抖動處理
使用mPaint.setDither()方法,設(shè)置是否使用圖像抖動處理。會使繪制的圖片等顏色更加的清晰以及飽滿,也是損失性能。
使用Path繪制圖形

Path繪制圖形
點組成線,線組成面,這樣Path可以繪制各種各樣的圖形,可以說是無所不能的了,但是Path也提供了很多方法,來繪制圖形。
文本繪制
上文中,介紹了Paint畫筆,和繪制了一些圖形。但是介紹Paint的時候,我們知道它可以繪制圖形,文本和bitmap,所以Paint是非常強大的了,我們看下Paint是如何繪制文本的。
設(shè)置字符之間的間距
setLetterSpacing
設(shè)置文本刪除線
mPaint.setStrikeThruText(true);
是否設(shè)置下劃線
mPaint.setUnderlineText(true);
設(shè)置文本大小
mPaint.setTextSize(textSize);
設(shè)置字體類型
mPaint.setTypeface(Typeface.BOLD); // Style public static final int NORMAL = 0;//常規(guī) public static final int BOLD = 1;//粗體 public static final int ITALIC = 2; //斜體 public static final int BOLD_ITALIC = 3;//粗斜體
字體類型有以上四種類型可以設(shè)置。
加載自定義字體
Typeface.create(familyName, style)
文字傾斜
mPaint.setTextSkewX(-0.25f);
文字傾斜默認為0,官方推薦的-0.25f是斜體
文本對齊方式
mPaint.setTextAlign(Align.LEFT)
有三種:
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;
}
計算制定長度的字符串
int breadText = mPaint.breakText(text, measureForwards, maxWidth, measuredWidth)
注意:字符長度、字符個數(shù)、顯示的時候是真實的長度
Rect bounds獲取文本的矩形區(qū)域(寬高) mPaint.getTextBounds(text, index, count, bounds) mPaint.getTextBounds(text, start, end, bounds) //獲取文本的寬度,和上面類似,但是是一個比較粗略的結(jié)果 float measureText = mPaint.measureText(str); //獲取文本的寬度,和上面類似,但是是比較精準的。 float[] measuredWidth = new float[10]; //measuredWidth得到每一個字符的寬度;textWidths字符數(shù) int textWidths = mPaint.getTextWidths(str, measuredWidth); mPaint.getTextWidths(text, start, end, widths)
使用drawText繪制文本
public class PaintView extends View {
private Paint mPaint;
private String text = "你是我世界之光,我心另一半";
public PaintView(Context context) {
this(context,null);
}
public PaintView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public PaintView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mPaint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.BLUE);
mPaint.setStyle(Paint.Style.STROKE);//設(shè)置畫筆的樣式
mPaint.setStrokeCap(Paint.Cap.BUTT);//線帽
mPaint.setStrokeJoin(Paint.Join.BEVEL);
int top = 100;
int baselineX = 0;
mPaint.setTextSize(50);
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawLine(0, top, 2000, top, mPaint);
//文本Metrics
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float baselineY = top - fontMetrics.top;
canvas.drawText(text, baselineX, baselineY, mPaint);
}
}

繪制文本
繪制文本時,還有一個很重要的知識點就是基線的確定
DrawText 基線的確定
在自定義控件的時候,有時候會用到DrawText 方法.
先把自定義TextView的貼出來
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int x = getPaddingLeft();
//dy 代表的是:高度的一半到 baseLine的距離
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
// top 是一個負值 bottom 是一個正值 top,bttom的值代表是 bottom是baseLine到文字底部的距離(正值)
// 必須要清楚的,可以自己打印就好
int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
int baseLine = getHeight()/2 + dy;
canvas.drawText(costom_text,x,baseLine,paint);
}
/**
* Draw the text, with origin at (x,y), using the specified paint. The
* origin is interpreted based on the Align setting in the paint.
*
* @param text The text to be drawn
* @param x The x-coordinate of the origin of the text being drawn
* @param y The y-coordinate of the baseline of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
paint.getNativeInstance(), paint.mNativeTypeface);
}
x,y 分別表示 基線的開始坐標,并不是 文字左上角的坐標,因為文字的繪制是以基線為基礎(chǔ)的

圖中的 五角星 所在的線 就是基線 BaseLine,那么如何確定基線的x,y坐標呢?
首寫我們先確定一下x坐標 :int x = getPaddingLeft(); 也就是文字距左邊的距離
y坐標:
1、我們先計算一下文字高度的一半到 baseLine的距離。
int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
2、之后我們再使用控件高度的一般,加上文字高度的一半到 baseLine的距離,就是基線的y坐標
int baseLine = getHeight()/2 + dy;
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Android應(yīng)用中實現(xiàn)手勢控制圖片縮放的完全攻略
這篇文章主要介紹了Android應(yīng)用中實現(xiàn)手勢控制圖片縮放的完全攻略,采用了Matrix矩陣的方法,實例講解了包括觸摸點設(shè)置與各種沖突的處理等方面,相當(dāng)全面,需要的朋友可以參考下2016-04-04
Android開發(fā)之使用GridView展示圖片的方法
這篇文章主要介紹了Android開發(fā)之使用GridView展示圖片的方法,涉及Android使用GridView操作圖片的布局與圖片調(diào)用功能實現(xiàn)技巧,需要的朋友可以參考下2016-01-01
Android 組合控件實現(xiàn)布局的復(fù)用的方法
本篇文章主要介紹了Android 組合控件實現(xiàn)布局的復(fù)用的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
Android開發(fā)之AlarmManager的用法詳解
這篇文章主要介紹了Android開發(fā)之AlarmManager的用法,是Android應(yīng)用開發(fā)中非常實用的技能,需要的朋友可以參考下2014-07-07
Android?ViewPager實現(xiàn)左右滑動翻頁效果
這篇文章主要為大家詳細介紹了Android?ViewPager實現(xiàn)左右滑動翻頁效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12
Android實現(xiàn)授權(quán)訪問網(wǎng)頁的方法
這篇文章主要介紹了Android實現(xiàn)授權(quán)訪問網(wǎng)頁的方法,需要的朋友可以參考下2014-07-07

