Graphics2D API:Canvas操作

Graphics2D API:Paint類昔驱、Canvas類中已經(jīng)介紹了Canvas基本的繪圖方法骤肛,本篇介紹一些基本的畫布操作.

注意:
1窍蓝、畫布操作針對(duì)的是畫布,而不是畫布上的圖形
2吓笙、畫布變換、裁剪影響后續(xù)圖形的繪制絮蒿,對(duì)之前已經(jīng)繪制過(guò)的內(nèi)容沒(méi)有影響

一叁鉴、畫布變換

1幌墓、平移(translate)

畫布平移,畫布的原始狀態(tài)是以左上角為原點(diǎn)常侣,向右是x軸正方向,向下是y軸正方向.


public void translate(float dx, float dy)
平移畫布溯祸,其實(shí)就是平移坐標(biāo)系焦辅,dx、dy分別代表水平氨鹏、豎直方向平移的距離

注意:平移是基于當(dāng)前起點(diǎn)平移
例:最初起點(diǎn)是(0,0),第一次平移translate(100,100)后起點(diǎn)變?yōu)?100,100)
再平移一次translate(200,200)后起點(diǎn)變?yōu)?300,300)

測(cè)試

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.GRAY);

        canvas.translate(100, 100);
        Rect rect = new Rect(0, 0, 100, 100);
        canvas.drawRect(rect, mPaint);

        canvas.translate(100, 100);
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(rect, mPaint);
    }
2跟继、旋轉(zhuǎn)(rotate)

畫布旋轉(zhuǎn)舔糖,默認(rèn)是圍繞坐標(biāo)原點(diǎn)(0莺匠,0)來(lái)旋轉(zhuǎn)的,也可以指定旋轉(zhuǎn)中心


public void rotate(float degrees)
degrees:旋轉(zhuǎn)度數(shù)摇庙,>0順時(shí)針旋轉(zhuǎn)遥缕,<0逆時(shí)針旋轉(zhuǎn)
以默認(rèn)原點(diǎn)(0,0)旋轉(zhuǎn)將畫布旋轉(zhuǎn)degrees度數(shù)

public final void rotate(float degrees, float px, float py)
以(px,py)為旋轉(zhuǎn)中心將畫布旋轉(zhuǎn)degrees度數(shù)

測(cè)試

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mPaint.setColor(Color.RED);

        Rect rectCanvas = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
        Rect rect = new Rect(300, 300, 400, 400);

        canvas.drawRect(rectCanvas, mPaint);//繪制畫布矩形邊框夕凝,方便觀察
        canvas.drawRect(rect, mPaint);

        canvas.rotate(30);//旋轉(zhuǎn)

        mPaint.setColor(Color.BLUE);
        canvas.drawRect(rectCanvas, mPaint);//繪制旋轉(zhuǎn)后畫布矩形邊框户秤,方便觀察
        canvas.drawRect(rect, mPaint);
    }
3鸡号、縮放(scale)

畫布縮放,默認(rèn)以(0,0)點(diǎn)為縮放中心堪藐,也可以指定縮放中心

public void scale(float sx, float sy)
sx挑围、sy:水平杉辙、豎直方向縮放比例
0~1代表縮小,>1代表放大蜘矢,若為負(fù)數(shù),縮放之后以x軸或y軸為對(duì)稱軸翻轉(zhuǎn)
以默認(rèn)原點(diǎn)(0,0)為縮放中心將畫布縮放

public final void scale(float sx, float sy, float px, float py)
以(px,py)為縮放中心將畫布縮放

PS:縮放后品腹,繪制在畫布上的圖形也會(huì)等比例縮放

測(cè)試

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(10);
        mPaint.setColor(Color.RED);

        Rect rectCanvas = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
        Rect rect = new Rect(40, 40, 200, 100);

        canvas.drawRect(rectCanvas, mPaint);//繪制畫布矩形邊框舞吭,方便觀察
        canvas.drawRect(rect, mPaint);

        canvas.scale(0.5f, 0.5f);//縮放
        
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(rectCanvas, mPaint);//繪制縮放后畫布矩形邊框,方便觀察
        canvas.drawRect(rect, mPaint);
    }

這里需要注意的是:平移蔑穴、旋轉(zhuǎn)惧浴、傾斜的疊加方式和縮放不同
例:

canvas.translate(100, 100);
canvas.translate(100, 100);
上面連續(xù)兩次平移 = canvas.translate(200, 200);

canvas.rotate(30);
canvas.rotate(30);
上面連續(xù)兩次旋轉(zhuǎn) = canvas.rotate(60);

