http://blog.csdn.net/u010949962/article/details/41865777

最近要把Android 原生的View渲染到OpenGL GLSurfaceView中,起初想到的是截图的方法,也就是把View截取成bitmap后,再把Bitmap渲染到OpenGL中;但是明显这种方法是不可行的,面对一些高速动态更新的View,只有不停的对view 进行截图才能渲染出原生View的效果。

通过大量的Google终于在国外的网站找到了一个做过类似的先例(链接:http://www.felixjones.co.uk/neo%20website/Android_View/)。不过经过测试该方法只能渲染直接父类为View的view,也就是只能渲染一层View(如progressbar,没不能添加child的view),当该原生Android View包含很多子view时(也就是根View为FramLayout、或者linearLayout之类),无法实时的监听到View动态改变,OpenGL中只能不停的渲染该view,才能渲染出原生View的效果。但是这样一来不同的渲染会耗费大量的资源,降低应用程序的效率。理想中的话,是监听到了该View的内容或者其子view 的内容发生了变化(如:View中的字幕发生滚动)才进行渲染。

经过接近两周的努力我终于完美地实现了该效果,既然是站在别人的基础上得来的成果,那么该方法就应当被共享,所以产生了此文,不过只支持api 15以上的

步骤一:重写根View

1.设置该View 绘制自己:

  1. setWillNotDraw(false);

2.监听View的变化,重写View,用ViewTreeObServer来监听,方法如下:

  1. private void addOnPreDrawListener() {
  2. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  3. final ViewTreeObserver mObserver = getViewTreeObserver();
  4. if (mObserver != null) {
  5. mObserver.addOnPreDrawListener(new OnPreDrawListener() {
  6. @Override
  7. public boolean onPreDraw() {
  8. if (isDirty()) {//View或者子view发生变化
  9. invalidate();
  10. }
  11. return true;
  12. }
  13. });
  14. }
  15. }
  16. }

3.重写该View的onDraw方法:

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. try {
  4. if (mSurface != null) {
  5. Canvas surfaceCanvas = mSurface.lockCanvas(null);
  6. super.dispatchDraw(surfaceCanvas);
  7. mSurface.unlockCanvasAndPost(surfaceCanvas);
  8. mSurface.release();
  9. mSurface = null;
  10. mSurface = new Surface(mSurfaceTexture);
  11. }
  12. } catch (OutOfResourcesException e) {
  13. e.printStackTrace();
  14. }
  15. }

