Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android圖形基礎知識

android圖形基礎知識

編輯:關於Android編程

 

AndroidGDI基本框架

    在Android中所涉及的概念和代碼最多,最繁雜的就是GDI相關的代碼了。但是本質從抽象上來講,這麼多的代碼和框架就干了一件事情:對顯示緩沖區的操作和管理。

    GDI主要管理圖形圖像的輸出,從整體方向上來看,GDI可以被認為是一個物理屏幕使用的管理器。因為在實際的產品中,我們需要在物理屏幕上輸出不同的窗口,而每個窗口認為自己獨占屏幕的使用,對所有窗口輸出,應用程序不會關心物理屏幕是否被別的窗口占用,而只是關心自己在本窗口的輸出,至於輸出是否能在屏幕上看見,則需要GDI來管理。


    從最上層到最底層的數據流的分析可以看到實際上GDI在上層為GUI提供一個抽象的概念,就好像操作系統中的文件系統所提供文件,目錄等抽象概念一樣,GDI輸出抽象成了文本,畫筆,位圖操作等設備無關的操作,讓應用程序員只需要面對邏輯的設備上下文進行輸出操作,而不要涉及到具體輸出設備,以及輸出邊界的管理。GDI負責將文本、線條、位圖等概念對象映射到具體的物理設備,所以GDI的在大體方向上可以分為以下幾大要素:

畫布

字體

文本輸出

繪畫對象

位圖輸出

Android的GDI系統

     Android的GDI系統所涉及到概念太多,加之使用了OpenGL使得Android的層次和代碼很繁雜。但是我們對於Android的GDI系統需要了解的方面不是他的靜態的代碼關系,而是動態的對象關系,在邏輯運行的架構上理解GDI。我們首先還是需要從代碼結構開始我們的理解。

Frameworks/Libs/Surfaceflinger

Frameworks/base/core/jni/android_view_Surface.cpp

Frameworks/base/core/java/android/view/surface.java

Frameworks/base/Graphics:繪圖接口

Frameworks/Libs/Ui

External/Skia

其中External/Skia是一個C++的2D圖形引擎庫,Android的2D繪制系統都是建立在該基礎之上.Skia完成了:文本輸出,位圖,點,線,圖像解碼等功能。

我在這裡給出AndroidGDI的基本框架示意圖。


    對於上面的GDI架構圖我們只是一個大概的了解,我們有太多的問題需要解決,有太多的疑問需要得到答案,我就一直在想,為什麼設計者有提出如此眾多的概念,這個概念的背景是什麼?他要管理什麼,他要抽象什麼?從前面知道,Android的整個設計理念就是無邊界化,他是如何穿透Linux進程這個鴻溝來達到無邊界的?Surface,Canvas, Layer,LayerBase,NativeBuffer,SurfaceFlinger,SurfaceFlingerClient這些到底是一個什麼東西?如何管理,傳遞的是什麼?創建的是什麼?這些都是抽象的概念,繪畫的終極的緩沖區到底是如何管理的?緩沖區到底在哪裡?

     我們還是看看做終極的,最本質的設計概念,在從這些概念出發,來探討這些概念的形成過程,是否有必要去生成寫概念。SurfaceFlinger本質上干什麼的?SurfaceFlinger的確就是這個意義:應用程序通過SurfaceFlinger將自己的“Surface”投擲到屏幕緩沖區。至於如何投擲的,我們將會在後面詳細描述。

 

Android核心分析(24)-----Android GDI之顯示緩沖管理
2010-06-14 13:36 24466人閱讀 評論(22) 收藏 舉報

Android GDI之屏幕設備管理-動態鏈接庫
       萬丈高樓從地起,從最根源的硬件幀緩沖區開始。我們知道顯示FrameBuffer在系統中就是一段內存,GDI的工作就是把需要輸出的內容放入到該段內存的某個位置。我們從基本的點(像素點)和基本的緩沖區操作開始。

1 基本知識
1.1點的格式
    對於不同的LCD來講,FrameBuffer的二進制格式不一樣,並且可以分為兩部分:

 

   1)點的格式:通常將Depth,即表示多少位表示一個點。
1位表示一個點

2位表示一個點

16位表示一個點

32位表示一個點(Alpha通道)

     2) 點內格式:RGB分量分布表示。
