View
的三大流程:測量,布局匙握,繪制
上篇Android自定義View學(xué)習(xí)(一)——準(zhǔn)備簡單介紹了部分測量的知識(shí)咆槽,后面會(huì)繼續(xù)學(xué)習(xí)測量的知識(shí)。本篇記錄下繪制onDraw()
方法的學(xué)習(xí)圈纺,只是開始秦忿。
1.View的繪制
完成了View
的測量后,根據(jù)拿到的View
的大小蛾娶,位置灯谣,重寫onDraw(Canvas canvas)
就可以進(jìn)行繪制。
現(xiàn)實(shí)中蛔琅,如果想要畫一幅畫胎许,必須要有畫筆和畫布。Canvas
就是畫布罗售,Paint
就是畫筆辜窑。Canvas
和Patint
有各種各樣的屬性。本篇先學(xué)習(xí)部分常用的基礎(chǔ)的屬性寨躁,一些可以高度化定制的屬性后續(xù)再進(jìn)行學(xué)習(xí)穆碎。
2.Canvas
源碼中關(guān)于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).
想要畫出一個(gè)View
就必須要有4個(gè)必要的元素:
- 保存像素的Bitmap
- 管理繪制請求的Canvas
- 繪畫的原始基本元素,例如矩形职恳,線所禀,文字,Bitmap
- 擁有顏色和風(fēng)格信息的畫筆
翻譯水平放钦,32級(jí) : )
Canvas
有兩種常見創(chuàng)建方法:
-
Canvas canvas = new Canvas()
空參構(gòu)造方法 -
Canvas canvas = new Canvas(bitmap)
創(chuàng)建一個(gè)裝載畫布色徘。構(gòu)造方法中傳入的bitmap
存儲(chǔ)所有繪制在canvas
的信息。
常用的幾個(gè)繪制方法
方法 | 作用 |
---|---|
drawRect() |
畫矩形 |
drawCircle() |
畫圓 |
drawArc() |
畫圓弧 |
drawRoundRect() |
畫圓角矩形 |
drawBitmap() |
畫一個(gè)Bitmap |
drawOval |
畫橢圓 |
drawText() |
畫文字 |
Canvas
的方法有很多最筒,這里先記錄幾個(gè)簡單的繪制方法贺氓,其他的后續(xù)學(xué)習(xí)再做補(bǔ)充。
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)
的參數(shù)順序辙培。
drawRect(@NonNull Rect r, @NonNull Paint paint)
drawRect(@NonNull RectF r, @NonNull Paint paint)
兩個(gè)方法的差別在于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
坐標(biāo)為integer
而RectF
坐標(biāo)為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);
}
注意構(gòu)造方法中的參數(shù)順序
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
坐標(biāo)
cy
: 圓心的y
坐標(biāo)
使用的時(shí)候需要考慮圓心和半徑
使用:
@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);
}
繪制圓形時(shí)邢锯,半徑是寬和高中較小者的二分之一
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)
兩個(gè)方法的差別:
- 方法2把坐標(biāo)封裝進(jìn)
RectF
對象中 - 方法1要求系統(tǒng)最低為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
扇形掃過的角度扬蕊,并不是停止時(shí)的角度。停止角度 =startAngle
+sweepAngle
-
boolean useCenter
ture就是有焦點(diǎn)圓心 丹擎, 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);
}
此時(shí)的boolean useCenter
為true
當(dāng)把boolean useCenter
設(shè)置為false
時(shí)
此時(shí)之畫出了開始點(diǎn)和結(jié)束點(diǎn)兩點(diǎn)之間的區(qū)域
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
左上角橫坐標(biāo) -
top
左上角縱坐標(biāo)
@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);
}
根據(jù)left
和top
確定繪制的位置,此時(shí)Paint
的用于繪制文字的屬性設(shè)置在繪制Bitmap
時(shí)是無效的蒂培。
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);
}
繪制文字需要設(shè)置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()
就是繪制的起始點(diǎn),默認(rèn)為(0,9) -
lineTo()
連接的點(diǎn)
3.Paint
The Paint class holds the style and color information about how to draw geometries, text and bitmaps.
<p>
畫筆能夠拿到护戳,所要繪制的幾何圖形翎冲、文字或者Bitmap的顏色、風(fēng)格等信息
<p>
畫筆有三種構(gòu)造方法:
public Paint() { this(0); }
Create a new paint with default settings.
創(chuàng)建一個(gè)默認(rèn)屬性的畫筆
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().
創(chuàng)建一個(gè)帶有標(biāo)記的畫筆媳荒。也可以通過setFlags()
去為一個(gè)已經(jīng)創(chuàng)建過的畫筆設(shè)置標(biāo)簽
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.
通過一個(gè)已經(jīng)配置好信息的畫筆來創(chuàng)建一個(gè)新的畫筆
3.1常用屬性方法
- 繪制文字
方法 | 作用 |
---|---|
setColor(@ColorInt int color) |
設(shè)置畫筆顏色 |
setStrokeWidth(float width) |
設(shè)置畫筆粗細(xì) |
setTextSkewX(float f) |
設(shè)置傾斜抗悍,負(fù)右斜,正為左 |
setARGB(int a,int r,int g,int b) |
設(shè)置顏色钳枕,a為透明度 |
setTextSize(float textSize) |
設(shè)置繪制文字大小 |
setFakeBoldText(boolean fakeBoldText) |
是否粗體 |
setTextAlign(Paint.Align align) |
設(shè)置文字對齊方式缴渊,LEFT,CENTER鱼炒,RIGHT |
setUnderlineText(boolean underlineText) |
設(shè)置下劃線 |
setStyle(Style style) |
設(shè)置畫筆樣式衔沼,F(xiàn)ILL,STROKE昔瞧,F(xiàn)ILL_AND_STROKE |
setTypeface(Typeface typeface) |
設(shè)置Typeface對象俐巴,即字體風(fēng)格,包括粗體硬爆,斜體以及襯線體欣舵,非襯線體等 |
- 繪制圖像
方法 | 作用 |
---|---|
setDither(boolean dither) |
設(shè)置抖動(dòng)處理 |
setAlpha(int a) |
設(shè)置透明度 |
setAntiAlias(boolean aa) |
是否開啟抗鋸齒 |
setFilterBitmap() |
是否開啟優(yōu)化Bitmap |
setColorFilter(ColorFilter filter) |
設(shè)置顏色過濾 |
setMaskFilter(MaskFilter maskfilter) |
設(shè)置濾鏡的效果 |
setShader(Shader shader) |
設(shè)置圖像漸變效果 |
setSrokeJoin(Paint.Join join) |
設(shè)置圖像結(jié)合方式 |
setXfermode(Xfermode xfermode) |
設(shè)置圖像重疊效果 |
setPathEffect(PathEffect effect) |
設(shè)置路徑效果 |
reset() |
恢復(fù)默認(rèn)設(shè)置 |
暫時(shí)就是先看看,知道有這么個(gè)方法缀磕。然而方法還有很多 :)
4.最后
這篇了解部分Canvas
和Paint
部分基礎(chǔ)知識(shí)缘圈。就是調(diào)用了方法而已。下篇繼續(xù)記錄學(xué)習(xí)Paint
袜蚕。
通過這篇的學(xué)習(xí)糟把,我再去看網(wǎng)絡(luò)其他的自定義View
博客,感覺能大概了解所講內(nèi)容了牲剃。不再是一頭的污水遣疯。才剛剛開始呢。:)
嗯凿傅,本篇有個(gè)小坑缠犀,不過也不想修改了数苫,這里說一下,onDraw()
方法中辨液,最好不要進(jìn)行new
對象虐急,會(huì)有警告。本篇這里只是學(xué)習(xí)滔迈,也并無大礙止吁。之后會(huì)注意
共勉。