步骤二:GLSurfaceView.Renderer

  1. class CustomRenderer implements GLSurfaceView.Renderer {
  2. int glSurfaceTex;
  3. private final int GL_TEXTURE_EXTERNAL_OES = 0x8D65;
  4. long currentTime;
  5. long previousTime;
  6. boolean b = false;
  7. int frameCount = 0;
  8. DirectDrawer mDirectDrawer;
  9. ActivityManager activityManager;
  10. MemoryInfo _memoryInfo;
  11. // Fixed values
  12. private int TEXTURE_WIDTH = 360;
  13. private int TEXTURE_HEIGHT = 360;
  14. Context context;
  15. private LauncherAppWidgetHostView addedWidgetView;
  16. private SurfaceTexture surfaceTexture = null;
  17. private Surface surface;
  18. float fps;
  19. public CustomRenderer(Context context, LauncherAppWidgetHostView addedWidgetView, Display mDisplay){
  20. this.context = context;
  21. this.addedWidgetView = addedWidgetView;
  22. TEXTURE_WIDTH = mDisplay.getWidth();
  23. TEXTURE_HEIGHT = mDisplay.getHeight();
  24. _memoryInfo = new MemoryInfo();
  25. activityManager = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
  26. }
  27. @Override
  28. public void onDrawFrame(GL10 gl) {
  29. synchronized (this) {
  30. surfaceTexture.updateTexImage();
  31. }
  32. activityManager.getMemoryInfo(_memoryInfo);
  33. GLES20.glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
  34. GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
  35. GLES20.glEnable(GLES20.GL_BLEND);
  36. GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
  37. float[] mtx = new float[16];
  38. surfaceTexture.getTransformMatrix(mtx);
  39. mDirectDrawer.draw(mtx);
  40. calculateFps();
  41. //getAppMemorySize();
  42. //getRunningAppProcessInfo();
  43. //Log.v("onDrawFrame", "FPS: " + Math.round(fps) + ", availMem: " + Math.round(_memoryInfo.availMem / 1048576) + "MB");
  44. }
  45. private void getAppMemorySize(){
  46. ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  47. android.os.Debug.MemoryInfo[] memoryInfos = mActivityManager.getProcessMemoryInfo(new int[]{android.os.Process.myPid()});
  48. int size = memoryInfos[0].dalvikPrivateDirty;
  49. Log.w("getAppMemorySize", size / 1024 + " MB");
  50. }
  51. private void getRunningAppProcessInfo() {
  52. ActivityManager  mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
  53. //获得系统里正在运行的所有进程
  54. List<RunningAppProcessInfo> runningAppProcessesList = mActivityManager.getRunningAppProcesses();
  55. for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcessesList) {
  56. // 进程ID号
  57. int pid = runningAppProcessInfo.pid;
  58. // 用户ID
  59. int uid = runningAppProcessInfo.uid;
  60. // 进程名
  61. String processName = runningAppProcessInfo.processName;
  62. // 占用的内存
  63. int[] pids = new int[] {pid};
  64. Debug.MemoryInfo[] memoryInfo = mActivityManager.getProcessMemoryInfo(pids);
  65. int memorySize = memoryInfo[0].dalvikPrivateDirty;
  66. System.out.println("processName="+processName+",currentPid: "+  "pid= " +android.os.Process.myPid()+"----------->"+pid+",uid="+uid+",memorySize="+memorySize+"kb");
  67. }
  68. }
  69. @Override
  70. public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  71. surface = null;
  72. surfaceTexture = null;
  73. glSurfaceTex = Engine_CreateSurfaceTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);
  74. Log.d("GLES20Ext", "glSurfaceTex" + glSurfaceTex);
  75. if (glSurfaceTex > 0) {
  76. surfaceTexture = new SurfaceTexture(glSurfaceTex);
  77. surfaceTexture.setDefaultBufferSize(TEXTURE_WIDTH, TEXTURE_HEIGHT);
  78. surface = new Surface(surfaceTexture);
  79. addedWidgetView.setSurface(surface);
  80. addedWidgetView.setSurfaceTexture(surfaceTexture);
  81. //addedWidgetView.setSurfaceTexture(surfaceTexture);
  82. mDirectDrawer = new DirectDrawer(glSurfaceTex);
  83. }
  84. }
  85. float calculateFps() {
  86. frameCount++;
  87. if (!b) {
  88. b = true;
  89. previousTime = System.currentTimeMillis();
  90. }
  91. long intervalTime = System.currentTimeMillis() - previousTime;
  92. if (intervalTime >= 1000) {
  93. b = false;
  94. fps = frameCount / (intervalTime / 1000f);
  95. frameCount = 0;
  96. Log.w("calculateFps", "FPS: " + fps);
  97. }
  98. return fps;
  99. }
  100. int Engine_CreateSurfaceTexture(int width, int height) {
  101. /*
  102. * Create our texture. This has to be done each time the surface is
  103. * created.
  104. */
  105. int[] textures = new int[1];
  106. GLES20.glGenTextures(1, textures, 0);
  107. glSurfaceTex = textures[0];
  108. if (glSurfaceTex > 0) {
  109. GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, glSurfaceTex);
  110. // Notice the use of GL_TEXTURE_2D for texture creation
  111. GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, width, height, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, null);
  112. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
  113. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
  114. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
  115. GLES20.glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
  116. }
  117. return glSurfaceTex;
  118. }
  119. @Override
  120. public void onSurfaceChanged(GL10 gl, int width, int height) {
  121. }
  122. }
  1. public class DirectDrawer {
  2. private final String vertexShaderCode =
  3. "attribute vec4 vPosition;" +
  4. "attribute vec2 inputTextureCoordinate;" +
  5. "varying vec2 textureCoordinate;" +
  6. "void main()" +
  7. "{"+
  8. "gl_Position = vPosition;"+
  9. "textureCoordinate = inputTextureCoordinate;" +
  10. "}";
  11. private final String fragmentShaderCode =
  12. "#extension GL_OES_EGL_image_external : require\n"+
  13. "precision mediump float;" +
  14. "varying vec2 textureCoordinate;\n" +
  15. "uniform samplerExternalOES s_texture;\n" +
  16. "void main() {" +
  17. "  gl_FragColor = texture2D( s_texture, textureCoordinate );\n" +
  18. "}";
  19. private FloatBuffer vertexBuffer, textureVerticesBuffer;
  20. private ShortBuffer drawListBuffer;
  21. private final int mProgram;
  22. private int mPositionHandle;
  23. private int mTextureCoordHandle;
  24. private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
  25. // number of coordinates per vertex in this array
  26. private static final int COORDS_PER_VERTEX = 2;
  27. private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
  28. static float squareCoords[] = {
  29. -1.0f,  0.0f,
  30. -1.0f, -2.2f,
  31. 1.0f, -2.2f,
  32. 1.0f,  0.0f,
  33. };
  34. static float textureVertices[] = {
  35. 0f,  0f,
  36. 0f,  1f,
  37. 1f,  1f,
  38. 1f,  0f,
  39. };
  40. private int texture;
  41. public DirectDrawer(int texture)
  42. {
  43. this.texture = texture;
  44. // initialize vertex byte buffer for shape coordinates
  45. ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
  46. bb.order(ByteOrder.nativeOrder());
  47. vertexBuffer = bb.asFloatBuffer();
  48. vertexBuffer.put(squareCoords);
  49. vertexBuffer.position(0);
  50. // initialize byte buffer for the draw list
  51. ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
  52. dlb.order(ByteOrder.nativeOrder());
  53. drawListBuffer = dlb.asShortBuffer();
  54. drawListBuffer.put(drawOrder);
  55. drawListBuffer.position(0);
  56. ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
  57. bb2.order(ByteOrder.nativeOrder());
  58. textureVerticesBuffer = bb2.asFloatBuffer();
  59. textureVerticesBuffer.put(textureVertices);
  60. textureVerticesBuffer.position(0);
  61. int vertexShader    = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
  62. int fragmentShader  = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
  63. mProgram = GLES20.glCreateProgram();             // create empty OpenGL ES Program
  64. GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
  65. GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
  66. GLES20.glLinkProgram(mProgram);                  // creates OpenGL ES program executables
  67. }
  68. public void draw(float[] mtx)
  69. {
  70. GLES20.glUseProgram(mProgram);
  71. GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  72. GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);
  73. // get handle to vertex shader's vPosition member
  74. mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
  75. // Enable a handle to the triangle vertices
  76. GLES20.glEnableVertexAttribArray(mPositionHandle);
  77. // Prepare the <insert shape here> coordinate data
  78. GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
  79. mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
  80. GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
  81. // textureVerticesBuffer.clear();
  82. // textureVerticesBuffer.put( transformTextureCoordinates(
  83. // textureVertices, mtx ));
  84. // textureVerticesBuffer.position(0);
  85. GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);
  86. GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
  87. // Disable vertex array
  88. GLES20.glDisableVertexAttribArray(mPositionHandle);
  89. GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
  90. }
  91. private  int loadShader(int type, String shaderCode){
  92. // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
  93. // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
  94. int shader = GLES20.glCreateShader(type);
  95. // add the source code to the shader and compile it
  96. GLES20.glShaderSource(shader, shaderCode);
  97. GLES20.glCompileShader(shader);
  98. return shader;
  99. }
  100. private float[] transformTextureCoordinates( float[] coords, float[] matrix)
  101. {
  102. float[] result = new float[ coords.length ];
  103. float[] vt = new float[4];
  104. for ( int i = 0 ; i < coords.length ; i += 2 ) {
  105. float[] v = { coords[i], coords[i+1], 0 , 1  };
  106. Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
  107. result[i] = vt[0];
  108. result[i+1] = vt[1];
  109. }
  110. return result;
  111. }
  112. }

