歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android開發之SurfaceView

在學習視頻播放和簡單照相機的制作的時候,用到了SurfaceView這個類。那麼這個類是干什麼的呢?如果不用這個類,用View類行不行呢?這個看起來是不行的。

如果做游戲或者視頻相關開發的時候,將會用到SurfaceView。對於SurfaceView,首先需要了解一下它自己的位置:

extends View

java.lang.Object

   ↳

Android.view.View

 

   ↳

android.view.SurfaceView

由手冊可知:

SurfaceView是View類的繼承類,這個View裡內嵌了一個專門用於繪制的Surface,這個可以類似的理解成為一個在View裡的Canvas。你可以控制這個Surface的格式和尺寸。Surfaceview類則控制這個Surface在屏幕上的正確位置。

《Android高級編程》裡這樣說:

在一般情況下,應用程序的View都是在相同的GUI線程中繪制的。這個主應用程序線程同時也用來處理所有的用戶交互(例如按鈕單擊或者文本輸入)。

對於一個View的onDraw()方法,不能夠滿足將其移動到後台線程中去。因為從後台線程修改一個GUI元素會被顯式地禁止的。

當需要快速地更新View的UI,或者當前渲染代碼阻塞GUI線程的時間過長的時候,SurfaceView就是解決上述問題的最佳選擇。SurfaceView封裝了一個Surface對象,而不是Canvas。這一點很重要,因為Surface可以使用後台線程繪制。對於那些資源敏感的操作,或者那些要求快速更新或者高速幀率的地方,例如使用3D圖形,創建游戲,或者實時預覽攝像頭,這一點特別有用。

       1. 何時應該使用SurfaceView

       SurfaceView使用的方式與任何View所派生的類都是完全相同的。可以像其他View那樣應用動畫,並把它們放到布局中。

       SurfaceView封裝的Surface支持所有標准的Canvas方法進行繪圖,同時也支持完全的OpenGL ES 庫。

       使用OpenGL,你可以在Surface上繪制任何支持2D或者3D對象,與在2D畫布上模擬相同的效果相比,這種方法可以依靠硬件加速(可用的時候)來極大地提高性能。

       對於顯示動態的3D圖像來說,例如,那些使用Google Earth 功能的應用程序,或者那些提供沉浸體驗的交互式游戲,Surface特別有用。它還是實時顯示攝像頭預覽的最佳選擇。

2. 創建一個新的SurfaceView控件

創建一個新的SurfaceView控件需要創建一個新的擴展了SurfaceView的類,並實現SurfaceHolder.Callback。

SurfaceHolder回調可以在底層的Surface被創建和銷毀的時候通知View,並傳遞給它SurfaceHolder對象的引用,其中包含了當前有效的Surface。

一個典型的SurfaceView 設計模型包括一個由Thread所派生的類,它可以接收對當前的SurfaceHolder的引用,並獨立地更新它。

3. 使用SurfaceView創建3D控件

Android完全支持OpenGL ES 3D 渲染框架,其中包含了對設備的硬件加速的支持。SurfaceView控件提供了一個表面,可以在它上面渲染你的OpenGL場景。

那麼我們在使用的時候可以這樣使用:

被動更新畫面的。比如棋類,這種用view就好了。因為畫面的更新是依賴於 onTouch 來更新,可以直接使用 invalidate。 因為這種情況下,這一次Touch和下一次的Touch需要的時間比較長些,不會產生影響。

主動更新。比如一個人在一直跑動。這就需要一個單獨的thread不停的重繪人的狀態,避免阻塞main UI thread。所以顯然view不合適,需要surfaceView來控制。

 

可以直接從內存或硬件設備比如相機等取得圖像數據,是個非常重要的繪圖容器。

它的特性是:可以在主線程之外的線程中向屏幕繪圖。這樣可以避免畫圖任務繁重的時候造成主線程阻塞,從而提高了程序的反應速度。

如何去使用一個SurfaceView:

首先繼承SurfaceView並實現SurfaceHolder.Callback接口。因為使用SurfaceView 有一個原則,所有的繪圖工作必須得在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)結束鎖定畫圖,並提交改變,將圖形顯示。