canvas.scale(0.5f, 0.5f);
canvas.scale(0.5f, 1f);
上面連續(xù)兩次縮放 = canvas.rotate(0.5f*0.5f,0.5f*1f) = canvas.rotate(0.25f捐腿,0.5f) 

根據(jù)這個(gè)特性我們可以繪制一個(gè)"箭靶":

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true);

        canvas.translate(canvas.getWidth() / 2, canvas.getHeight() / 2);

        for (int i = 0; i <= 30; i++) {
            canvas.drawCircle(0, 0, 300, mPaint);
            canvas.scale(0.88f, 0.88f);
        }
    }
4柿顶、傾斜(skew)
public void skew(float sx, float sy)
sx:將畫布在x軸方向上傾斜相應(yīng)的角度,sx為傾斜角度的tan值
sy:將畫布在y軸方向上傾斜相應(yīng)的角度绞佩,sy為傾斜角度的tan值

注意:這里的sx,sy是三角函數(shù)中的tan值品山,比如在x軸方向上傾斜45度肘交,tan45°=1扑馁,這里的sx = 1
傾斜也是可以疊加使用的,連續(xù)傾斜角度 = 每次傾斜的tan值對(duì)應(yīng)角度相加

測(cè)試

    private void gogogo(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLUE);

        Rect rect = new Rect(0, 0, 300, 300);
        canvas.drawRect(rect, mPaint);

        canvas.skew(1, 0);//tan45°=1,x軸方向傾斜45°

        mPaint.setColor(Color.argb(0x66, 0, 0xff, 0));
        canvas.drawRect(rect, mPaint);
    }

二复罐、畫布裁剪

public boolean clipRect(Rect rect)
public boolean clipRect(RectF rect)
public boolean clipRect(Rect rect, Region.Op op)
public boolean clipRect(RectF rect, Region.Op op)
public boolean clipRect(int left, int top, int right, int bottom)
public boolean clipRect(float left, float top, float right, float bottom)
public boolean clipRect(float left, float top, float right, float bottom,Region.Op op)
            
public boolean clipPath(Path path)
public boolean clipPath(Path path, Region.Op op)

public boolean clipRegion(Region region)
public boolean clipRegion(Region region, Region.Op op)

這些方法大同小異效诅,都是從原畫布裁剪出一個(gè)新畫布,就不一一贅述了.
測(cè)試

    private void gogogo(Canvas canvas) {
        canvas.drawColor(Color.GRAY);//裁剪前---為整個(gè)畫布設(shè)置背景色

        canvas.clipRect(new Rect(200, 200, 400, 400));//裁剪

        canvas.drawColor(Color.YELLOW);//裁剪后---為整個(gè)畫布設(shè)置背景色
    }

三咽笼、畫布的保存(save)與恢復(fù)(restore)

上面介紹的畫布操作都是不可逆的戚炫,而且畫布操作會(huì)影響后面的繪圖,但是大多數(shù)時(shí)候我們需要使用之前狀態(tài)的畫布双肤,所以我們需要保存畫布的狀態(tài)(save),在需要的時(shí)候恢復(fù)(restore).

public int save() 
每次調(diào)用杨伙,都會(huì)把當(dāng)前畫布狀態(tài)進(jìn)行保存,然后放入特定的棧中

public void restore()
每當(dāng)調(diào)用限匣,就會(huì)把棧中最頂層的畫布狀態(tài)取出來(lái),并按照這個(gè)狀態(tài)恢復(fù)畫布米死,之后的繪圖就是在這個(gè)恢復(fù)的畫布上

測(cè)試

    private void gogogo(Canvas canvas) {
        canvas.drawColor(Color.GRAY);
        canvas.save();//第1次保存畫布狀態(tài)---全屏幕

        canvas.clipRect(new Rect(100, 100, 600, 600));
        canvas.drawColor(Color.RED);
        canvas.save();//第2次保存畫布狀態(tài)---矩形區(qū)域Rect(100, 100, 600, 600)

        canvas.clipRect(new Rect(200, 200, 500, 500));
        canvas.drawColor(Color.GREEN);
        canvas.save();//第3次保存畫布狀態(tài)---矩形區(qū)域Rect(200, 200, 500, 500)

        canvas.clipRect(new Rect(300, 300, 400, 400));
        canvas.drawColor(Color.BLUE);
    }

上面的測(cè)試代碼進(jìn)行了3次save,在最后一次save后又裁剪了一次畫布并將其背景色設(shè)為藍(lán)色

