An implementation of SurfaceView that uses the dedicated surface for displaying OpenGL rendering.
A GLSurfaceView provides the following features:
- Manages a surface, which is a special piece of memory that can be composited into the Android view system.
- Manages an EGL display, which enables OpenGL to render into a surface.
- Accepts a user-provided Renderer object that does the actual rendering.
- Renders on a dedicated thread to decouple rendering performance from the UI thread.
- Supports both on-demand and continuous rendering.
- Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
SurfaceView的實現,使用OpenGL進行渲染。它提供以下特征:
1)管理一個“界面”,是一塊內存,可以被組合進入Android的界面系統;
2)管理EGL顯示,可以允許OpenGL渲染;
3)接受用於提供的渲染器進行渲染;
4)獨立開辟線程進行渲染;
5)提供持續的渲染;
總之一句話,這個類很適合做渲染工作。
使用GLSurfaceView
通常會繼承GLSurfaceView,並重載一些和用戶輸入事件有關的方法。如果你不需要重載事件方法,GLSurfaceView也可以直接使用,你可以使用set方法來為該類提供自定義的行為。例如,GLSurfaceView的渲染被委托給渲染器在獨立的渲染線程里進行,這一點和普通視圖不一樣,setRenderer(Renderer)設置渲染器。
初始化GLSurfaceView
初始化過程其實僅需要你使用setRenderer(Renderer)設置一個渲染器(render)。當然,你也可以修改GLSurfaceView一些默認配置。
* setDebugFlags(int)
* setEGLConfigChooser(boolean)
* setEGLConfigChooser(EGLConfigChooser)
* setEGLConfigChooser(int, int, int, int, int, int)
* setGLWrapper(GLWrapper)
定制android.view.Surface
GLSurfaceView默認會創建像素格式為PixelFormat.RGB_565的surface。如果需要透明效果,調用getHolder().setFormat(PixelFormat.TRANSLUCENT)。透明(TRANSLUCENT)的surface的像素格式都是32位,每個色彩單元都是8位深度,像素格式是設備相關的,這意味着它可能是ARGB、RGBA或其它。
選擇EGL配置
Android設備往往支持多種EGL配置,可以使用不同數目的通道(channel),也可以指定每個通道具有不同數目的位(bits)深度。因此,在渲染器工作之前就應該指定EGL的配置。GLSurfaceView默認EGL配置的像素格式為RGB_656,16位的深度緩存(depth buffer),默認不開啟遮罩緩存(stencil buffer)。
如果你要選擇不同的EGL配置,請使用setEGLConfigChooser方法中的一種。
調試行為
你可以調用調試方法setDebugFlags(int)或setGLWrapper(GLSurfaceView.GLWrapper)來自定義GLSurfaceView一些行為。在setRenderer方法之前或之后都可以調用調試方法,不過最好是在之前調用,這樣它們能立即生效。
設置渲染器
總之,你必須調用setRenderer(GLSurfaceView.Renderer)來注冊一個GLSurfaceView.Renderer渲染器。渲染器負責真正的GL渲染工作。
渲染模式
渲染器設定之后,你可以使用setRenderMode(int)指定渲染模式是按需(on demand)還是連續(continuous)。默認是連續渲染。
Activity生命周期
Activity窗口暫停(pause)或恢復(resume)時,GLSurfaceView都會收到通知,此時它的onPause方法和onResume方法應該被調用。這樣做是為了讓GLSurfaceView暫停或恢復它的渲染線程,以便它及時釋放或重建OpenGL的資源。
事件處理
為了處理事件,一般都是繼承GLSurfaceView類並重載它的事件方法。但是由於GLSurfaceView是多線程操作,所以需要一些特殊的處理。由於渲染器在獨立的渲染線程里,你應該使用Java的跨線程機制跟渲染器通訊。queueEvent(Runnable)方法就是一種相對簡單的操作,例如下面的例子
1 class MyGLSurfaceView extends GLSurfaceView { 2 3 private MyRenderer mMyRenderer; 4 5 public void start() { 6 mMyRenderer = ...; 7 setRenderer(mMyRenderer); 8 } 9 10 public boolean onKeyDown(int keyCode, KeyEvent event) { 11 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 12 queueEvent(new Runnable() { 13 // This method will be called on the rendering 14 // thread: 15 public void run() { 16 mMyRenderer.handleDpadCenter(); 17 }}); 18 return true; 19 } 20 return super.onKeyDown(keyCode, event); 21 } 22 }
示例代碼:
1 import javax.microedition.khronos.egl.EGLConfig; 2 import javax.microedition.khronos.opengles.GL10; 3 import android.app.Activity; 4 import android.opengl.GLSurfaceView; 5 import android.os.Bundle; 6 public class ClearActivity extends Activity { 7 @Override 8 protected void onCreate(Bundle savedInstanceState) { 9 super.onCreate(savedInstanceState); 10 mGLView = new GLSurfaceView(this); 11 mGLView.setRenderer(new ClearRenderer()); 12 setContentView(mGLView); 13 } 14 @Override 15 protected void onPause() { 16 super.onPause(); 17 mGLView.onPause(); 18 } 19 @Override 20 protected void onResume() { 21 super.onResume(); 22 mGLView.onResume(); 23 } 24 private GLSurfaceView mGLView; 25 } 26 class ClearRenderer implements GLSurfaceView.Renderer { 27 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 28 // Do nothing special. 29 } 30 public void onSurfaceChanged(GL10 gl, int w, int h) { 31 gl.glViewport(0, 0, w, h); 32 } 33 public void onDrawFrame(GL10 gl) { 34 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 35 } 36 }
這個程序功能很簡單,每幀繪制時將屏幕設置成黑色。但它是一個完整的工作在Activity 生命周期中的 OpenGL 程序。當 activity 暫停時,它暫停渲染;當activity 繼續時,它繼續渲染。可以將這個程序用作非交互式的 demo 程序。可以在 ClearRenderer.onDrawFrame() 接口中增加 OpenGL 調用做很多的繪制。
GLSurfaceView.Render 接口有三個方法:
- onSurfaceCreated():該方法在渲染開始前調用,OpenGL ES 的繪制上下文被重建時也會被調用。當 activity 暫停時繪制上下文會丟失,當 activity 繼續時,繪制上下文會被重建。另外,創建長期存在的 OpenGL 資源(如texture)往往也在這里進行。
- onSurfaceChanged():當 surface 的尺寸發生改變時該方法被調用。往往在這里設置 viewport。若你的 camera 是固定的,也可以在這里設置 camera。
- onDrawFrame():每幀都通過該方法進行繪制。繪制時通常先調用 glClear 函數來清空 framebuffer,然后在調用 OpenGL ES 的起它的接口進行繪制。
若是開發一個交互型的應用(如游戲),通常需要子類化 GLSurfaceView,由此可以獲取輸入事件。下面有個例子:
1 import javax.microedition.khronos.egl.EGLConfig; 2 import javax.microedition.khronos.opengles.GL10; 3 import android.app.Activity; 4 import android.content.Context; 5 import android.opengl.GLSurfaceView; 6 import android.os.Bundle; 7 import android.view.MotionEvent; 8 public class ClearActivity extends Activity { 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 mGLView = new ClearGLSurfaceView(this); 13 setContentView(mGLView); 14 } 15 @Override 16 protected void onPause() { 17 super.onPause(); 18 mGLView.onPause(); 19 } 20 @Override 21 protected void onResume() { 22 super.onResume(); 23 mGLView.onResume(); 24 } 25 private GLSurfaceView mGLView; 26 } 27 class ClearGLSurfaceView extends GLSurfaceView { 28 public ClearGLSurfaceView(Context context) { 29 super(context); 30 mRenderer = new ClearRenderer(); 31 setRenderer(mRenderer); 32 } 33 public boolean onTouchEvent(final MotionEvent event) { 34 queueEvent(new Runnable(){ 35 public void run() { 36 mRenderer.setColor(event.getX() / getWidth(), 37 event.getY() / getHeight(), 1.0f); 38 }}); 39 return true; 40 } 41 ClearRenderer mRenderer; 42 } 43 class ClearRenderer implements GLSurfaceView.Renderer { 44 public void onSurfaceCreated(GL10 gl, EGLConfig config) { 45 // Do nothing special. 46 } 47 public void onSurfaceChanged(GL10 gl, int w, int h) { 48 gl.glViewport(0, 0, w, h); 49 } 50 public void onDrawFrame(GL10 gl) { 51 gl.glClearColor(mRed, mGreen, mBlue, 1.0f); 52 gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); 53 } 54 public void setColor(float r, float g, float b) { 55 mRed = r; 56 mGreen = g; 57 mBlue = b; 58 } 59 private float mRed; 60 private float mGreen; 61 private float mBlue; 62 } 63