Android繪圖基礎(chǔ)--Canvas和Drawable

Github鏈接

概述

畫2D圖形有兩種方法:

  • 把圖片和動(dòng)畫設(shè)置到布局文件的View里,整個(gè)繪圖過(guò)程由系統(tǒng)的視圖樹(shù)處理罕伯,我們只需要定義好圖形蔫巩。適合于不需要?jiǎng)討B(tài)改變的簡(jiǎn)單圖形舆驶,如靜態(tài)圖片和定義好的動(dòng)畫
  • 在Canvas上繪圖渊胸。在類的ondraw方法內(nèi)獲取Canvas旬盯,或者調(diào)用Canvas.drawXXX方法。適合重復(fù)重繪自己的圖形翎猛,如視頻。既可以通過(guò)自定義View通過(guò)invalidate接剩,在UI線程里回調(diào)onDraw方法切厘,也可以通過(guò)SurfaceView啟動(dòng)別的線程調(diào)用invalidate。

用Canvas繪圖

Canvas實(shí)際上是封裝了各種draw方法的類懊缺,調(diào)用draw方法把圖形繪制到底層的Surface上疫稿,即繪制在Window上。

  • 在onDraw方法里使用Canvas繪制比較簡(jiǎn)單鹃两,不再贅述遗座。
  • 在SurfaceView通過(guò)lockCanvas獲取Canvas,同上俊扳。
  • 自定義Canvas途蒋。創(chuàng)建Canvas時(shí),需要設(shè)置Bitmap馋记。Bitmap中有內(nèi)存指針mNativePtr和屬性号坡,可以簡(jiǎn)單視為一小塊內(nèi)存。從前篇知道梯醒,繪制其實(shí)是寫一塊共享內(nèi)存宽堆,而B(niǎo)itmap可視為共享內(nèi)存的一小塊。

這個(gè)例子中構(gòu)造了兩個(gè)Canvas和一個(gè)Bitmap茸习,分別調(diào)用其draw方法畜隶,先是mCanvas往Bitmap里繪制一個(gè)方塊,再在onDraw方法內(nèi)調(diào)用canvas.drawBitmap繪制這個(gè)方塊号胚。

CustomView.draw.png

思考一個(gè)問(wèn)題籽慢,為什么mCanvas需要設(shè)置Bitmap?

很簡(jiǎn)單涕刚,因?yàn)樗鼪](méi)有持有一塊內(nèi)存地址嗡综,自然沒(méi)法繪制。來(lái)看一下draw的起點(diǎn)ViewRootImpl(軟件繪制杜漠,不開(kāi)啟硬件加速下)极景。

ViewRootImpl.drawSoftware.png

這個(gè)通過(guò)mSurface.lockCanvas返回的Canvas是View.draw的canvas變量察净,所以當(dāng)1,2情況時(shí)盼樟,Canvas都持有一個(gè)Bitmap氢卡,指向共享內(nèi)存里的某一小塊,當(dāng)調(diào)用Canvas.draw方法時(shí)就能繪制出東西晨缴。但對(duì)于自定義Canvas來(lái)說(shuō)并不是译秦,即使設(shè)置一個(gè)Bitmap和繪制了Bitmap,但不往共享內(nèi)存上寫击碗,屏幕上是不會(huì)顯示的筑悴,SurfaceView同理,通過(guò)Surface.lockCanvas獲取持有共享內(nèi)存的Canvas稍途,繪制完畢后調(diào)用Surface.unlockCanvasAndPost把繪制內(nèi)容顯示到surface上并release掉Canvas阁吝。

SurfaceView.internalLockCanvas.png

順帶一提Canvas.save和Canvas.restore方法,如下Demo


MyView.onDraw.png

效果圖如

效果圖.png

畫的是三個(gè)顏色和旋轉(zhuǎn)角度都不同的小方形械拍。

步驟1把默認(rèn)坐標(biāo)系旋轉(zhuǎn)20°突勇,畫出第一個(gè)藍(lán)色的方形,步驟2保存當(dāng)前的matrix(旋轉(zhuǎn)了20°)坷虑,繼續(xù)旋轉(zhuǎn)20°甲馋,此時(shí)坐標(biāo)系已經(jīng)旋轉(zhuǎn)了40°,畫出第二個(gè)黃色的方塊迄损,步驟3定躏,恢復(fù)上一步保存的matrix(旋轉(zhuǎn)了20°),此時(shí)坐標(biāo)系還是旋轉(zhuǎn)了20°海蔽,步驟4共屈,再旋轉(zhuǎn)40°,此時(shí)坐標(biāo)系旋轉(zhuǎn)了60°党窜,畫出第三個(gè)黑色方塊拗引。