步骤三:配置GLSurfaceView:

  1. GLSurfaceView glSurfaceView = new GLSurfaceView(getApplicationContext());
  2. // Setup the surface view for drawing to
  3. glSurfaceView.setEGLContextClientVersion(2);
  4. glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
  5. glSurfaceView.setRenderer(renderer);
  6. //glSurfaceView.setZOrderOnTop(true);
  7. // Add our WebView to the Android View hierarchy
  8. glSurfaceView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));

效果如图所示,上面是猎豹清理大师的widget,下面是GLSurfaceView的Texture,原生的Widget和OpenGL中渲染的一模一样。并且点击widget进行清理时也能达到实时渲染的Veiw动画清理效果,源码链接:https://github.com/MrHuangXin/RenderViewToOpenGL/tree/master

把Android原生的View渲染到OpenGL Texture的更多相关文章

  1. Android原生编解码接口 MediaCodec 之——踩坑

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/gb702250823/article/d ...

  2. 拓展 Android 原生 CountDownTimer 倒计时

    拓展 Android 原生 CountDownTimer 倒计时 [TOC] CountDownTimer 在系统的CountDownTimer上进行的修改,主要是拓展了功能,当然也保留了系统默认的模 ...

  3. android原生ExpandableListView

    android原生可扩展ExpandableListView就是可以伸缩的listView,一条标题下面有多条内容. 这个list的adapter对的数据要求与普通ListView的数据要求也有一些差 ...

  4. Android 原生listview item伸展收缩效果

    Android原生listview做的一个item的伸缩效果.*永远不要让你老大有机会改需求 package com.example.yunkanglast; import java.io.Seria ...

  5. Android原生跳转React不同页面(undefined is not an object)

    继续上篇文章的demo,由于现在的项目是原生的,打算用部分页面试下react native,那么问题来了:react貌似只有一个入口 index.android.js,那么在不同的原生页面需要跳转到不 ...

  6. android 原生camera——设置模块修改

    , 此篇博客是记一次客户需求修改,从上周五到现在正好一周时间,期间的各种酸爽,就不说了,还是来看大家关注的技术问题吧. 首先看下以前效果和修改后的效果: 修改前:修改后: 不知道有没有看明白,我在简单 ...

  7. Android进阶(二十七)Android原生扰人烦的布局

    Android原生扰人烦的布局 在开发Android应用时,UI布局是一件令人烦恼的事情.下面主要讲解一下Android中的界面布局. 一.线性布局(LinearLayout) 线性布局分为: (1) ...

  8. Android原生嵌入React Native

    1.首先集成的项目目录 我使用的是直接按照react-native init Project 的格式来导入的,也就是说,我的Android项目目录是跟node_modules是在一个目录下的. 我们i ...

  9. uni-app&H5&Android混合开发三 || uni-app调用Android原生方法的三种方式

    前言: 关于H5的调用Android原生方法的方式有很多,在该片文章中我主要简单介绍三种与Android原生方法交互的方式. 一.H5+方法调用android原生方法 H5+ Android开发规范官 ...

