Android Take Скриншот просмотра поверхности показывает черный экран

Я пытаюсь сделать снимок экрана моей игры с помощью кода и поделиться им с помощью намерения. Я могу делать это, однако скриншот всегда кажется черным. Вот код, связанный с совместным использованием снимка экрана:

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

Это в MainActivity:

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

И сам вид:

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

И так я рисую все:

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

Хорошо, основываясь на некоторых ответах, я построил это:

 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 содержит следующий метод:

 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; } 

Скриншот по-прежнему черный. Что-то не так с тем, как я могу его спасти?

Я попробовал несколько разных способов сделать снимок экрана, но ни один из них не работал: Наиболее показанный в приведенном выше коде был показан. Но, похоже, это не работает. Это проблема с использованием SurfaceView? И если да, то почему view.getDrawingCache (true) даже существует, если я не могу его использовать и как это исправить?

РАБОЧИЙ КОД:

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

Спасибо.

Solutions Collecting From Web of "Android Take Скриншот просмотра поверхности показывает черный экран"

В этом есть много путаницы и несколько правильных ответов .

Вот сделка:

  1. SurfaceView имеет две части: поверхность и вид. Поверхность находится на полностью отдельном уровне от всех элементов View UI. Подход getDrawingCache() работает только на уровне представления, поэтому он не захватывает ничего на поверхности.

  2. Буферная очередь имеет API-интерфейс производителя-потребителя и может иметь только один производитель. Холст – один из продюсеров, GLES – другой. Вы не можете рисовать с помощью Canvas и читать пиксели с помощью GLES. (Технически, если бы Canvas использовал GLES, и корректный контекст EGL был текущим, когда вы читали пиксели, но это не гарантировано. Canvas-рендеринг на Surface не ускоряется в любой выпущенной версии Android, поэтому прямо сейчас есть Нет надежды на то, что он работает.)

  3. (Не относится к вашему делу, но я упомянул об этом для полноты 🙂 Поверхность не является буфером кадров, это очередь буферов. Когда вы отправляете буфер с GLES, он исчезает, и вы больше не можете читать его. Поэтому, если вы выполняли рендеринг с помощью GLES и записывали с помощью GLES, вам нужно было бы прочитать пиксели перед вызовом eglSwapBuffers() .

С рендерингом Canvas самый простой способ «захватить» содержимое Surface – просто нарисовать его дважды. Создайте растровое изображение размера экрана, создайте холст из растрового изображения и передайте его в свою функцию draw() .

С помощью рендеринга GLES вы можете использовать glReadPixels() до того, как буферный своп захватит пиксели. В Grafika есть (менее дорогой, чем код в вопросе) реализация кода захвата; См. saveFrame() в EglSurfaceBase .

Если вы отправляете видео напрямую на поверхность (через MediaPlayer), невозможно будет захватить фреймы, потому что ваше приложение никогда не имеет к ним доступа – они идут напрямую из медиазера в композитор (SurfaceFlinger). Однако вы можете направлять входящие кадры через SurfaceTexture и отображать их дважды из вашего приложения, один раз для отображения и один раз для захвата. См. Этот вопрос для получения дополнительной информации.

Один из вариантов – заменить SurfaceView на TextureView, который можно нарисовать, как и на любой другой поверхности. Затем вы можете использовать один из getBitmap() для захвата фрейма. TextureView менее эффективен, чем SurfaceView, поэтому это не рекомендуется для всех ситуаций, но это просто сделать.

Если бы вы надеялись получить композитный скриншот, содержащий как содержимое Surface, так и содержимое View UI, вам нужно будет захватить Canvas, как указано выше, захватить представление с помощью обычного трюка кэша чертежа, а затем собрать два вручную. Обратите внимание, что это не приведет к сбоям частей системы (строка состояния, навигационная панель).

Обновление: на Lollipop и позже (API 21+) вы можете использовать класс MediaProjection для захвата всего экрана с помощью виртуального дисплея. Есть несколько компромиссов с таким подходом, например, вы захватываете отображаемый экран, а не кадр, который был отправлен на поверхность, поэтому то, что вы получаете, возможно, было масштабировано вверх или вниз, чтобы соответствовать окну.

Если вы хотите узнать больше о том, как все это работает, обратитесь к документации по графической архитектуре уровня системы Android.

Это связано с тем, что SurfaceView использует поток OpenGL для рисования и рисования непосредственно в аппаратном буфере. Вы должны использовать glReadPixels (и, возможно, GLWrapper).

Посмотреть тему: Android OpenGL Screenshot