例如對於我們常見的16位表示一個點


1.2.格式之間的轉換
所以屏幕輸出實際上是一個值映射的關系。我們可以有如下的點格式轉換,


源格式可能來自單色位圖和彩色位圖,對於具體的目標機來講,我們的目標格式可能就是一種,例如16位(5/6/5)格式。其實就只存在一種格式的轉換,即從目標格式都是16位格式。


但是,在設計GDI時,基本要求有一個可移植性好,所以我們還是必須考慮對於不同點格式LCD之間的轉換操作。所以在GDI的驅動程序中涉及到如下幾類主要操作:

區域操作(Blit):我們在顯示緩沖區上做的最多的操作就是區塊搬運。由此,很多的應用處理器使用了硬件圖形加速器來完成區域搬運:blit.從我們的主要操作的對象來看,可以分為兩個方向:

1)內存區域到屏幕區域

2)屏幕區域到屏幕區域

3)屏幕區域到內存區域

4)內存區域到內存區域

在這裡我們需要特別提出的是,由於在Linux不同進程之間的內存不能自由的訪問,使得我們的每個Android應用對於內存區域和屏幕緩沖區的使用變得很復雜。在Android的設計中,在屏幕緩沖區和顯示內存緩沖區的管理分類很多的層次,最上層的對象是可以在進程間自由傳遞,但是對於緩沖區內容則使用共享內存的機制。

基於以上的基礎知識,我們可以知道:

(1)代碼中Config及其Format的意義所在了。也就理解了兼容性的意義:采用同硬件相同的點的描述對象

(2)所有屏幕上圖形的移動都是顯示緩沖區搬運的結果。

1.2圖形加速器
    應用處理器都可能帶有圖形加速器,對於不同的應用處理器對其圖形加速器可能有不同的處理方式,對於2D加速來講,都可歸結為Blit。多為數據的搬運,放大縮小,旋轉等。

 

2 Android的緩沖區抽象定義
 
    不同的硬件有不同的硬件圖形加速設備和緩沖內存實現方法。Android Gralloc動態庫抽象的任務就是消除不同的設備之間的差別,在上層看來都是同樣的方法和對象。在Moudle層隱藏緩沖區操作細節。Android使用了動態鏈接庫gralloc.xxx.so,來完成底層細節的封裝。

2.1 本地定義@hardware/libhandware/modules/gralloc

每個動態鏈接庫都是用相同名稱的調用接口:

1)硬件圖形加速器的抽象:BlitEngine,CopyBit的加速操作。

2)硬件FrameBuffer內存管理

3)共享緩存管理

從數據關系上我們來考察..動態鏈接庫的抽象行為:在層次:Hardware.c@hardware/libhardware中對動態鏈接庫中的內容作了全新的包裝。/system/lib/hw/gralloc.xxx.so動態庫文件。從文件Gralloc.h(handware/libhardware/include/hardware)是抽象的結果:hw_get_module從gralloc.xxx.so提取了HAL_MODULE_INFO_SYM(SYM變量)


從展露在外部的數據結構,我們在@Gralloc.cpp看到到了這樣的布局:

static structhw_module_methods_t gralloc_module_methods = {

open:gralloc_device_open

};

structprivate_module_t HAL_MODULE_INFO_SYM = {

   base: {

       common: {

           tag: HARDWARE_MODULE_TAG,

            …

           id: GRALLOC_HARDWARE_MODULE_ID,

           name: "Graphics Memory Allocator Module",

           author: "The Android Open Source Project",

           methods: &gralloc_module_methods

       },

registerBuffer:gralloc_register_buffer,

unregisterBuffer:gralloc_unregister_buffer,

lock: gralloc_lock,

unlock: gralloc_unlock,

    },

   framebuffer: 0,

   flags: 0,

   numBuffers: 0,

   bufferMask: 0,

};

我們建立了什麼對象來支撐緩沖區的操作?

buffer_handle_t:外部接口。

methods.open,registerBuffer,unregisterBuffer,lock,unlock

下面是外部接口和內部對象的結構關系,該類型的結構充分利用CStruct的數據排列特性:基本結構體放置在最前面,本地私有放置在後面,滿足了抽象的需要。

typedef constnative_handle* buffer_handle_t;

private_module_t HAL_MODULE_INFO_SYM 向往暴露的動態鏈接庫接口,通過該接口,我們直接可以使用該對象。


