Android 自定義View學(xué)習(xí)(二)——開始了解Canvas和Paint

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就是畫筆辜窑。CanvasPatint有各種各樣的屬性。本篇先學(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è)必要的元素:

  1. 保存像素的Bitmap
  2. 管理繪制請求的Canvas
  3. 繪畫的原始基本元素,例如矩形职恳,線所禀,文字,Bitmap
  4. 擁有顏色和風(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>
繪制矩形

MeausureViewwidth = right - left
MeausureViewheight = bottom - top

注意drawRect(left,top,right,bottom,paint)的參數(shù)順序辙培。


  • drawRect(@NonNull Rect r, @NonNull Paint paint)
  • drawRect(@NonNull RectF r, @NonNull Paint paint)

兩個(gè)方法的差別在于RectRectF的差別。

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)為integerRectF 坐標(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() 繪制扇形

  1. drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint)
  2. 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 沒有

扇形尾抑,有焦點(diǎn)圓心
@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 useCentertrue


當(dāng)把boolean useCenter設(shè)置為false時(shí)

扇形無焦點(diǎn)圓形

此時(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)

繪制bitmap
@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ù)lefttop確定繪制的位置,此時(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)

baseline

繪制文字
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.最后

這篇了解部分CanvasPaint部分基礎(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ì)注意

共勉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末燎悍,一起剝皮案震驚了整個(gè)濱河市敬惦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谈山,老刑警劉巖俄删,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勾哩,居然都是意外死亡抗蠢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門思劳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迅矛,“玉大人,你說我怎么就攤上這事潜叛』喟” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵威兜,是天一觀的道長销斟。 經(jīng)常有香客問我,道長椒舵,這世上最難降的妖魔是什么蚂踊? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮笔宿,結(jié)果婚禮上犁钟,老公的妹妹穿的比我還像新娘。我一直安慰自己泼橘,他們只是感情好涝动,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著炬灭,像睡著了一般醋粟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天米愿,我揣著相機(jī)與錄音厦凤,去河邊找鬼。 笑死吗货,一個(gè)胖子當(dāng)著我的面吹牛泳唠,可吹牛的內(nèi)容都是我干的狈网。 我是一名探鬼主播宙搬,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拓哺!你這毒婦竟也來了勇垛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤士鸥,失蹤者是張志新(化名)和其女友劉穎闲孤,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烤礁,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讼积,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脚仔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片勤众。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鲤脏,靈堂內(nèi)的尸體忽然破棺而出们颜,到底是詐尸還是另有隱情,我是刑警寧澤猎醇,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布窥突,位于F島的核電站,受9級(jí)特大地震影響硫嘶,放射性物質(zhì)發(fā)生泄漏阻问。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一沦疾、第九天 我趴在偏房一處隱蔽的房頂上張望称近。 院中可真熱鬧,春花似錦曹鸠、人聲如沸煌茬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坛善。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間眠屎,已是汗流浹背剔交。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留改衩,地道東北人岖常。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像葫督,于是被迫代替她去往敵國和親竭鞍。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容