現(xiàn)在峦筒,我們一步步恢復(fù)看看效果:

    private void gogogo(Canvas canvas) {
        ......
        canvas.restore();
        canvas.drawColor(Color.WHITE);//第1次恢復(fù)---截圖

        canvas.restore();
        canvas.drawColor(Color.BLACK);//第2次恢復(fù)---截圖

        canvas.restore();
        canvas.drawColor(Color.YELLOW);//第3次恢復(fù)---截圖
    }

如果你想直接恢復(fù)到第一次保存的畫布狀態(tài),可以連續(xù)調(diào)用:

canvas.restore();
canvas.restore();
canvas.restore();

或者調(diào)用方法:

public void restoreToCount(int saveCount)

這個(gè)方法會(huì)彈出棧中指定位置以及以上所有狀態(tài)物喷,并根據(jù)指定位置狀態(tài)進(jìn)行恢復(fù)
比如此處參數(shù)傳1,會(huì)彈出第1峦失、2、3次保存的畫布狀態(tài)尉辑,并根據(jù)第1次保存的畫布狀態(tài)進(jìn)行恢復(fù)

這里還有一個(gè)方法:

public int getSaveCount()
獲取棧中保存畫布狀態(tài)的次數(shù),這個(gè)方法的最小返回值為1隧魄,即使彈出了所有的畫布狀態(tài),返回值依舊為1襟企,代表默認(rèn)狀態(tài)

四闸溃、Demo

最后給出一個(gè)Demo拱撵,演示本文內(nèi)容

public class XView extends View {

    private Paint mPaint;

    public XView(Context context) {
        this(context, null);
    }

    public XView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(3);
        mPaint.setColor(Color.BLUE);
        mPaint.setAntiAlias(true);//抗鋸齒功能
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.save();//保存畫布A
        for (int i = 0; i < 5; i++) {
            canvas.drawCircle(100, 100, 50, mPaint);
            canvas.translate(60, 0);
        }
        canvas.restore();//恢復(fù)畫布A


        canvas.translate(100, 200);//平移畫布,讓接下來(lái)的圖形繪制在上一次圖形的下面
        canvas.save();//保存平移后的畫布B
        for (int i = 0; i < 30; i++) {
            canvas.drawRect(0, 0, 400, 400, mPaint);
            canvas.scale(0.88f, 0.88f, 200, 200);//縮放畫布
        }
        canvas.restore();//恢復(fù)平移后的畫布B


        canvas.translate(0, 400);//平移畫布,讓接下來(lái)的圖形繪制在上一次圖形的下面
        canvas.save();
        canvas.drawCircle(200, 200, 200, mPaint);
        for (int i = 0; i < 12; i++) {
            canvas.drawLine(200, 200, 400, 200, mPaint);
            canvas.rotate(30, 200, 200);
        }
        canvas.restore();
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市乓旗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屿愚,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件穷遂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡娱据,警方通過(guò)查閱死者的電腦和手機(jī)蚪黑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)中剩,“玉大人忌穿,你說(shuō)我怎么就攤上這事〗崽洌” “怎么了掠剑?”我有些...
    開(kāi)封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)郊愧。 經(jīng)常有香客問(wèn)我朴译,道長(zhǎng),這世上最難降的妖魔是什么属铁? 我笑而不...
    開(kāi)封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任澜公,我火速辦了婚禮喇肋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘甚侣。我一直安慰自己殷费,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布仍律。 她就那樣靜靜地躺著水泉,像睡著了一般窒盐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蟹漓,一...
    開(kāi)封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天葡粒,我揣著相機(jī)與錄音塔鳍,去河邊找鬼。 笑死腔寡,一個(gè)胖子當(dāng)著我的面吹牛放前,可吹牛的內(nèi)容都是我干的糯彬。 我是一名探鬼主播撩扒,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼搓谆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了黔寇?” 一聲冷哼從身側(cè)響起缝裤,我...
    開(kāi)封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤憋飞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后叨粘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘤睹,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轰传,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年获茬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恕曲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佩谣。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茸俭,死狀恐怖安皱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腾窝,我是刑警寧澤虹脯,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布归形,位于F島的核電站鼻由,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蔼紧。R本人自食惡果不足惜奸例,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一查吊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宋列,春花似錦炼杖、人聲如沸盗迟。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至员帮,卻和暖如春捞高,著一層夾襖步出監(jiān)牢的瞬間渣锦,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工听盖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裂七,地道東北人仓坞。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓徙瓶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親侦镇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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