Android之SurfaceView學習

銳湃發表於2015-11-11

SurfaceView的API介紹

Provides a dedicated drawing surface embedded inside of a view hierarchy. You can control the format of this surface and, if you like, its size; the SurfaceView takes care of placing the surface at the correct location on the screen

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed. The view hierarchy will take care of correctly compositing with the Surface any siblings of the SurfaceView that would normally appear on top of it. This can be used to place overlays such as buttons on top of the Surface, though note however that it can have an impact on performance since a full alpha-blended composite will be performed each time the Surface changes.

Access to the underlying surface is provided via the SurfaceHolder interface, which can be retrieved by calling getHolder().

The Surface will be created for you while the SurfaceView's window is visible; you should implement surfaceCreated(SurfaceHolder) and surfaceDestroyed(SurfaceHolder) to discover when the Surface is created and destroyed as the window is shown and hidden.

One of the purposes of this class is to provide a surface in which a secondary thread can render in to the screen. If you are going to use it this way, you need to be aware of some threading semantics:

•All SurfaceView and SurfaceHolder.Callback methods will be called from the thread running the SurfaceView's window (typically the main thread of the application). They thus need to correctly synchronize with any state that is also touched by the drawing thread. 
•You must ensure that the drawing thread only touches the underlying Surface while it is valid -- between SurfaceHolder.Callback.surfaceCreated() and SurfaceHolder.Callback.surfaceDestroyed(). 
對應的中文翻譯
SurfaceView是檢視(View)的繼承類,這個檢視裡內嵌了一個專門用於繪製的Surface。你可以控制這個Surface的格式和尺寸。Surfaceview控制這個Surface的繪製位置。
        surface是縱深排序(Z-ordered)的,這表明它總在自己所在視窗的後面。surfaceview提供了一個可見區域,只有在這個可見區域內 的surface部分內容才可見,可見區域外的部分不可見。surface的排版顯示受到檢視層級關係的影響,它的兄弟檢視結點會在頂端顯示。這意味者 surface的內容會被它的兄弟檢視遮擋,這一特性可以用來放置遮蓋物(overlays)(例如,文字和按鈕等控制元件)。注意,如果surface上面 有透明控制元件,那麼它的每次變化都會引起框架重新計算它和頂層控制元件的透明效果,這會影響效能。
        你可以通過SurfaceHolder介面訪問這個surface,getHolder()方法可以得到這個介面。
        surfaceview變得可見時,surface被建立;surfaceview隱藏前,surface被銷燬。這樣能節省資源。如果你要檢視 surface被建立和銷燬的時機,可以過載surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)。
        surfaceview的核心在於提供了兩個執行緒:UI執行緒和渲染執行緒。這裡應注意:
        1> 所有SurfaceView和SurfaceHolder.Callback的方法都應該在UI執行緒裡呼叫,一般來說就是應用程式主執行緒。渲染執行緒所要訪問的各種變數應該作同步處理。
        2> 由於surface可能被銷燬,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效,所以要確保渲染執行緒訪問的是合法有效的surface。

 

接下來呢,說說自己對它的理解
1、定義

可以直接從記憶體或者DMA等硬體介面取得影象資料,是個非常重要的繪圖容器。

它的特性是:可以在主執行緒之外的執行緒中向螢幕繪圖上。這樣可以避免畫圖任務繁重的時候造成主執行緒阻塞,從而提高了程式的反應速度。在遊戲開發中多用到SurfaceView,遊戲中的背景、人物、動畫等等儘量在畫布canvas中畫出。

2、實現

首先繼承SurfaceView並實現SurfaceHolder.Callback介面
使用介面的原因:因為使用SurfaceView 有一個原則,所有的繪圖工作必須得在Surface 被建立之後才能開始(Surface—表面,這個概念在 圖形程式設計中常常被提到。基本上我們可以把它當作視訊記憶體的一個對映,寫入到Surface 的內容
                      可以被直接複製到視訊記憶體從而顯示出來,這使得顯示速度會非常快),而在Surface 被銷燬之前必須結束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理程式碼的邊界。

需要重寫的方法

 (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}

     //在surface的大小發生改變時激發

 (2)public void surfaceCreated(SurfaceHolder holder){}

     //在建立時激發,一般在這裡呼叫畫圖的執行緒。

 (3)public void surfaceDestroyed(SurfaceHolder holder) {}

     //銷燬時激發,一般在這裡將畫圖的執行緒停止、釋放。

整個過程:繼承SurfaceView並實現SurfaceHolder.Callback介面 ----> SurfaceView.getHolder()獲得SurfaceHolder物件 ---->SurfaceHolder.addCallback(callback)新增回撥函式---->SurfaceHolder.lockCanvas()獲得Canvas物件並鎖定畫布----> Canvas繪畫 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結束鎖定畫圖,並提交改變,將圖形顯示。


3、SurfaceHolder
這裡用到了一個類SurfaceHolder,可以把它當成surface的控制器,用來操縱surface。處理它的Canvas上畫的效果和動畫,控制表面,大小,畫素等。
幾個需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 給SurfaceView當前的持有者一個回撥物件。
(2)、abstract Canvas lockCanvas();
// 鎖定畫布,一般在鎖定後就可以通過其返回的畫布物件Canvas,在其上面畫圖等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 鎖定畫布的某個區域進行畫圖等..因為畫完圖後,會呼叫下面的unlockCanvasAndPost來改變顯示內容。
// 相對部分記憶體要求比較高的遊戲來說,可以不用重畫dirty外的其它區域的畫素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 結束鎖定畫圖,並提交改變。
4、例項

這裡的例子實現了一個矩形和一個計時器

package xl.test;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class ViewTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyView(this));
    }
    //檢視內部類
    class MyView extends SurfaceView implements SurfaceHolder.Callback
    {
        private SurfaceHolder holder;
        private MyThread myThread; 
        public MyView(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
            holder = this.getHolder();
            holder.addCallback(this);
            myThread = new MyThread(holder);//建立一個繪圖執行緒
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
            // TODO Auto-generated method stub
            
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            myThread.isRun = true;
            myThread.start();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // TODO Auto-generated method stub
            myThread.isRun = false;
        }
        
    }
    //執行緒內部類
    class MyThread extends Thread
    {
        private SurfaceHolder holder;
        public boolean isRun ;
        public  MyThread(SurfaceHolder holder)
        {
            this.holder =holder; 
            isRun = true;
        }
        @Override
        public void run()
        {
            int count = 0;
            while(isRun)
            {
                Canvas c = null;
                try
                {
                    synchronized (holder)
                    {
                        c = holder.lockCanvas();//鎖定畫布,一般在鎖定後就可以通過其返回的畫布物件Canvas,在其上面畫圖等操作了。
                        c.drawColor(Color.BLACK);//設定畫布背景顏色
                        Paint p = new Paint(); //建立畫筆
                        p.setColor(Color.WHITE);
                        Rect r = new Rect(100, 50, 300, 250);
                        c.drawRect(r, p);
                        c.drawText("這是第"+(count++)+"秒", 100, 310, p);
                        Thread.sleep(1000);//睡眠時間為1秒
                    }
                }
                catch (Exception e) {
                    // TODO: handle exception
                    e.printStackTrace();
                }
                finally
                {
                    if(c!= null)
                    {
                        holder.unlockCanvasAndPost(c);//結束鎖定畫圖,並提交改變。

                    }
                }
            }
        }
    }
}

轉自:http://www.cnblogs.com/xuling/archive/2011/06/06/android.html

相關文章