与SurfaceView相比,TextureView并没有创建一个单独的Surface用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等。
另外,Textureview必须在硬件加速开启的窗口中。下面的例子演示了通过TextureView来创建一个opengl程序。
基于TextureView的程序,我们需要实现TextureView.SurfaceTextureListener这个接口,首先给出Activity的代码,在该类中,我们实现了此接口:
package com.fyj.test; import java.util.concurrent.atomic.AtomicBoolean; import android.app.Activity; import android.graphics.SurfaceTexture; import android.os.Bundle; import android.view.TextureView; import android.view.View; import android.view.Window; import android.view.WindowManager; public class TextureviewtestActivity extends Activity implements TextureView.SurfaceTextureListener{ private TextureView mTextureView; private Thread mProducerThread = null; private GLRendererImpl mRenderer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); mTextureView = new TextureView(this); mTextureView.setSurfaceTextureListener(this); mTextureView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);//隐藏虚拟按键,即navigator bar setContentView(mTextureView); mRenderer = new GLRendererImpl(this); } @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // TODO Auto-generated method stub mRenderer.setViewport(width, height); mProducerThread = new GLProducerThread(surface, mRenderer, new AtomicBoolean(true)); mProducerThread.start(); } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { // TODO Auto-generated method stub mProducerThread = null; return true; } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { // TODO Auto-generated method stub mRenderer.resize(width, height); } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // TODO Auto-generated method stub } }
package com.fyj.test; import java.util.concurrent.atomic.AtomicBoolean; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL; import android.graphics.SurfaceTexture; import android.opengl.GLUtils; public class GLProducerThread extends Thread { private AtomicBoolean mShouldRender; private SurfaceTexture mSurfaceTexture; private GLRenderer mRenderer; private EGL10 mEgl; private EGLDisplay mEglDisplay = EGL10.EGL_NO_DISPLAY; private EGLContext mEglContext = EGL10.EGL_NO_CONTEXT; private EGLSurface mEglSurface = EGL10.EGL_NO_SURFACE; private GL mGL; private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; private static final int EGL_OPENGL_ES2_BIT = 4; public interface GLRenderer { public void drawFrame(); } public GLProducerThread(SurfaceTexture surfaceTexture, GLRenderer renderer, AtomicBoolean shouldRender) { mSurfaceTexture = surfaceTexture; mRenderer = renderer; mShouldRender = shouldRender; } private void initGL() { mEgl = (EGL10)EGLContext.getEGL(); mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); if (mEglDisplay == EGL10.EGL_NO_DISPLAY) { throw new RuntimeException("eglGetdisplay failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); } int []version = new int[2]; if (!mEgl.eglInitialize(mEglDisplay, version)) { throw new RuntimeException("eglInitialize failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); } int []configAttribs = { EGL10.EGL_BUFFER_SIZE, 32, EGL10.EGL_ALPHA_SIZE, 8, EGL10.EGL_BLUE_SIZE, 8, EGL10.EGL_GREEN_SIZE, 8, EGL10.EGL_RED_SIZE, 8, EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, EGL10.EGL_NONE }; int []numConfigs = new int[1]; EGLConfig []configs = new EGLConfig[1]; if (!mEgl.eglChooseConfig(mEglDisplay, configAttribs, configs, 1, numConfigs)) { throw new RuntimeException("eglChooseConfig failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); } int []contextAttribs = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE }; mEglContext = mEgl.eglCreateContext(mEglDisplay, configs[0], EGL10.EGL_NO_CONTEXT, contextAttribs); mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, configs[0], mSurfaceTexture, null); if (mEglSurface == EGL10.EGL_NO_SURFACE || mEglContext == EGL10.EGL_NO_CONTEXT) { int error = mEgl.eglGetError(); if (error == EGL10.EGL_BAD_NATIVE_WINDOW) { throw new RuntimeException("eglCreateWindowSurface returned EGL_BAD_NATIVE_WINDOW. " ); } throw new RuntimeException("eglCreateWindowSurface failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); } if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) { throw new RuntimeException("eglMakeCurrent failed : " + GLUtils.getEGLErrorString(mEgl.eglGetError())); } mGL = mEglContext.getGL(); } private void destoryGL() { mEgl.eglDestroyContext(mEglDisplay, mEglContext); mEgl.eglDestroySurface(mEglDisplay, mEglSurface); mEglContext = EGL10.EGL_NO_CONTEXT; mEglSurface = EGL10.EGL_NO_SURFACE; } public void run() { initGL(); if (mRenderer != null) { ((GLRendererImpl)mRenderer).initGL(); } while (mShouldRender != null && mShouldRender.get() != false) { if (mRenderer != null) mRenderer.drawFrame(); mEgl.eglSwapBuffers(mEglDisplay, mEglSurface); try { sleep(5); } catch(InterruptedException e) { } } destoryGL(); } }
package com.fyj.test; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLES20; import android.opengl.GLUtils; import android.util.Log; import com.fyj.test.GLProducerThread.GLRenderer; public class GLRendererImpl implements GLRenderer { private int mProgramObject; private int mWidth; private int mHeight; private FloatBuffer mVertices; private ShortBuffer mTexCoords; private Context mContext; private int mTexID; private static String TAG = "GLRendererImpl"; private final float[] mVerticesData = { -0.5f, -0.5f, 0, 0.5f, -0.5f, 0, -0.5f, 0.5f, 0, 0.5f, 0.5f, 0 }; private final short[] mTexCoordsData = {0, 1, 1, 1, 0, 0, 1, 0}; public GLRendererImpl(Context ctx) { mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mVertices.put(mVerticesData).position(0); mTexCoords = ByteBuffer.allocateDirect(mTexCoordsData.length * 2) .order(ByteOrder.nativeOrder()).asShortBuffer(); mTexCoords.put(mTexCoordsData).position(0); mContext = ctx; } public void setViewport(int width, int height) { mWidth = width; mHeight = height; } public void initGL() { comipleAndLinkProgram(); loadTexture(); GLES20.glClearColor(0, 0, 0, 0); } public void resize(int width, int height) { mWidth = width; mHeight = height; } @Override public void drawFrame() { // TODO Auto-generated method stub GLES20.glViewport(0, 0, mWidth, mHeight); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mProgramObject); GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 0, mVertices); GLES20.glEnableVertexAttribArray(0); GLES20.glVertexAttribPointer(1, 2, GLES20.GL_SHORT, false, 0, mTexCoords); GLES20.glEnableVertexAttribArray(1); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexID); int loc = GLES20.glGetUniformLocation(mProgramObject, "u_Texture"); GLES20.glUniform1f(loc, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); Log.i("GLRendererImpl", "drawing..." + mWidth); } private void loadTexture() { Bitmap b = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_launcher); if (b != null) { int []texID = new int[1]; GLES20.glGenTextures(1, texID, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texID[0]); mTexID = texID[0]; GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, b, 0); b.recycle(); } } private int loadShader(int shaderType, String shaderSource) { int shader; int[] compiled = new int[1]; // Create the shader object shader = GLES20.glCreateShader(shaderType); if (shader == 0) return 0; // Load the shader source GLES20.glShaderSource(shader, shaderSource); // Compile the shader GLES20.glCompileShader(shader); // Check the compile status GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); return 0; } return shader; } private void comipleAndLinkProgram() { String vShaderStr = "attribute vec4 a_position; \n" +"attribute vec2 a_texCoords; \n" +"varying vec2 v_texCoords; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position; \n" +" v_texCoords = a_texCoords; \n" + "} \n"; String fShaderStr = "precision mediump float; \n" +"uniform sampler2D u_Texture; \n" +"varying vec2 v_texCoords; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D(u_Texture, v_texCoords) ;\n" + "} \n"; int vertexShader; int fragmentShader; int programObject; int[] linked = new int[1]; // Load the vertex/fragment shaders vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vShaderStr); fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fShaderStr); // Create the program object programObject = GLES20.glCreateProgram(); if (programObject == 0) return ; GLES20.glAttachShader(programObject, vertexShader); GLES20.glAttachShader(programObject, fragmentShader); // Bind vPosition to attribute 0 GLES20.glBindAttribLocation(programObject, 0, "a_position"); GLES20.glBindAttribLocation(programObject, 1, "a_texCoords"); // Link the program GLES20.glLinkProgram(programObject); // Check the link status GLES20.glGetProgramiv(programObject, GLES20.GL_LINK_STATUS, linked, 0); if (linked[0] == 0) { Log.e(TAG, "Error linking program:"); Log.e(TAG, GLES20.glGetProgramInfoLog(programObject)); GLES20.glDeleteProgram(programObject); return ; } mProgramObject = programObject; } }