看不清楚上面圖,可以偏一下頭橫著看:


幾個接口函數的解釋:

(1)fb_post

對於幀緩沖區實際地址並不需要向上層報告,所有的操作都是通過fb_post了完成。

 

fp_post的任務就是將一個Buffer的內容傳遞到硬件緩沖區。其實現方式有兩種:

 

 

 

 

(方式1)無需拷貝動作,是把Framebuffer的後buffer切為前buffer,然後通過IOCTRL機制告訴FB驅動切換DMA源地地址。這個實現方式的前提是Linux內核必須分配至少兩個緩沖區大小的物理內存和實現切換的ioctrol,這個實現快速切換。

 

(方式2)利用Copy的方式。不修改內核,則在適配層利用從拷貝的方式進行,但是這個是費時了。

 

 

 

(2)gralloc的主要功能是要完成:

    1)打開屏幕設備 "/dev/fb0",,並映射硬件顯示緩沖區。

    2)提供分配共享顯示緩存的接口

    3)提供BiltEngine接口(完成硬件加速器的包裝)

(3)gralloc_alloc輸出buffer_handle_t句柄。

    這個句柄是共享的基本依據,其基本原理在後面的章節有詳細描述。

3 總結
      總結一下,/system/lib/hw/gralloc.xxx.so是跟硬件體系相關的一個動態鏈接庫,也可以叫做Android的硬件抽象層。他實現了Android的硬件抽象接口標准,提供顯示內存的分配機制和CopyBit等的加速實現。而如何具體實現這些功能,則跟硬件平台的配備有關系,所以我們看到了對於與不同的硬件架構,有不同的配置關系。

Android核心分析(25)------Android GDI之共享緩沖區機制
2010-06-14 16:29 17969人閱讀 評論(13) 收藏 舉報

Androird GDI之共享緩沖區機制
1  native_handle_t對private_handle_t的包裹
    private_handle_t是gralloc.so使用的本地緩沖區私有的數據結構,而Native_handle_t是上層抽象的可以在進程間傳遞的數據結構。在客戶端是如何還原所傳遞的數據結構呢?首先看看native_handle_t對private_handle_t的抽象包裝。


numFds= sNumFds=1;

numInts= sNumInts=8;

這個是Parcel中描述句柄的抽象模式。實際上是指的Native_handle所指向句柄對象的具體內容:

numFds=1表示有一個文件句柄:fd/

numInts= 8表示後面跟了8個INT型的數據:magic,flags,size,offset,base,lockState,writeOwner,pid;

由於在上層系統不要關心buffer_handle_t中data的具體內容。在進程間傳遞buffer_handle_t(native_handle_t)句柄是其實是將這個句柄內容傳遞到Client端。在客戶端通過Binder讀取readNativeHandle @Parcel.cpp新生成一個native_handle。

native_handle*Parcel::readNativeHandle() const

{

native_handle* h =native_handle_create(numFds, numInts);

    for(int i=0 ; err==NO_ERROR && i<="" font="">

       h->data[i] = dup(readFileDescriptor());

       if (h->data[i] < 0) err = BAD_VALUE;

    }

    err= read(h->data + numFds, sizeof(int)*numInts);

    ….

return h;

}

    這裡需要提到的是為在構造客戶端的native_handle時,對於對方傳遞過來的文件句柄的處理。由於不是在同一個進程中,所以需要dup(…)一下為客戶端使用。這樣就將Native_handle句柄中的,客戶端感興趣的從服務端復制過來。這樣就將Private_native_t的數據:magic,flags,size,offset,base,lockState,writeOwner,pid;復制到了客戶端。

    客戶端利用這個新的Native_buffer被Mapper傳回到gralloc.xxx.so中,獲取到native_handle關聯的共享緩沖區映射地址,從而獲取到了該緩沖區的控制權,達到了客服端和Server間的內存共享。從SurfaceFlinger來看就是作圖區域的共享。

