Home | History | Annotate | Download | only in articles
      1 page.title=Introducing GLSurfaceView
      2 @jd:body
      3 
      4 
      5 <p>The {@link android android.opengl.GLSurfaceView} class makes it 
      6 easier for you to use OpenGL ES rendering in your applications by:</p>
      7 
      8 <ul>
      9 <li>Providing the glue code to connect OpenGL ES to the {@link
     10 android.view.View} system.</li>
     11 <li>Providing the glue code to make OpenGL ES work with the {@link
     12 android.app.Activity} life-cycle.</li>
     13 <li>Making it easy to choose an appropriate frame buffer pixel format.</li>
     14 <li>Creating and managing a separate rendering thread, to enable smooth
     15 animation.</li>
     16 <li>Providing easy-to-use debugging tools for tracing OpenGL ES API calls and
     17 checking for errors.</li>
     18 </ul>
     19 
     20 <p>GLSurfaceView is a good base for building an application that uses OpenGL ES
     21 for part or all of its rendering. A 2D or 3D action game would be a good
     22 candidate, as would a 2D or 3D data visualization application such as <a
     23 href="http://www.youtube.com/watch?v=4PRfVKzuUJ4&fmt=18" title="Google Maps
     24 StreetView">Google Maps StreetView</a>.</p>
     25 
     26 <h3>A simple GLSurfaceView application</h3>
     27 
     28 <p>Here's the source code to the simplest possible OpenGL ES application:</p>
     29 
     30 <pre>package com.example.android.apis.graphics;
     31 
     32 import javax.microedition.khronos.egl.EGLConfig;
     33 import javax.microedition.khronos.opengles.GL10;
     34 
     35 import android.app.Activity;
     36 import android.opengl.GLSurfaceView;
     37 import android.os.Bundle;
     38 
     39 public class ClearActivity extends Activity {
     40     &#64;Override
     41     protected void onCreate(Bundle savedInstanceState) {
     42         super.onCreate(savedInstanceState);
     43         mGLView = new GLSurfaceView(this);
     44         mGLView.setRenderer(new ClearRenderer());
     45         setContentView(mGLView);
     46     }
     47 
     48     &#64;Override
     49     protected void onPause() {
     50         super.onPause();
     51         mGLView.onPause();
     52     }
     53 
     54     &#64;Override
     55     protected void onResume() {
     56         super.onResume();
     57         mGLView.onResume();
     58     }
     59 
     60     private GLSurfaceView mGLView;
     61 }
     62 
     63 class ClearRenderer implements GLSurfaceView.Renderer {
     64     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
     65         // Do nothing special.
     66     }
     67 
     68     public void onSurfaceChanged(GL10 gl, int w, int h) {
     69         gl.glViewport(0, 0, w, h);
     70     }
     71 
     72     public void onDrawFrame(GL10 gl) {
     73         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
     74     }
     75 }</pre>
     76 
     77 <p>This program doesn't do much: it clears the screen to black on every frame.
     78 But it is a complete OpenGL application that correctly implements the
     79 Android activity life-cycle. It pauses rendering when the activity is
     80 paused, and resumes it when the activity is resumed. You could use this
     81 application as the basis for non-interactive demonstration programs.
     82 Just add more OpenGL calls to the <code>ClearRenderer.onDrawFrame()</code> method.
     83 Notice that you don't even need to subclass the <code>GLSurfaceView</code> view.</p>
     84 
     85 <p>The {@link android.opengl.GLSurfaceView.Renderer} interface has three methods:</p>
     86 
     87 <ul>
     88 <li>The
     89 <code>onSurfaceCreated()</code> method is called at the start of rendering, and
     90 whenever the OpenGL ES drawing context has to be recreated. (The
     91 drawing context is typically lost and recreated when the activity is
     92 paused and resumed.) <code>OnSurfaceCreated()</code> is a good place to create
     93 long-lived OpenGL resources such as textures.</li>
     94 <li>The <code>onSurfaceChanged()</code>
     95 method is called when the surface changes size. It's a good place to
     96 set your OpenGL viewport. You may also want to set your camera here, if
     97 it's a fixed camera that doesn't move around the scene.</li>
     98 <li>The <code>onDrawFrame()</code> method is called every frame, and is
     99 responsible for drawing the scene. You would typically start by calling
    100 <code>glClear</code> to clear the framebuffer, followed by other OpenGL ES calls
    101 to draw the current scene.</li>
    102 </ul>
    103 
    104 <h3>How about user input?</h3>
    105 
    106 <p>If you want an interactive application (such as a game), you will typically
    107 subclass <code>GLSurfaceView</code>, because that's an easy way of obtaining
    108 input events. Here's a slightly longer example showing how to do that:</p>
    109 
    110 <pre>package com.google.android.ClearTest;
    111 
    112 import javax.microedition.khronos.egl.EGLConfig;
    113 import javax.microedition.khronos.opengles.GL10;
    114 
    115 import android.app.Activity;
    116 import android.content.Context;
    117 import android.opengl.GLSurfaceView;
    118 import android.os.Bundle;
    119 import android.view.MotionEvent;
    120 
    121 public class ClearActivity extends Activity {
    122     &#64;Override
    123     protected void onCreate(Bundle savedInstanceState) {
    124         super.onCreate(savedInstanceState);
    125         mGLView = new ClearGLSurfaceView(this);
    126         setContentView(mGLView);
    127     }
    128 
    129     &#64;Override
    130     protected void onPause() {
    131         super.onPause();
    132         mGLView.onPause();
    133     }
    134 
    135     &#64;Override
    136     protected void onResume() {
    137         super.onResume();
    138         mGLView.onResume();
    139     }
    140 
    141     private GLSurfaceView mGLView;
    142 }
    143 
    144 class ClearGLSurfaceView extends GLSurfaceView {
    145     public ClearGLSurfaceView(Context context) {
    146         super(context);
    147         mRenderer = new ClearRenderer();
    148         setRenderer(mRenderer);
    149     }
    150 
    151     public boolean onTouchEvent(final MotionEvent event) {
    152         queueEvent(new Runnable(){
    153             public void run() {
    154                 mRenderer.setColor(event.getX() / getWidth(),
    155                         event.getY() / getHeight(), 1.0f);
    156             }});
    157             return true;
    158         }
    159 
    160         ClearRenderer mRenderer;
    161 }
    162 
    163 class ClearRenderer implements GLSurfaceView.Renderer {
    164     public void onSurfaceCreated(GL10 gl, EGLConfig config) {
    165         // Do nothing special.
    166     }
    167 
    168     public void onSurfaceChanged(GL10 gl, int w, int h) {
    169         gl.glViewport(0, 0, w, h);
    170     }
    171 
    172     public void onDrawFrame(GL10 gl) {
    173         gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
    174         gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
    175     }
    176 
    177     public void setColor(float r, float g, float b) {
    178         mRed = r;
    179         mGreen = g;
    180         mBlue = b;
    181     }
    182 
    183     private float mRed;
    184     private float mGreen;
    185     private float mBlue;
    186 }</pre>
    187 
    188 <p>This application clears the screen for every frame. When you tap on the
    189 screen, it sets the clear color based on the (x,y) coordinates of your touch
    190 event. Note the use of <code>queueEvent()</code> in
    191 <code>ClearGLSurfaceView.onTouchEvent()</code>. The <code>queueEvent()</code>
    192 method is used to safely communicate between the UI thread and the rendering
    193 thread. If you prefer, you can use some other Java cross-thread communication
    194 technique, such as synchronized methods on the <code>Renderer</code> class 
    195 itself. However, queueing events is often the simplest way of dealing with 
    196 cross-thread communication.</p>
    197 
    198 <h3>Other GLSurfaceView samples</h3>
    199 
    200 <p>Tired
    201 of just clearing the screen? You can find more interesting samples in
    202 the API Demos sample included in the Android SDK. All the OpenGL ES samples have been
    203 converted to use the <code>GLSurfaceView</code> view:</p>
    204 
    205 <ul>
    206 <li>GLSurfaceView - a spinning triangle</li>
    207 <li>Kube - a cube puzzle demo</li>
    208 <li>Translucent GLSurfaceView - shows how to display 3D graphics on a translucent background</li>
    209 <li>Textured Triangle - shows how to draw a textured 3D triangle</li>
    210 <li>Sprite Text - shows how to draw text into a texture and then composite it into a 3D scene</li>
    211 <li>Touch Rotate - shows how to rotate a 3D object in response to user input.</li>
    212 </ul>
    213 
    214 <h3>Choosing a surface</h3>
    215 
    216 <p><code>GLSurfaceView</code>
    217 helps you choose the type of surface to render to. Different Android
    218 devices support different types of surfaces, with no common subset.
    219 This makes it tricky problem to choose the best available surface on
    220 each device. </p>
    221 
    222 <p>By default, <code>GLSurfaceView</code> tries to find a surface that's as
    223 close as possible to a 16-bit RGB frame buffer with a 16-bit depth
    224 buffer. Depending upon your application's needs you may want to change
    225 this behavior. For example, the Translucent GLSurfaceView sample needs
    226 an Alpha channel in order to render translucent data. <code>GLSurfaceView</code>
    227 provides an overloaded <code>setEGLSurfaceChooser()</code> method to give 
    228 you control over which surface type is chosen:</p>
    229 
    230 <dl>
    231 <dt><code>setEGLConfigChooser(boolean needDepth)</code></dt>
    232 <dd>Choose a config that's closest to R5G6B5 with or without a 16-bit framebuffer</dd>
    233 <dt><code>setEGLConfigChooser(int redSize, int greenSize,int blueSize, 
    234 int alphaSize,int depthSize, int stencilSize)</code></dt>
    235 <dd>Choose the config with the fewest number of bits per pixel that has at least
    236 as many bits-per-channel as specified in the constructor.</dd>
    237 <dt><code>setEGLConfigChooser(EGLConfigChooser configChooser)</code></dt>
    238 <dd>Allow total control over choosing a configuration. You pass in your own
    239 implementation of <code>EGLConfigChooser</code>, which gets to inspect the
    240 device's capabilities and choose a configuration.</dd>
    241 </dl>
    242 
    243 <h3>Continuous rendering versus render-when-dirty</h3>
    244 
    245 <p>Most 3D applications, such as games or simulations, are continuously
    246 animated. But some 3D applications are more reactive: they wait passively until
    247 the user does something, and then react to it. For those types of applications,
    248 the default <code>GLSurfaceView</code> behavior of continuously redrawing the
    249 screen is a waste of time. If you are developing a reactive application, you can
    250 call <code>GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY)</code>, which
    251 turns off the continuous animation. Then you call
    252 <code>GLSurfaceView.requestRender()</code> whenever you want to re-render.</p>
    253 
    254 <h3>Help With debugging</h3>
    255 
    256 <p><code>GLSurfaceView</code> has a handy built-in feature for debugging OpenGL ES
    257 applications: the <code>GLSurfaceView.setDebugFlags()</code> method can be used
    258 to enable logging and/or error checking your OpenGL ES calls. Call this method
    259 in your <code>GLSurfaceView</code>'s constructor, before calling
    260 <code>setRenderer()</code>:</p>
    261 
    262 <pre>public ClearGLSurfaceView(Context context) {
    263     super(context);
    264     // Turn on error-checking and logging
    265     setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
    266     mRenderer = new ClearRenderer();
    267     setRenderer(mRenderer);
    268 }</pre>