關於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);
// 結束鎖定畫圖,並提交改變。

測試代碼如下:

  1. /* 
  2.  *  Android開發之SurfaceView 
  3.  *  SurfaceView01.java 
  4.  *  Created on: 2011-8-25 
  5.  *  Author: blueeagle 
  6.  *  Email: [email protected] 
  7.  */  
  8.   
  9. package com.blueeagle;  
  10.   
  11. import android.app.Activity;  
  12.  import android.content.Context;  
  13.  import android.graphics.Canvas;  
  14.  import android.graphics.Color;  
  15.  import android.graphics.Paint;  
  16.  import android.graphics.Rect;  
  17.  import android.os.Bundle;  
  18.  import android.view.SurfaceHolder;  
  19.  import android.view.SurfaceView;  
  20.    
  21. public class SurfaceView01 extends Activity {  
  22.      /** Called when the activity is first created. */  
  23.      @Override  
  24.      public void onCreate(Bundle savedInstanceState) {  
  25.          super.onCreate(savedInstanceState);  
  26.          setContentView(new MyView(this));  
  27.      }  
  28.      //視圖內部類   
  29.      class MyView extends SurfaceView implements SurfaceHolder.Callback  
  30.      {  
  31.          private SurfaceHolder holder;  
  32.          private MyThread myThread;   
  33.          public MyView(Context context) {  
  34.              super(context);  
  35.              // TODO Auto-generated constructor stub   
  36.              holder = this.getHolder();  
  37.              holder.addCallback(this);  
  38.              myThread = new MyThread(holder);//創建一個繪圖線程   
  39.          }  
  40.    
  41.          @Override  
  42.          public void surfaceChanged(SurfaceHolder holder, int format, int width,  
  43.                  int height) {  
  44.              // TODO Auto-generated method stub   
  45.                
  46.          }  
  47.    
  48.          @Override  
  49.          public void surfaceCreated(SurfaceHolder holder) {  
  50.              // TODO Auto-generated method stub   
  51.              myThread.isRun = true;  
  52.              myThread.start();  
  53.          }  
  54.    
  55.          @Override  
  56.          public void surfaceDestroyed(SurfaceHolder holder) {  
  57.              // TODO Auto-generated method stub   
  58.              myThread.isRun = false;  
  59.          }  
  60.            
  61.      }  
  62.      //線程內部類   
  63.      class MyThread extends Thread  
  64.      {  
  65.          private SurfaceHolder holder;  
  66.          public boolean isRun ;  
  67.          public  MyThread(SurfaceHolder holder)  
  68.          {  
  69.              this.holder =holder;   
  70.              isRun = true;  
  71.          }  
  72.          @Override  
  73.          public void run()  
  74.          {  
  75.              int count = 0;  
  76.              while(isRun)  
  77.              {  
  78.                  Canvas c = null;  
  79.                  try  
  80.                  {  
  81.                      synchronized (holder)  
  82.                      {  
  83.                          c = holder.lockCanvas();//鎖定畫布,一般在鎖定後就可以通過其返回的畫布對象Canvas,在其上面畫圖等操作了。   
  84.                          c.drawColor(Color.BLACK);//設置畫布背景顏色   
  85.                          Paint p = new Paint(); //創建畫筆   
  86.                          p.setColor(Color.WHITE);  
  87.                          Rect r = new Rect(10050300250);  
  88.                          c.drawRect(r, p);  
  89.                          c.drawText("這是第"+(count++)+"秒"100310, p);  
  90.                          Thread.sleep(1000);//睡眠時間為1秒   
  91.                      }  
  92.                  }  
  93.                  catch (Exception e) {  
  94.                      // TODO: handle exception   
  95.                      e.printStackTrace();  
  96.                  }  
  97.                  finally  
  98.                  {  
  99.                      if(c!= null)  
  100.                      {  
  101.                          holder.unlockCanvasAndPost(c);//結束鎖定畫圖,並提交改變。   
  102.    
  103.                      }  
  104.                  }  
  105.             }  
  106.          }  
  107.      }  
  108. }  
Copyright © Linux教程網 All Rights Reserved