Canvas.save用于保存當(dāng)前matrix和clip,Canvas.restore用于恢復(fù)上次保存的matrix和clip幌衣。

Drawable

Drawable是一個(gè)能畫出來(lái)的物體的抽象矾削,使用前需要調(diào)用setBounds確定位置和大小,通過(guò)getIntrinsicHeight和getIntrinsicWidth取到實(shí)際大小豁护。Drawable可以有幾種形式存在:Bitmap哼凯、Nine Patch、Vector楚里、Shape断部、Layers等。

getdrawable.png

從Resource.getDrawable會(huì)判斷是否.xml結(jié)尾班缎,不是的話走6蝴光,7步她渴,如果從xml中讀取,需要getResource.getDrawable -> ResourceImpl.loadDrawableForCookie -> drawable.createFromXml -> DrawableInflater.inflateFromXmlForDensity -> drawable.inflateFromTag

DrawableInflater.inflateFromTag.png

看一下Shape實(shí)現(xiàn)類GradientDrawable的inflate實(shí)現(xiàn)蔑祟,讀取各項(xiàng)屬性并賦值趁耗,到draw方法。

GradientDrawable.draw.png

調(diào)用canvas.drawRect把mRect畫出來(lái)疆虚,而mRect的賦值在ensureValidRect苛败。[圖片上傳失敗...(image-a25af0-1515826613001)]

bounds在哪里設(shè)置的?答案是ImageView.updateDrawable內(nèi)径簿,會(huì)調(diào)用Drawable.getIntrinsicHeight賦值(從xml中size屬性讀劝涨),再調(diào)用configureBounds -> setBounds牍帚,如果使用的不是ImageView儡遮,一定要在draw之前調(diào)用setBounds,否則size就會(huì)出錯(cuò)暗赶。

ImageView.updateDrawable.png

回到loadDrawableForCookie,再看一下6肃叶,7步加載圖片的過(guò)程蹂随,通過(guò)AssetManager讀取圖片流數(shù)據(jù),通過(guò)Drawable.createFromResourceStream這個(gè)我們經(jīng)常使用的方法獲取到Drawable因惭。

Drawable.createFromResourcesStream.png

取到屏幕密度之后調(diào)用BitmapFactory.decodeResourcesStream岳锁,計(jì)算密度后調(diào)用native創(chuàng)建Bitmap,感興趣的同學(xué)可以看下更具體的分析文章(如理解Bitmap)蹦魔。

總結(jié)

本文探究了兩點(diǎn)

  • Canvas能繪制的原因激率,View.Canvas與new Canvas的區(qū)別。
  • 創(chuàng)建Drawable的過(guò)程

參考資料

Android 7.1.1 源碼

Android 官方文檔勿决,Canvas and Drawable, Drawable

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乒躺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子低缩,更是在濱河造成了極大的恐慌嘉冒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咆繁,死亡現(xiàn)場(chǎng)離奇詭異讳推,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)玩般,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門银觅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人坏为,你說(shuō)我怎么就攤上這事究驴∧餍鳎” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵纳胧,是天一觀的道長(zhǎng)镰吆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)跑慕,這世上最難降的妖魔是什么万皿? 我笑而不...
    開(kāi)封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮核行,結(jié)果婚禮上牢硅,老公的妹妹穿的比我還像新娘。我一直安慰自己芝雪,他們只是感情好减余,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著惩系,像睡著了一般位岔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上堡牡,一...
    開(kāi)封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天抒抬,我揣著相機(jī)與錄音,去河邊找鬼晤柄。 笑死擦剑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的芥颈。 我是一名探鬼主播惠勒,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼爬坑!你這毒婦竟也來(lái)了纠屋?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妇垢,失蹤者是張志新(化名)和其女友劉穎巾遭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體闯估,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灼舍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涨薪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骑素。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖刚夺,靈堂內(nèi)的尸體忽然破棺而出献丑,到底是詐尸還是另有隱情末捣,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布创橄,位于F島的核電站箩做,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妥畏。R本人自食惡果不足惜邦邦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望醉蚁。 院中可真熱鬧燃辖,春花似錦、人聲如沸网棍。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)滥玷。三九已至氏身,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惑畴,已是汗流浹背观谦。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桨菜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓捉偏,卻偏偏與公主長(zhǎng)得像倒得,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夭禽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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