Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 1 | page.title= Responding to Touch Events |
| 2 | parent.title=Displaying Graphics with OpenGL ES |
| 3 | parent.link=index.html |
| 4 | |
| 5 | trainingnavtop=true |
| 6 | previous.title=Adding Motion |
| 7 | previous.link=motion.html |
| 8 | |
| 9 | @jd:body |
| 10 | |
| 11 | <div id="tb-wrapper"> |
| 12 | <div id="tb"> |
| 13 | |
| 14 | <h2>This lesson teaches you to</h2> |
| 15 | <ol> |
| 16 | <li><a href="#listener">Setup a Touch Listener</a></li> |
| 17 | <li><a href="#angle">Expose the Rotation Angle</a></li> |
| 18 | <li><a href="#rotate">Apply Rotation</a></li> |
| 19 | </ol> |
| 20 | |
| 21 | <h2>You should also read</h2> |
| 22 | <ul> |
| 23 | <li><a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL</a></li> |
| 24 | </ul> |
| 25 | |
| 26 | <div class="download-box"> |
| 27 | <a href="http://developer.android.com/shareables/training/OpenGLES.zip" |
| 28 | class="button">Download the sample</a> |
| 29 | <p class="filename">OpenGLES.zip</p> |
| 30 | </div> |
| 31 | |
| 32 | </div> |
| 33 | </div> |
| 34 | |
| 35 | <p>Making objects move according to a preset program like the rotating triangle is useful for |
| 36 | getting some attention, but what if you want to have users interact with your OpenGL ES graphics? |
| 37 | The key to making your OpenGL ES application touch interactive is expanding your implementation of |
| 38 | {@link android.opengl.GLSurfaceView} to override the {@link |
| 39 | android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} to listen for touch events.</p> |
| 40 | |
| 41 | <p>This lesson shows you how to listen for touch events to let users rotate an OpenGL ES object.</p> |
| 42 | |
| 43 | |
| 44 | <h2 id="listener">Setup a Touch Listener</h2> |
| 45 | |
| 46 | <p>In order to make your OpenGL ES application respond to touch events, you must implement the |
| 47 | {@link android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} method in your |
| 48 | {@link android.opengl.GLSurfaceView} class. The example implementation below shows how to listen for |
| 49 | {@link android.view.MotionEvent#ACTION_MOVE MotionEvent.ACTION_MOVE} events and translate them to |
| 50 | an angle of rotation for a shape.</p> |
| 51 | |
| 52 | <pre> |
| 53 | @Override |
| 54 | public boolean onTouchEvent(MotionEvent e) { |
| 55 | // MotionEvent reports input details from the touch screen |
| 56 | // and other input controls. In this case, you are only |
| 57 | // interested in events where the touch position changed. |
| 58 | |
| 59 | float x = e.getX(); |
| 60 | float y = e.getY(); |
| 61 | |
| 62 | switch (e.getAction()) { |
| 63 | case MotionEvent.ACTION_MOVE: |
| 64 | |
| 65 | float dx = x - mPreviousX; |
| 66 | float dy = y - mPreviousY; |
| 67 | |
| 68 | // reverse direction of rotation above the mid-line |
| 69 | if (y > getHeight() / 2) { |
| 70 | dx = dx * -1 ; |
| 71 | } |
| 72 | |
| 73 | // reverse direction of rotation to left of the mid-line |
| 74 | if (x < getWidth() / 2) { |
| 75 | dy = dy * -1 ; |
| 76 | } |
| 77 | |
Joe Fernandez | feaaea5 | 2013-11-15 10:33:41 -0800 | [diff] [blame^] | 78 | mRenderer.setAngle( |
| 79 | mRenderer.getAngle() + |
| 80 | ((dx + dy) * TOUCH_SCALE_FACTOR); // = 180.0f / 320 |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 81 | requestRender(); |
| 82 | } |
| 83 | |
| 84 | mPreviousX = x; |
| 85 | mPreviousY = y; |
| 86 | return true; |
| 87 | } |
| 88 | </pre> |
| 89 | |
| 90 | <p>Notice that after calculating the rotation angle, this method calls {@link |
| 91 | android.opengl.GLSurfaceView#requestRender requestRender()} to tell the |
| 92 | renderer that it is time to render the frame. This approach is the most efficient in this example |
| 93 | because the frame does not need to be redrawn unless there is a change in the rotation. However, it |
| 94 | does not have any impact on efficiency unless you also request that the renderer only redraw when |
| 95 | the data changes using the {@link android.opengl.GLSurfaceView#setRenderMode setRenderMode()} |
| 96 | method, so make sure this line is uncommented in the renderer:</p> |
| 97 | |
| 98 | <pre> |
| 99 | public MyGLSurfaceView(Context context) { |
| 100 | ... |
| 101 | // Render the view only when there is a change in the drawing data |
| 102 | <strong>setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);</strong> |
| 103 | } |
| 104 | </pre> |
| 105 | |
| 106 | <h2 id="angle">Expose the Rotation Angle</h2> |
| 107 | |
| 108 | <p>The example code above requires that you expose the rotation angle through your renderer by |
| 109 | adding a public member. Since the renderer code is running on a separate thread from the main user |
| 110 | interface thread of your application, you must declare this public variable as {@code volatile}. |
| 111 | Here is the code to do that:</p> |
| 112 | |
| 113 | <pre> |
| 114 | public class MyGLRenderer implements GLSurfaceView.Renderer { |
| 115 | ... |
| 116 | public volatile float mAngle; |
| 117 | </pre> |
| 118 | |
| 119 | |
| 120 | <h2 id="rotate">Apply Rotation</h2> |
| 121 | |
| 122 | <p>To apply the rotation generated by touch input, comment out the code that generates an angle and |
| 123 | add {@code mAngle}, which contains the touch input generated angle:</p> |
| 124 | |
| 125 | <pre> |
| 126 | public void onDrawFrame(GL10 gl) { |
| 127 | ... |
Joe Fernandez | feaaea5 | 2013-11-15 10:33:41 -0800 | [diff] [blame^] | 128 | float[] scratch = new float[16]; |
| 129 | |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 130 | // Create a rotation for the triangle |
| 131 | // long time = SystemClock.uptimeMillis() % 4000L; |
| 132 | // float angle = 0.090f * ((int) time); |
| 133 | <strong>Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);</strong> |
| 134 | |
| 135 | // Combine the rotation matrix with the projection and camera view |
Joe Fernandez | feaaea5 | 2013-11-15 10:33:41 -0800 | [diff] [blame^] | 136 | // Note that the mMVPMatrix factor *must be first* in order |
| 137 | // for the matrix multiplication product to be correct. |
| 138 | Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0); |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 139 | |
| 140 | // Draw triangle |
Joe Fernandez | feaaea5 | 2013-11-15 10:33:41 -0800 | [diff] [blame^] | 141 | mTriangle.draw(scratch); |
Scott Main | 50e990c | 2012-06-21 17:14:39 -0700 | [diff] [blame] | 142 | } |
| 143 | </pre> |
| 144 | |
| 145 | <p>When you have completed the steps described above, run the program and drag your finger over the |
| 146 | screen to rotate the triangle:</p> |
| 147 | |
| 148 | <img src="{@docRoot}images/opengl/ogl-triangle-touch.png"> |
| 149 | <p class="img-caption"> |
| 150 | <strong>Figure 1.</strong> Triangle being rotated with touch input (circle shows touch |
| 151 | location).</p> |