歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Android開發之SurfaceView

Android開發之SurfaceView

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