studio programacion herramientas fundamentos con avanzado aplicaciones android surfaceview

programacion - Captura de pantalla de Android Take de Surface View muestra la pantalla en negro



manual de android en pdf (2)

Esto se debe a que SurfaceView utiliza un hilo OpenGL para dibujar y dibuja directamente en un búfer de hardware. Tienes que usar glReadPixels (y probablemente un GLWrapper).

Vea el tema: Captura de pantalla de Android OpenGL

Estoy intentando tomar una captura de pantalla de mi juego a través del código y compartirlo a través de un intento. 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 MainActivity:

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 propia vista:

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, basado 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:")); }

El 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 < w; j++) { int pix = b[i * w + j]; int pb = (pix >> 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 negro. ¿Hay algo malo 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 sugerido. pero no parece funcionar. ¿Es este un problema con el uso de SurfaceView? Y si es así, ¿por qué existe view.getDrawingCache (true) si no puedo usarlo y cómo soluciono esto?

Mi código:

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.


Hay mucha confusión sobre esto y algunas answers correct .

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 interfaz de usuario de la vista. El enfoque getDrawingCache() funciona solo en la capa Vista, por lo que no captura nada en la Superficie.

  2. La cola de búfer tiene una API productor-consumidor, y solo puede tener un productor. Canvas es un productor, GLES es otro. No puedes dibujar con Canvas y leer píxeles con GLES. (Técnicamente, podría hacerlo si el Canvas estuviera usando GLES y el contexto EGL correcto fuera actual cuando fue a leer los píxeles, pero eso no está garantizado. El renderizado del Canvas a una Surface no se acelera en ninguna versión lanzada de Android, por lo que ahora hay no hay esperanza de que funcione.)

  3. (No es relevante para su caso, pero lo mencionaré en su totalidad :) Una Superficie no es un búfer de cuadros, es una cola de búferes. Cuando envía un búfer con GLES, desaparece y ya no puede leerlo. Entonces, si estaba renderizando con GLES y capturando con GLES, tendría que leer los píxeles antes de llamar a eglSwapBuffers() .

Con el renderizado de Canvas, la forma más fácil de "capturar" el contenido de Surface es simplemente dibujarlo dos veces. Cree un mapa de bits del tamaño de una pantalla, cree un lienzo desde el mapa de bits y páselo a su función draw() .

Con el procesamiento de GLES, puede usar glReadPixels() antes del intercambio de búfer para capturar los píxeles. Hay una implementación (menos costosa que el código de la pregunta) del código grab en Grafika ; vea saveFrame() en EglSurfaceBase .

Si enviara un video directamente a un Surface (a través de MediaPlayer), no habría manera de capturar los marcos, ya que su aplicación nunca tiene acceso a ellos: van directamente desde el servidor de medios al compositor (SurfaceFlinger). Sin embargo, puede enrutar los marcos entrantes a través de una SurfaceTexture y renderizarlos dos veces desde su aplicación, una para mostrar y una para capturar. Vea esta pregunta para más información.

Una alternativa es reemplazar la SurfaceView con una TextureView, que se puede dibujar como cualquier otra Surface. Luego puedes usar una de las llamadas getBitmap() para capturar un marco. 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 Superficie como el contenido de la interfaz de usuario de la Vista, deberá capturar el Lienzo como se muestra arriba, capturar la Vista con el truco de caché de dibujo habitual y luego compilar 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 versiones posteriores (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 haber sido subido o bajado para adaptarse a la ventana. Además, este enfoque implica un cambio de actividad, ya que tiene que crear un intento (llamando a createScreenCaptureIntent en el objeto ProjectionManager) y esperar su resultado.

Si desea obtener más información sobre cómo funciona todo esto, consulte la documentación de Arquitectura de gráficos a nivel de sistema de Android.