2 Graphic Mapper是干什麼的?
    服務端(SurfaceFlinger)分配了一段內存作為Surface的作圖緩沖,客戶端怎樣在這個作圖緩沖區上工作呢?這個就是Mapper(GraphicBufferMapper)y要干的事情。兩個進程間如何共享內存,如何獲取到共享內存?Mapper就是干這個得。需要利用到兩個信息:共享緩沖區設備句柄,分配時的偏移量。Mapper利用這樣的原理:

    客戶端只有lock,unlock,實質上就是mmap和ummap的操作。對於同樣一個共享緩沖區,偏移量才是總要的,起始地址不重要。實際上他們操作了同一物理地址的內存塊。我們在上面討論了native_handle_t對private_handle_t 的包裹過程,從中知道服務端給客戶端傳遞了什麼東西。

        進程1在共享內存設備上預分配了8M的內存。以後所有的分配都是在這個8M的空間進行。對以該文件設備來講,8M物理內存提交後,就實實在在的占用了8M內存。每個每個進程都可以同個該內存設備共享該8M的內存,他們使用的工具就會mmap。由於在mmap都是用0開始獲取映射地址,所以所有的客戶端進程都是有了同一個物理其實地址,所以此時偏移量和size就可以標識一段內存。而這個偏移量和size是個數值,從服務進程傳遞到客戶端直接就可以使用。


 
3 GraphicBuffer(緩沖區代理對象)
typedef structandroid_native_buffer_t

{

   struct android_native_base_t common;

    intwidth;

    intheight;

    intstride;

    intformat;

    intusage;

    …

    buffer_handle_thandle;

    …

}android_native_buffer_t;

關系圖表:

GraphicBuffer :EGLNativeBase:android_native_buffer_t

GraphicBuffer(parcel&)建立本地的GraphicBuffer的數據native_buffer_t。在通過接收對方的傳遞的native_buffer_t 構建GraphicBuffer。我們來看看在客戶端Surface::lock獲取操作緩沖區的函數調用:

Surface::lock(SurfaceInfo*other, Region* dirtyIn, bool blocking)

    {int Surface::dequeueBuffer(android_native_buffer_t** buffer)(Surface)

       {status_t Surface::getBufferLocked(int index, int usage)

         {

              sp buffer= s->requestBuffer(index, usage);

          {

virtual sp requestBuffer(intbufferIdx, int usage)

{  remote()->transact(REQUEST_BUFFER, data, &reply);

   sp buffer = newGraphicBuffer(reply);

Surface::Lock建立一個在Client端建立了一個新的GraphicBuffer對象,該對象通過(1)描述的原理將SurfaceFlinger的buffer_handle_t相關數據構成新的客戶端buffer_handle_t數據。在客戶端的Surface對象就可以使用GraphicMapper對客戶端buffer_handle_t進行mmap從而獲取到共享緩沖區的開始地址了。

4 總結
   Android在該節使用了共享內存的方式來管理與顯示相關的緩沖區,他設計成了兩層,上層是緩沖區管理的代理機構GraphicBuffer,及其相關的native_buffer_t,下層是具體的緩沖區的分配管理及其緩沖區本身。上層的對象是可以在經常間通過Binder傳遞的,而在進程間並不是傳遞緩沖區本身,而是使用mmap來獲取指向共同物理內存的映射地址。

Android核心分析(26)-----Android GDI之SurfaceFlinger
2010-06-14 20:31 36394人閱讀 評論(28) 收藏 舉報

Android GDI之SurfaceFlinger
SurfaceFinger按英文翻譯過來就是Surface投遞者。SufaceFlinger的構成並不是太復雜,復雜的是他的客戶端建構。SufaceFlinger主要功能是:

1) 將Layers(Surfaces) 內容的刷新到屏幕上

2) 維持Layer的Zorder序列,並對Layer最終輸出做出裁剪計算。

3) 響應Client要求,創建Layer與客戶端的Surface建立連接

4) 接收Client要求,修改Layer屬性(輸出大小,Alpha等設定)

但是作為投遞者的實際意義,我們首先需要知道的是如何投遞,投擲物,投遞路線,投遞目的地。

1  SurfaceFlinger的基本組成框架

SurfaceFlinger管理對象為:

mClientsMap:管理客戶端與服務端的連接。

ISurface,IsurfaceComposer:AIDL調用接口實例

mLayerMap:服務端的Surface的管理對象。

mCurrentState.layersSortedByZ:以Surface的Z-order序列排列的Layer數組。

graphicPlane 緩沖區輸出管理

OpenGL ES:圖形計算,圖像合成等圖形庫。

gralloc.xxx.so這是個跟平台相關的圖形緩沖區管理器。

pmem Device:提供共享內存,在這裡只是在gralloc.xxx.so可見,在上層被gralloc.xxx.so抽象了。

2 SurfaceFinger Client和服務端對象關系圖

Client端與SurfaceFlinger連接圖:

 

Client對象:一般的在客戶端都是通過SurfaceComposerClient來跟SurfaceFlinger打交道。

 

3 主要對象說明
3.1 DisplayHardware&FrameBuffer
    首先SurfaceFlinger需要操作到屏幕,需要建立一個屏幕硬件緩沖區管理框架。Android在設計支持時,考慮多個屏幕的情況,引入了graphicPlane的概念。在SurfaceFlinger上有一個graphicPlane數組,每一個graphicPlane對象都對應一個DisplayHardware.在當前的Android(2.1)版本的設計中,系統支持一個graphicPlane,所以也就支持一個DisplayHardware。

SurfaceFlinger,Hardware硬件緩沖區的數據結構關系圖。


3.2 Layer

method:setBuffer 在SurfaceFlinger端建立顯示緩沖區。這裡的緩沖區是指的HW性質的,PMEM設備文件映射的內存。

1) layer的繪制

