Android隱喻(三) 圖形繪制: Canvas、SurfaceView、Paint、Surface、SurfaceHolder、Bitmap


  如果你是畫家,有一群人想要看你的畫,想象一下整個流程。

  首先找一塊畫布,執筆,繪制,完成后找一面牆把畫掛上去,眾人圍觀。其中不可缺少的要素包括:畫布、(畫家拿筆)繪制、掛牆展示。對於計算機,也就對應着 幀緩存、像素填充、刷新至屏幕。如果你希望顯示一些東西,那么首先你需要拿到一塊緩存,然后向這塊緩存中填充像素(也就是繪制),然后將這塊緩存交給屏幕顯示出來。

   Android中的圖形繪制,不外如是。

  來看一下典型的繪制-顯示代碼:

class GameView extends SurfaceView implements Callback
{
  @Override
  public void surfaceCreated(SurfaceHolder holder)
  {
    Canvas c = holder.lockCanvas();
    c.drawBitmap(x, y, bitmap, paint);
    holder.unlockCanvasAndPost(c);
  }
}

   這里先談談Canvas,其英文語意為"畫布“,一開始天真的我就真把它當成了畫布。所謂畫布,乃承載畫的載體,之於計算機,我的第一反應:畫布 = buffer = 緩存,用像素填充畫布,然后將它”貼“到屏幕上,就完成了繪制和顯示的過程。正是由於我有這個先入為主的觀念,按我看來,Canvas應該有個形如Canvas(width, height, PixelType)構造方法,用來生成一塊width * height * getBytesOfPerPixel(PixelType) 字節大小的buffer,然后我就可以盡情在上面進行繪制了。但是Canvas最常見的構造方法是Canvas(Bitmap),這曾令我百思不得其解,把一個位圖給一塊畫布,這是什么意思?這個錯誤的隱喻導致我在看Canvas和Bitmap時糾結了很久。

  事實上,Canvas為一個功能類,提供了大量的drawXXX方法,所以它實際上扮演的是畫家的角色,而非畫布。要想繪畫,你必須給畫家一塊畫布,而Canvas(Bitmap)中的Bitmap則充當了畫布的角色。

  再來看上面那個典型的繪制-顯示代碼:我們需要從中找到繪制(畫家和畫筆)、緩存(畫布)、刷新至屏幕(掛牆展示)這三個基本要素。

  1,繪制

    和一開始的隱喻對應,畫家是Canvas,畫筆是Paint類的實例paint,Canvas使用paint完成繪制。即代碼:c.drawBitmap(x, y, bitmap, paint); 本質是將bitmap繪制(復制)在畫布上。

  2, 畫布

    來看看畫布在哪,Canvas來自於holder.lockCanvas(),這個方法會通過jni調用對應的native方法,其本質是從holder所持有的Surface中獲取屏幕沖區的地址,然后用這個地址構造一個native的Bitmap對象(SKBitmap),再用這個Bitmap對象構造一個native的Canvas對象(SKCanvas),再返回這個Canvas對象,java層的Canvas對象其實只是對SKCanvas對象的一個簡單包裝,所有繪制方法都是轉交給SKCanvas來做。

   所以這里的畫布就是由屏幕緩存所構造出來的Bitmap。holder.lockCanvas()所返回的Canvas對象是在這塊畫布上進行繪制。這里的Surface是對屏幕緩存的抽象。我們繪制,可以在屏幕緩存上直接繪制,也可以先繪制在任何一塊緩存上,然后將這塊緩存中的內容復制到屏幕緩存中。

   值得注意的是,這里你並沒有直接獲得一塊畫布(Bitmap),而只是獲得了一個畫家(Canvas),畫家出現的時候手里已經有了畫布,不需要你提供給他,就可以進行繪制。

   3,刷新至屏幕

    對應的代碼為holder.unlockCanvasAndPost(c),其本質就是將之前繪制好的畫布交給圖形顯示驅動,在真實的設備(屏幕)上顯示出來。

    總結: 如果你想在Android上進行繪制,你首先要找到一塊緩存(Bitmap),然后將其交給畫家(構造Canvas),讓畫家在其上繪制(drawXXX,可能需要提供一只Paint畫筆),再將其交給屏幕去顯示(本質上是將 所繪制的緩存復制到屏幕緩存中,再觸發更新命令,所以你需要找到一個Surface(屏幕緩存),而Android中你無法直接構造Surface,你只能通過SurfaceView或其它方式由系統提供給你一個Surface,而SurfaceView中的Surface是在WindowManagerImpl.addView() - 即添加窗口到WMS時創建的,這個Surface對象為ViewRoot所持有。)

  Andoid中除了用SurfaceView自己來控制屏幕繪制外,對於簡單應用,更通常的做法是使用XML指定的View布局,通過框架回調來實現屏幕顯示,但其本質和SurvaceView並無區別,同樣是 獲取緩存、繪制、顯示在屏幕,后續將進行分析。

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM