|  | page.title= Responding to Touch Events | 
|  | parent.title=Displaying Graphics with OpenGL ES | 
|  | parent.link=index.html | 
|  |  | 
|  | trainingnavtop=true | 
|  | previous.title=Adding Motion | 
|  | previous.link=motion.html | 
|  |  | 
|  | @jd:body | 
|  |  | 
|  | <div id="tb-wrapper"> | 
|  | <div id="tb"> | 
|  |  | 
|  | <h2>This lesson teaches you to</h2> | 
|  | <ol> | 
|  | <li><a href="#listener">Setup a Touch Listener</a></li> | 
|  | <li><a href="#angle">Expose the Rotation Angle</a></li> | 
|  | <li><a href="#rotate">Apply Rotation</a></li> | 
|  | </ol> | 
|  |  | 
|  | <h2>You should also read</h2> | 
|  | <ul> | 
|  | <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> | 
|  | </ul> | 
|  |  | 
|  | <div class="download-box"> | 
|  | <a href="http://developer.android.com/shareables/training/OpenGLES.zip" | 
|  | class="button">Download the sample</a> | 
|  | <p class="filename">OpenGLES.zip</p> | 
|  | </div> | 
|  |  | 
|  | </div> | 
|  | </div> | 
|  |  | 
|  | <p>Making objects move according to a preset program like the rotating triangle is useful for | 
|  | getting some attention, but what if you want to have users interact with your OpenGL ES graphics? | 
|  | The key to making your OpenGL ES application touch interactive is expanding your implementation of | 
|  | {@link android.opengl.GLSurfaceView} to override the {@link | 
|  | android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} to listen for touch events.</p> | 
|  |  | 
|  | <p>This lesson shows you how to listen for touch events to let users rotate an OpenGL ES object.</p> | 
|  |  | 
|  |  | 
|  | <h2 id="listener">Setup a Touch Listener</h2> | 
|  |  | 
|  | <p>In order to make your OpenGL ES application respond to touch events, you must implement the | 
|  | {@link android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} method in your | 
|  | {@link android.opengl.GLSurfaceView} class. The example implementation below shows how to listen for | 
|  | {@link android.view.MotionEvent#ACTION_MOVE MotionEvent.ACTION_MOVE} events and translate them to | 
|  | an angle of rotation for a shape.</p> | 
|  |  | 
|  | <pre> | 
|  | @Override | 
|  | public boolean onTouchEvent(MotionEvent e) { | 
|  | // MotionEvent reports input details from the touch screen | 
|  | // and other input controls. In this case, you are only | 
|  | // interested in events where the touch position changed. | 
|  |  | 
|  | float x = e.getX(); | 
|  | float y = e.getY(); | 
|  |  | 
|  | switch (e.getAction()) { | 
|  | case MotionEvent.ACTION_MOVE: | 
|  |  | 
|  | float dx = x - mPreviousX; | 
|  | float dy = y - mPreviousY; | 
|  |  | 
|  | // reverse direction of rotation above the mid-line | 
|  | if (y > getHeight() / 2) { | 
|  | dx = dx * -1 ; | 
|  | } | 
|  |  | 
|  | // reverse direction of rotation to left of the mid-line | 
|  | if (x < getWidth() / 2) { | 
|  | dy = dy * -1 ; | 
|  | } | 
|  |  | 
|  | mRenderer.setAngle( | 
|  | mRenderer.getAngle() + | 
|  | ((dx + dy) * TOUCH_SCALE_FACTOR);  // = 180.0f / 320 | 
|  | requestRender(); | 
|  | } | 
|  |  | 
|  | mPreviousX = x; | 
|  | mPreviousY = y; | 
|  | return true; | 
|  | } | 
|  | </pre> | 
|  |  | 
|  | <p>Notice that after calculating the rotation angle, this method calls {@link | 
|  | android.opengl.GLSurfaceView#requestRender requestRender()} to tell the | 
|  | renderer that it is time to render the frame. This approach is the most efficient in this example | 
|  | because the frame does not need to be redrawn unless there is a change in the rotation. However, it | 
|  | does not have any impact on efficiency unless you also request that the renderer only redraw when | 
|  | the data changes using the {@link android.opengl.GLSurfaceView#setRenderMode setRenderMode()} | 
|  | method, so make sure this line is uncommented in the renderer:</p> | 
|  |  | 
|  | <pre> | 
|  | public MyGLSurfaceView(Context context) { | 
|  | ... | 
|  | // Render the view only when there is a change in the drawing data | 
|  | <strong>setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);</strong> | 
|  | } | 
|  | </pre> | 
|  |  | 
|  | <h2 id="angle">Expose the Rotation Angle</h2> | 
|  |  | 
|  | <p>The example code above requires that you expose the rotation angle through your renderer by | 
|  | adding a public member. Since the renderer code is running on a separate thread from the main user | 
|  | interface thread of your application, you must declare this public variable as {@code volatile}. | 
|  | Here is the code to do that:</p> | 
|  |  | 
|  | <pre> | 
|  | public class MyGLRenderer implements GLSurfaceView.Renderer { | 
|  | ... | 
|  | public volatile float mAngle; | 
|  | </pre> | 
|  |  | 
|  |  | 
|  | <h2 id="rotate">Apply Rotation</h2> | 
|  |  | 
|  | <p>To apply the rotation generated by touch input, comment out the code that generates an angle and | 
|  | add {@code mAngle}, which contains the touch input generated angle:</p> | 
|  |  | 
|  | <pre> | 
|  | public void onDrawFrame(GL10 gl) { | 
|  | ... | 
|  | float[] scratch = new float[16]; | 
|  |  | 
|  | // Create a rotation for the triangle | 
|  | // long time = SystemClock.uptimeMillis() % 4000L; | 
|  | // float angle = 0.090f * ((int) time); | 
|  | <strong>Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);</strong> | 
|  |  | 
|  | // Combine the rotation matrix with the projection and camera view | 
|  | // Note that the mMVPMatrix factor *must be first* in order | 
|  | // for the matrix multiplication product to be correct. | 
|  | Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); | 
|  |  | 
|  | // Draw triangle | 
|  | mTriangle.draw(scratch); | 
|  | } | 
|  | </pre> | 
|  |  | 
|  | <p>When you have completed the steps described above, run the program and drag your finger over the | 
|  | screen to rotate the triangle:</p> | 
|  |  | 
|  | <img src="{@docRoot}images/opengl/ogl-triangle-touch.png"> | 
|  | <p class="img-caption"> | 
|  | <strong>Figure 1.</strong> Triangle being rotated with touch input (circle shows touch | 
|  | location).</p> |