Android采取表面视图的屏幕截图显示黑屏

我试图通过代码截取我的游戏,并通过意图分享。 我能够做到这些事情,但截图总是显示为黑色。 这是与共享屏幕截图有关的代码:

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); ... 

好的,基于一些答案,我已经构build了这个:

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

游戏视图包含以下方法:

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

谢谢。

这个问题有很多困惑,还有一些正确的 答案 。

这是交易:

  1. 一个SurfaceView有两个部分,Surface和View。 Surface与所有View UI元素完全分离。 getDrawingCache()方法仅适用于View层,因此它不会捕获Surface上的任何东西。

  2. 缓冲队列有一个生产者 – 消费者API,它只能有一个生产者。 帆布是一个生产者,GLES是另一个。 您不能使用canvas绘制并使用GLES读取像素。 (从技术上讲,如果Canvas使用GLES,并且在读取像素时正确的EGL上下文是当前的,但是不能保证。在任何发布的Android版本中,Canvas渲染到Surface都不会加速,所以现在没有希望工作。)

  3. (与你的情况不相关,但我会提到它的完整性:) Surface不是一个帧缓冲区,它是一个缓冲区的队列。 当你用GLES提交一个缓冲区时,它就消失了,你不能再读取它了。 因此,如果您使用GLES进行渲染并使用GLES进行捕获,则在调用eglSwapBuffers()之前,您需要先读取像素。

使用Canvas渲染,“捕捉”Surface内容的最简单方法是简单地绘制两次。 创build一个屏幕大小的位图,从位图创build一个canvas,并将其传递给draw()函数。

使用GLES渲染,可以在缓冲区交换之前使用glReadPixels()来获取像素。 在Grafika中有一个(比问题中的代码更便宜的)实现抓取代码; 请参阅saveFrame()

如果您直接将video发送到Surface(通过MediaPlayer),将无法捕获帧,因为您的应用程序永远不能访问它们 – 它们直接从mediaserver传送到合成器(SurfaceFlinger)。 但是,您可以通过SurfaceTexture路由传入的帧,并从应用渲染两次,一次用于显示,一次用于捕获。 看到这个问题的更多信息。

一种替代方法是用一个TextureViewreplaceSurfaceView,它可以像其他Surface一样绘制。 然后,您可以使用其中一个getBitmap()调用来捕获一个帧。 TextureView的效率比SurfaceView低,所以不build议在所有情况下使用,但是很简单。

如果您希望获得包含Surface内容和View UI内容的复合屏幕截图,则需要如上所述捕获Canvas,使用通常的绘图caching技巧捕获View,然后手动合成这两个视图。 注意这不会拿起系统部件(状态栏,导航栏)。

更新:在棒棒糖及更高版本(API 21+)上,您可以使用MediaProjection类以虚拟显示捕捉整个屏幕。 这种方法有一些权衡,例如捕获渲染的屏幕,而不是发送到Surface的帧,所以你得到的可能已经被缩小或缩小以适合窗口。

如果你想了解所有这些东西是如何工作的,请参阅Android 系统级graphics体系结构文档。

这是因为SurfaceView使用OpenGL线程绘制并直接绘制到硬件缓冲区。 你必须使用glReadPixels(可能是一个GLWrapper)。

查看线程: Android OpenGL截图