voidLayer::onDraw(const Region& clip) const

{

    intindex = mFrontBufferIndex;

   GLuint textureName = mTextures[index].name;

  drawWithOpenGL(clip,mTextures[index]);

}

3.2mCurrentState.layersSortedByZ
   以Surface的Z-order序列排列的LayerBase數組,該數組是層顯示遮擋的依據。在每個層計算自己的可見區域時,從Z-order 頂層開始計算,是考慮到遮擋區域的裁減,自己之前層的可見區域就是自己的不可見區域。而繪制Layer時,則從Z-order底層開始繪制,這個考慮到透明層的疊加。

4 SurfaceFlinger的運行框架
    我們從前面的章節<Android Service>的基本原理可以知道,SurfaceFlinger的運行框架存在於:threadLoop,他是SurfaceFlinger的主循環體。SurfaceFlinger在進入主體循環之前會首先運行:SurfaceFlinger::readyToRun()。

4.1SurfaceFlinger::readyToRun()
(1)建立GraphicPanle

(2)建立FrameBufferHardware(確定輸出目標)

     初始化:OpenGL ES

          建立兼容的mainSurface.利用eglCreateWindowSurface。

          建立OpenGL ES進程上下文。

   建立主Surface(OpenGL ES)。DisplayHardware的Init()@DisplayHardware.cpp函數對OpenGL做了初始化,並創建立主Surface。為什麼叫主Surface,因為所有的Layer在繪制時,都需要先繪制在這個主Surface上,最後系統才將主Surface的內容”投擲”到真正的屏幕上。

(3) 主Surface的綁定

1)在DisplayHandware初始完畢後,hw.makeCurrent()將主Surface,OpenGL ES進程上下文綁定到SurfaceFlinger的上下文中,

2)之後所有的SurfaceFlinger進程中使用EGL的所有的操作目的地都是mSurface@DisplayHardware。

這樣,在OpenGL繪制圖形時,主Surface被記錄在進程的上下文中,所以看不到顯示的主Surfce相關參數的傳遞。下面是Layer-Draw,Hardware.flip的動作示意圖:


4.2 ThreadLoop

(1)handleTransaction(…):主要計算每個Layer有無屬性修改,如果有修改著內用需要重畫。

(2)handlePageFlip()

   computeVisibleRegions:根據Z-Order序列計算每個Layer的可見區域和被覆蓋區域。裁剪輸出范圍計算-

在生成裁剪區域的時候,根據Z_order依次,每個Layer在計算自己在屏幕的可顯示區域時,需要經歷如下步驟:

  1)以自己的W,H給出自己初始的可見區域

  2)減去自己上面窗口所覆蓋的區域


在繪制時,Layer將根據自己的可將區域做相應的區域數據Copy。

(3)handleRepaint()

composeSurfaces(需要刷新區域):

根據每個Layer的可見區域與需要刷新區域的交集區域從Z-Order序列從底部開始繪制到主Surface上。

(4)postFramebuffer()

(DisplayHardware)hw.flip(mInvalidRegion);

eglSwapBuffers(display,mSurface):將mSruface投遞到屏幕。

