View
的三大流程:測量,布局,繪制
上篇Android自定義View學習(一)——準備簡單介紹了部分測量的知識,后面會繼續學習測量的知識。本篇記錄下繪制onDraw()
方法的學習,只是開始。
1.View的繪制
完成了View
的測量后,根據拿到的View
的大小,位置,重寫onDraw(Canvas canvas)
就可以進行繪制。
現實中,如果想要畫一幅畫,必須要有畫筆和畫布。Canvas
就是畫布,Paint
就是畫筆。Canvas
和Patint
有各種各樣的屬性。本篇先學習部分常用的基礎的屬性,一些可以高度化定制的屬性后續再進行學習。
2.Canvas
源碼中關于Canvas
的解釋:
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
想要畫出一個View
就必須要有4個必要的元素:
- 保存像素的Bitmap
- 管理繪制請求的Canvas
- 繪畫的原始基本元素,例如矩形,線,文字,Bitmap
- 擁有顏色和風格信息的畫筆
翻譯水平,32級 : )
Canvas
有兩種常見創建方法:
-
Canvas canvas = new Canvas()
空參構造方法 -
Canvas canvas = new Canvas(bitmap)
創建一個裝載畫布。構造方法中傳入的bitmap
存儲所有繪制在canvas
的信息。
常用的幾個繪制方法
方法 | 作用 |
---|---|
drawRect() |
畫矩形 |
drawCircle() |
畫圓 |
drawArc() |
畫圓弧 |
drawRoundRect() |
畫圓角矩形 |
drawBitmap() |
畫一個Bitmap |
drawOval |
畫橢圓 |
drawText() |
畫文字 |
Canvas
的方法有很多,這里先記錄幾個簡單的繪制方法,其他的后續學習再做補充。
2.1 drawRect() 繪制矩形
drawRect()
有三種重載方法:
drawRect(float left, float top, float right, float bottom, @NonNull Paint paint)
Draw the specified Rect using the specified paint. The rectangle will be filled or framed based on the Style in the paint.
@param left The left side of the rectangle to be drawn
@param top The top side of the rectangle to be drawn
@param right The right side of the rectangle to be drawn
@param bottom The bottom side of the rectangle to be drawn
@param paint The paint used to draw the rect
MeausreView
代碼,主要繪制就是onDraw()
方法:
public class MeasureView extends View {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public MeasureView(Context context) {
super(context);
initPaint();
}
public MeasureView(Context context, AttributeSet attrs) {
super(context, attrs);
initPaint();
}
public MeasureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
private void initPaint() {
paint.setColor(Color.parseColor("#FF4081"));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float left = getLeft();
float right = getRight();
float top = getTop();
float bottom = getBottom();
canvas.drawRect(left,top,right,bottom,paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec), measuredHeight(heightMeasureSpec));
}
/**
* 測量寬
*
* @param widthMeasureSpec
*/
private int measureWidth(int widthMeasureSpec) {
int result;
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
/**
* 測量高
*
* @param heightMeasureSpec
*/
private int measuredHeight(int heightMeasureSpec) {
int result;
int specMode = MeasureSpec.getMode(heightMeasureSpec);
int specSize = MeasureSpec.getSize(heightMeasureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = 200;
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
}
在Activity
的布局文件中:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.szlk.customview.custom.MeasureView
android:id="@+id/mv_custom_activity"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@color/colorPrimary" />
</LinearLayout>
MeausureView
的width = right - left
MeausureView
的height = bottom - top
注意drawRect(left,top,right,bottom,paint)
的參數順序。
drawRect(@NonNull Rect r, @NonNull Paint paint)
drawRect(@NonNull RectF r, @NonNull Paint paint)
兩個方法的差別在于Rect
和RectF
的差別。
Rect
Rect holds four integer coordinates for a rectangle. The rectangle is
represented by the coordinates of its 4 edges (left, top, right bottom).
These fields can be accessed directly. Use width() and height() to retrieve
the rectangle's width and height. Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
RectF
RectF holds four float coordinates for a rectangle. The rectangle is represented by the coordinates of its 4 edges (left, top, right bottom). These fields can be accessed directly. Use width() and height() to retrieve
the rectangle's width and height. Note: most methods do not check to see that the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
兩者差別就是:Rect
坐標為integer
而RectF
坐標為float
使用:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect rect = new RectF(100,100,200,200);
canvas.drawRect(rect,paint);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect = new RectF(100.5f,100.5f,200.5f,200.5f);
canvas.drawRect(rect,paint);
}
注意構造方法中的參數順序
2.2 drawCricle() 繪制圓形
drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
Draw the specified circle using the specified paint. If radius is <= 0, then nothing will be drawn. The circle will be filled or framed based on the Style in the paint.
@param cx The x-coordinate of the center of the cirle to be drawn
@param cy The y-coordinate of the center of the cirle to be drawn
@param radius The radius of the cirle to be drawn
@param paint The paint used to draw the circle
radius
: 半徑
cx
: 圓心的x
坐標
cy
: 圓心的y
坐標
使用的時候需要考慮圓心和半徑
使用:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = getWidth();
float height = getHeight();
float radius = Math.min(width,height)/2;
canvas.drawCircle(width/2,height/2,radius,paint);
}
繪制圓形時,半徑是寬和高中較小者的二分之一
2.3 drawArc() 繪制扇形
drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint)
drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)
兩個方法的差別:
- 方法2把坐標封裝進
RectF
對象中 - 方法1要求系統最低為21
drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint)
@param oval The bounds of oval used to define the shape and size of the arc
@param startAngle Starting angle (in degrees) where the arc begins
@param sweepAngle Sweep angle (in degrees) measured clockwise
@param useCenter If true, include the center of the oval in the arc, and close it if it is being stroked. This will draw a wedge
@param paint The paint used to draw the arc
-
float startAngle
開始繪制的角度 -
float sweepAngle
扇形掃過的角度,并不是停止時的角度。停止角度 =startAngle
+sweepAngle
-
boolean useCenter
ture就是有焦點圓心 , false 沒有
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
RectF rect = new RectF(0f,0f,500f,500f);
canvas.drawArc(rect,0,60,true,paint);
canvas.drawArc(rect,60,30,true,paint_2);
}
此時的boolean useCenter
為true
當把boolean useCenter
設置為false
時
此時之畫出了開始點和結束點兩點之間的區域
2.4 drawBitmap() 繪制Bitmap
drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint)
@param bitmap The bitmap to be drawn
@param left The position of the left side of the bitmap being drawn
@param top The position of the top side of the bitmap being drawn
@param paint The paint used to draw the bitmap (may be null)
-
left
左上角橫坐標 -
top
左上角縱坐標
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
float width = (getWidth()-bitmap.getWidth())/2;
float height = (getHeight()-bitmap.getHeight())/2;
canvas.drawBitmap(bitmap,width,height,paint);
}
根據left
和top
確定繪制的位置,此時Paint
的用于繪制文字的屬性設置在繪制Bitmap
時是無效的。
2.5 drawText()繪制文字
drawText(@NonNull String text, float x, float y, @NonNull Paint 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)
private void initPaint() {
paint.setColor(Color.parseColor("#FF4081"));
paint.setTextSize(90f);
}
/**
* 繪制文字
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText("HelloWorld",100,100,paint);
}
繪制文字需要設置Paint
的屬性。
2.6 drawPath() 繪制路徑
drawPath()
@param path The path to be drawn
@param paint The paint used to draw the path
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Path p = new Path();
p.moveTo(100, 100);
p.lineTo(200, 50);
p.lineTo(300, 100);
p.lineTo(200,400);
canvas.drawPath(p,paint);
}
-
moveTo()
就是繪制的起始點,默認為(0,9) -
lineTo()
連接的點
3.Paint
The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
<p>
畫筆能夠拿到,所要繪制的幾何圖形、文字或者Bitmap的顏色、風格等信息
<p>
畫筆有三種構造方法:
public Paint() { this(0); }
Create a new paint with default settings.
創建一個默認屬性的畫筆
public Paint(int flags) {...}
Create a new paint with the specified flags. Use setFlags() to change these after the paint is created.
<p>
@param flags initial flag bits, as if they were passed via setFlags().
創建一個帶有標記的畫筆。也可以通過setFlags()
去為一個已經創建過的畫筆設置標簽
public Paint(Paint paint) {...}
Create a new paint, initialized with the attributes in the specified paint parameter.
<p>
@param paint Existing paint used to initialized the attributes of the new paint.
通過一個已經配置好信息的畫筆來創建一個新的畫筆
3.1常用屬性方法
- 繪制文字
方法 | 作用 |
---|---|
setColor(@ColorInt int color) |
設置畫筆顏色 |
setStrokeWidth(float width) |
設置畫筆粗細 |
setTextSkewX(float f) |
設置傾斜,負右斜,正為左 |
setARGB(int a,int r,int g,int b) |
設置顏色,a為透明度 |
setTextSize(float textSize) |
設置繪制文字大小 |
setFakeBoldText(boolean fakeBoldText) |
是否粗體 |
setTextAlign(Paint.Align align) |
設置文字對齊方式,LEFT,CENTER,RIGHT |
setUnderlineText(boolean underlineText) |
設置下劃線 |
setStyle(Style style) |
設置畫筆樣式,FILL,STROKE,FILL_AND_STROKE |
setTypeface(Typeface typeface) |
設置Typeface對象,即字體風格,包括粗體,斜體以及襯線體,非襯線體等 |
- 繪制圖像
方法 | 作用 |
---|---|
setDither(boolean dither) |
設置抖動處理 |
setAlpha(int a) |
設置透明度 |
setAntiAlias(boolean aa) |
是否開啟抗鋸齒 |
setFilterBitmap() |
是否開啟優化Bitmap |
setColorFilter(ColorFilter filter) |
設置顏色過濾 |
setMaskFilter(MaskFilter maskfilter) |
設置濾鏡的效果 |
setShader(Shader shader) |
設置圖像漸變效果 |
setSrokeJoin(Paint.Join join) |
設置圖像結合方式 |
setXfermode(Xfermode xfermode) |
設置圖像重疊效果 |
setPathEffect(PathEffect effect) |
設置路徑效果 |
reset() |
恢復默認設置 |
暫時就是先看看,知道有這么個方法。然而方法還有很多 :)
4.最后
這篇了解部分Canvas
和Paint
部分基礎知識。就是調用了方法而已。下篇繼續記錄學習Paint
。
通過這篇的學習,我再去看網絡其他的自定義View
博客,感覺能大概了解所講內容了。不再是一頭的污水。才剛剛開始呢。:)
嗯,本篇有個小坑,不過也不想修改了,這里說一下,onDraw()
方法中,最好不要進行new
對象,會有警告。本篇這里只是學習,也并無大礙。之后會注意
共勉。