随机推荐

  1. 使用加密的squid配合stunnel实现HTTP代理

    现在大部分人都是用ssh tunnel来搭建socks5代理,其实这种方式效率并不高,ssh tunnel并不是为了做代理而存在的.一个比较好的方法是加密squid配合stunnel实现http代理. ...

  2. iframe定位获取

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Android 混淆完全解析

    1.http://blog.csdn.net/jddkdd2/article/details/8858909

  4. 理解和使用WPF 验证机制(值得推荐)

    首先建立一个demo用以学习和实验WPF Data Validation机制.创建一个数据实体类: public class Employee { public string Name { get;  ...

  5. iOS JSON字符串转化为字典-字典转Json字符串-

    1. JSON字符串转化为字典 + (NSDictionary *)dictionaryWithJsonString:(NSString *)jsonString { if (jsonString = ...

  6. js 的函数参数的默认值问题

    js函数参数设置默认值   php有个很方便的用法是在定义函数时可以直接给参数设默认值,如: function simue ($a=1,$b=2){ return $a+$b; } echo simu ...

  7. 构造三层时报错“程序 “D:\MyTest\....”不包含适合于入口点的静态"Main"方法”

    错误 1 程序“D:\MyTest\EBookShop\Model\obj\x86\Debug\Model.exe”不包含适合于入口点的静态“Main”方法 原因:原来创建项目的时候,用的是“空项目” ...

  8. Linq与扩展方法

    使用数据集 /// <summary> /// 库房信息类 /// </summary> public class Kfxx { /// <summary> /// ...

  9. Spec Explorer 工具学习

    基础概念:http://blogs.msdn.com/b/sechina/archive/2009/12/28/test.aspx 在线教程:http://blogs.msdn.com/b/sechi ...

  10. SpringBoot-------实现多数据源Demo

    之前SpringBoot出来时候就看了下Springboot,感觉的确精简掉了配置文件! 还是很方便的!没办法,我只是个菜鸟! 什么怎么启动Springboot什么的就不说了, 具体的Demo地址我都 ...