5總結
現在SurfaceFlinger干的事情利用下面的示意圖表示出來:


 

Android核心分析(27)-----Android GDI 之SurfaceFlinger之動態結構示意圖
2010-06-14 22:02 18942人閱讀 評論(12) 收藏 舉報

SurfaceFlinger對象建立過程示意
 

1SurfaceSession的建立
    客戶端請求建立Surface時,首先在要與SurfaceFlinger建立一個Session,然後再Session上建立一個Connection通過概念返回Bclient對象。WindowManagerService在添加第一個窗口前會檢查SurfaceSession是否建立,如何沒有建立,將會新建立一個實例來代表與SurfaceFlinger的一個連接。

new SurfaceSession()@windowAddedLocked()@WindowManagerService.java。

SurfaceSession的建立過程大部分是在C++ Native空間中完成的,表現在SurfaceSession的初始化函數:init()本地函數上。從下面的初始化函數可以看到:

Init()<->SurfaceSession_init@android_view_Surface.cpp

    new SurfaceComposerClient

   SurfaceSession在C++Native空間建立一個SurfaceComposerClient實例。而該實例的建立實現了如下的與SurfaceFlinger通訊基礎:

(1)建立了代理SurfaceFlinger服務的代理服務端

(2)建立了IsurfaceFlingerClient連接,在SurfaceFlinger端建立了對應的Client,並將BClient返回給WindowManagerService。

 

2Surface的建立
    在WindowManagerService中WindowState類中,我們知道每個主窗口子啊需要是都需要建立一個Surface與之對應。win.createSurfaceLocked()@relayoutWindow

Surface.java

Init()< -->Surface_init(….,session,pid,dpy,w,h,format)@android_view_Surface.cpp

    SurfaceControl surface(client->createSurface

     在mClient的連接上:建立ISurface接口:

     M_Client->greateSurface(...)@

       Bclient ::createSurface(mId...)@SurfaceFlinger.cpp

           mFlinger->createSurface(clientid....)

              createNormalSurfaceLocked

*createNormalSurfaceLocked:建立一個Layer分配顯示內存

*createPushBuffersSurfaceLocked:建立一個LayBuffer但是不分配顯示內存。


 

Android核心分析(28)-----Android GDI之Surface&Canvas

2010-06-14 22:05 20162人閱讀 評論(41) 收藏 舉報

Surface&Canvas

    Canvas為在畫布的意思。Android上層的作圖幾乎都通過Canvas實例來完成,其實Canvas更多是一種接口的包裝。drawPaints ,drawPoints,drawRect,drawBitmap...


1 Canvas與Surface之間本質關系

     對於本節,我們不去研究Skia圖形引擎本身,我們需要了解的我們的所做的圖形到底放置到了那個地方,並且這個Canvas如何與Surface連接在一起的。

Canvas(Java)在C++Native層有一個Native Canvas的C++對象所對應。

lockCanvas()@java

        Surface_lockCanvas@android_view_Surface.cpp

      SurfaceControl->newSurface(control) @Surface.cpp

     Surface: lock操作:


     GraphicBuffer :lock

       getBufferMapper().lock<-> GraphicBufferMapper ::lock

                 mAllocMod->lock<->gralloc_module_t::lock

通過SurfaceLock可取得Surface(mLockedBuffe)所對應的圖形緩沖區地址。

(1) 建立與SkCanvas連接的位圖設備,而該位圖使用上面取得的圖形緩沖區地址做自己的位圖內存。

(2) 設置SkCanvas的作圖目標設備為該位圖。

通過該過程就建立起了SurfaceControl與Canvas之間的聯系。


 

2  View:OnDraw的本源

不是使用OpenGL繪制時,Android在View屬性發生變化,新建View時,或者Z-order發生變化時,需要對系統屏幕上的View重新繪制,此時我們的View會執行OnDraw(canvas),這個根源在哪裡呢?

ViewRoot.Java

  performTraversals(..)

     …

     draw()

       canvas = surface.lockCanvas(dirty);

        …

      mView.draw(canvas);

        draw(cavas)@view.java

            background.draw(canvas);

             onDraw(cavas)

            dispatchDraw(cavas)

             onDrawScrolbars(cavas)

   surface.unlockCanvasAndPost(canvas);

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved