Android Captura de pantalla de la vista de superficie muestra la pantalla en negro

Estoy intentando tomar una captura de pantalla de mi juego a través del código y compartirlo a través de una intención. Puedo hacer esas cosas, sin embargo, la captura de pantalla siempre aparece en negro. Aquí está el código relacionado con Compartir la captura de pantalla:

View view = MainActivity.getView(); view.setDrawingCacheEnabled(true); Bitmap screen = Bitmap.createBitmap(view.getDrawingCache(true)); .. save Bitmap 

Esto está en la actividad principal:

 view = new GameView(this); view.setLayoutParams(new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT)); public static SurfaceView getView() { return view; } 

Y la vista en sí misma:

 public class GameView extends SurfaceView implements SurfaceHolder.Callback { private static SurfaceHolder surfaceHolder; ...etc 

Y así es como estoy dibujando todo:

 Canvas canvas = surfaceHolder.lockCanvas(null); if (canvas != null) { Game.draw(canvas); ... 

Ok, basándome en algunas respuestas, he construido esto:

 public static void share() { Bitmap screen = GameView.SavePixels(0, 0, Screen.width, Screen.height); Calendar c = Calendar.getInstance(); Date d = c.getTime(); String path = Images.Media.insertImage( Game.context.getContentResolver(), screen, "screenShotBJ" + d + ".png", null); System.out.println(path + " PATH"); Uri screenshotUri = Uri.parse(path); final Intent emailIntent = new Intent( android.content.Intent.ACTION_SEND); emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); emailIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri); emailIntent.setType("image/png"); Game.context.startActivity(Intent.createChooser(emailIntent, "Share High Score:")); } 

Gameview contiene el siguiente método:

 public static Bitmap SavePixels(int x, int y, int w, int h) { EGL10 egl = (EGL10) EGLContext.getEGL(); GL10 gl = (GL10) egl.eglGetCurrentContext().getGL(); int b[] = new int[w * (y + h)]; int bt[] = new int[w * h]; IntBuffer ib = IntBuffer.wrap(b); ib.position(0); gl.glReadPixels(x, 0, w, y + h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib); for (int i = 0, k = 0; i < h; i++, k++) { for (int j = 0; j > 16) & 0xff; int pr = (pix << 16) & 0x00ff0000; int pix1 = (pix & 0xff00ff00) | pr | pb; bt[(h - k - 1) * w + j] = pix1; } } Bitmap sb = Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888); return sb; } 

La captura de pantalla sigue siendo negra. ¿Hay algún problema con la forma en que lo estoy guardando?

He intentado varios métodos diferentes para tomar la captura de pantalla, pero ninguno de ellos funcionó: el que se muestra en el código anterior fue el más comúnmente sugerido. pero no parece funcionar. ¿Es esto un problema con el uso de SurfaceView? Y si es así, ¿por qué view.getDrawingCache (true) incluso existe si no puedo usarlo y cómo lo soluciono?

