概述
畫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è)方塊号胚。
思考一個(gè)問(wèn)題籽慢,為什么mCanvas需要設(shè)置Bitmap?
很簡(jiǎn)單涕刚,因?yàn)樗鼪](méi)有持有一塊內(nèi)存地址嗡综,自然沒(méi)法繪制。來(lái)看一下draw的起點(diǎn)ViewRootImpl(軟件繪制杜漠,不開(kāi)啟硬件加速下)极景。
這個(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阁吝。
順帶一提Canvas.save和Canvas.restore方法,如下Demo
效果圖如
畫的是三個(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等。
從Resource.getDrawable會(huì)判斷是否.xml結(jié)尾班缎,不是的話走6蝴光,7步她渴,如果從xml中讀取,需要getResource.getDrawable -> ResourceImpl.loadDrawableForCookie -> drawable.createFromXml -> DrawableInflater.inflateFromXmlForDensity -> drawable.inflateFromTag
看一下Shape實(shí)現(xiàn)類GradientDrawable的inflate實(shí)現(xiàn)蔑祟,讀取各項(xiàng)屬性并賦值趁耗,到draw方法。
調(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ò)暗赶。
回到loadDrawableForCookie,再看一下6肃叶,7步加載圖片的過(guò)程蹂随,通過(guò)AssetManager讀取圖片流數(shù)據(jù),通過(guò)Drawable.createFromResourceStream這個(gè)我們經(jīng)常使用的方法獲取到Drawable因惭。
取到屏幕密度之后調(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等