CÓDIGO DE TRABAJO:

 public static void share() { // GIVES BLACK SCREENSHOT: Calendar c = Calendar.getInstance(); Date d = c.getTime(); Game.update(); Bitmap.Config conf = Bitmap.Config.RGB_565; Bitmap image = Bitmap.createBitmap(Screen.width, Screen.height, conf); Canvas canvas = GameThread.surfaceHolder.lockCanvas(null); canvas.setBitmap(image); Paint backgroundPaint = new Paint(); backgroundPaint.setARGB(255, 40, 40, 40); canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint); Game.draw(canvas); Bitmap screen = Bitmap.createBitmap(image, 0, 0, Screen.width, Screen.height); canvas.setBitmap(null); GameThread.surfaceHolder.unlockCanvasAndPost(canvas); String path = Images.Media.insertImage( Game.context.getContentResolver(), screen, "screenShotBJ" + d + ".png", null); System.out.println(path + " PATH"); Uri screenshotUri = Uri.parse(path); final Intent emailIntent = new Intent( android.content.Intent.ACTION_SEND); emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); emailIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri); emailIntent.setType("image/png"); Game.context.startActivity(Intent.createChooser(emailIntent, "Share High Score:")); } 

Gracias.

Existe una gran confusión sobre esto y algunas respuestas correctas .

Aquí está el trato:

  1. Un SurfaceView tiene dos partes, la superficie y la vista. La Superficie está en una capa completamente separada de todos los elementos de la IU de Vista. El enfoque getDrawingCache() funciona en la capa Ver, por lo que no captura nada en Surface.

  2. La cola de almacenamiento intermedio tiene una API productor-consumidor, y solo puede tener un productor. Canvas es un productor, GLES es otro. No puede dibujar con Canvas y leer píxeles con GLES. (Técnicamente, podrías si el Lienzo estuviese usando GLES y el contexto EGL correcto estuviera actualizado cuando fueras a leer los píxeles, pero eso no está garantizado. El renderizado de canvas en una Superficie no se acelera en ninguna versión de Android lanzada, así que ahora mismo no hay esperanza de que funcione.)

  3. (No es relevante para su caso, pero lo mencionaré como completo 🙂 Una Surface no es un buffer de cuadro, es una cola de buffers. Cuando envía un búfer con GLES, se va y ya no puede leer de él. Entonces, si estuvieras renderizando con GLES y capturando con GLES, necesitarías leer los píxeles antes de llamar a eglSwapBuffers() .

Con el procesamiento de canvas, la forma más fácil de “capturar” el contenido de Surface es simplemente dibujarlo dos veces. Cree un bitmap del tamaño de la pantalla, cree un canvas del bitmap y páselo a la función draw() .

Con la representación de GLES, puede usar glReadPixels() antes del intercambio de memoria intermedia para captar los píxeles. Hay una implementación (menos costosa que el código en la pregunta) del código de captura en Grafika ; vea saveFrame() en EglSurfaceBase .

Si estuvieras enviando video directamente a una Surface (a través de MediaPlayer) no habría forma de capturar los frames, porque tu aplicación nunca tiene acceso a ellos, van directamente de mediaserver al compositor (SurfaceFlinger). Sin embargo, puede enrutar los fotogtwigs entrantes a través de una Textura de superficie, y renderizarlos dos veces desde su aplicación, una para mostrar y otra para capturar. Vea esta pregunta para más información.

Una alternativa es reemplazar el SurfaceView con un TextureView, que se puede dibujar como cualquier otra superficie. A continuación, puede usar una de las llamadas getBitmap() para capturar un fotogtwig. TextureView es menos eficiente que SurfaceView, por lo que no se recomienda para todas las situaciones, pero es fácil de hacer.

Si esperaba obtener una captura de pantalla compuesta que contenga tanto el contenido de la superficie como el contenido de la interfaz de usuario de la vista, deberá capturar el canvas como se indica anteriormente, capturar la vista con el truco habitual de caché de dibujo y luego combinar los dos manualmente. Tenga en cuenta que esto no recogerá las partes del sistema (barra de estado, barra de navegación).

Actualización: en Lollipop y posterior (API 21+) puede usar la clase MediaProjection para capturar toda la pantalla con una pantalla virtual. Hay algunas ventajas y desventajas con este enfoque, por ejemplo, está capturando la pantalla renderizada, no el marco que se envió a la superficie, por lo que lo que obtiene puede haberse escalado hacia arriba o hacia abajo para ajustarse a la ventana. Además, este enfoque implica un cambio de actividad ya que debe crear un bash (llamando a createScreenCaptureIntent en el objeto ProjectionManager) y esperar su resultado.

Si desea obtener más información acerca de cómo funciona todo esto, consulte el documento de la architecture de gráficos del sistema Android.

Esto se debe a que SurfaceView usa hilo OpenGL para dibujar y dibujar directamente a un buffer de hardware. Tienes que usar glReadPixels (y probablemente un GLWrapper).

Ver el hilo: Android OpenGL Captura de pantalla