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:"));
}
谢谢。
#1 楼
对此有很多困惑,还有一些正确答案。这是要解决的问题:
SurfaceView有两个部分,Surface和视图。 Surface与所有View UI元素位于完全独立的图层上。
getDrawingCache()
方法仅在View层上有效,因此它无法捕获Surface上的任何内容。缓冲区队列具有生产者-消费者API,并且只能有一个生产者。帆布是一个生产商,GLES是另一个生产商。您不能使用Canvas进行绘制,也不能使用GLES读取像素。 (从技术上讲,当您读取像素时,如果Canvas使用的是GLES,并且当前使用的是正确的EGL上下文,则可以这样做,但是不能保证。在任何发行版本的Android中,Canvas渲染到Surface的速度都不会加快,所以现在没有希望。)
(与您的情况无关,但出于完整性考虑,我将其提及:) Surface不是帧缓冲区,而是缓冲区队列。当您使用GLES提交缓冲区时,缓冲区消失了,无法再从中读取。因此,如果要使用GLES进行渲染并使用GLES进行捕获,则需要在调用
eglSwapBuffers()
之前先读取像素。使用Canvas渲染,“捕获” Surface内容的最简单方法是简单地绘制它两次。创建一个屏幕大小的位图,从该位图创建一个Canvas,并将其传递给您的
draw()
函数。使用GLES渲染,可以在缓冲区交换之前使用
glReadPixels()
来捕获像素。 Grafika中有一个抓取代码的实现(比问题中的代码便宜)。请参阅EglSurfaceBase中的saveFrame()
。如果您将视频直接发送到Surface(通过MediaPlayer),将无法捕获帧,因为您的应用程序永远无法访问它们-它们直接从mediaserver到达合成器(SurfaceFlinger)。但是,您可以通过SurfaceTexture路由传入的帧,然后从您的应用程序渲染两次,一次进行显示,一次进行捕获。有关更多信息,请参见此问题。
一种替代方法是用TextureView替换SurfaceView,该TextureView可以像其他任何Surface一样绘制。然后,您可以使用
getBitmap()
调用之一来捕获帧。 TextureView比SurfaceView效率低,因此不建议在所有情况下都使用它,但是这样做很简单。如果您希望获得包含Surface内容和View UI内容的复合屏幕截图,您将需要捕获上述的Canvas,使用常规的图形缓存技巧捕获View,然后手动将两者进行合成。请注意,这不会占用系统部分(状态栏,导航栏)。
更新:在Lollipop及更高版本(API 21+)上,您可以使用MediaProjection类来捕获整个屏幕。虚拟显示。这种方法需要权衡取舍,例如您正在捕获渲染的屏幕,而不是捕获发送到Surface的帧,因此您可能会放大或缩小以适应窗口。另外,这种方法涉及到活动切换,因为您必须创建一个意图(通过在ProjectionManager对象上调用createScreenCaptureIntent)并等待其结果。
如果您想了解更多有关这些东西的信息可以使用,请参见Android系统级图形体系结构文档。
评论
因此,为了澄清起见,我如何准确地将画布存储到位图中?
–user3011902
2015年1月8日在2:25
您创建一个位图,然后将其传递给Canvas构造函数-developer.android.com/reference/android/graphics/…
–淡淡
2015年1月8日在6:14
我有一个相对的布局,在该布局下有一个调用相机的表面视图,在它的顶部有一个可以增加相机的图像视图。如何拍摄整个场景的屏幕截图?
– navinpai
15年8月30日在12:01
@navinpai请通过创建新问题而不是在评论中添加评论来提问。参见stackoverflow.com/questions/26545970/…
–淡淡
15年8月30日在17:44
所以如何在给定活动根视图的情况下截取该死的屏幕截图
–艾哈迈德·阿里(Ahmed Ali)
18/12/6在16:25
#2 楼
我知道它的回复很晚,但是对于那些遇到相同问题的人,我们可以使用PixelCopy来获取快照。在API level 24
及更高版本中可用PixelCopy.request(surfaceViewObject,BitmapDest,listener,new Handler());
其中,
surfaceViewObject是表面视图的对象
BitmapDest是要保存图像且不能为null的位图对象
listener为OnPixelCopyFinishedListener
有关更多信息,请参考-https://developer.android.com/reference/ android / view / PixelCopy
评论
您对24年前可以使用的类似产品有任何建议吗?
– hellowill89
19年1月11日,1:50
不,我尝试了很多解决方案,但是在表面视图的情况下,只有这种方法有效。
–安贾尼·米塔尔(Anjani Mittal)
19年1月14日在11:59
因此,这将适用于单个Surface视图吗?如果当前的显示由表面视图和普通的Android UI层组成,该怎么办?
–迪兹曼狂热
19年1月17日在2:27
这仅用于表面视图,对于表面视图和其他内容,我没有这种情况。
–安贾尼·米塔尔(Anjani Mittal)
19年1月17日在4:43
@AnjaniMittal在第26级添加了一个新的api,您可以捕获一个窗口developer.android.com/reference/android/view/…
– Alijandro
19年4月6日在6:59
#3 楼
这是因为SurfaceView使用OpenGL线程绘制并直接绘制到硬件缓冲区。您必须使用glReadPixels(可能还需要使用GLWrapper)。查看线程:Android OpenGL屏幕截图
评论
大链接。接受答案中的代码似乎要求以下条件:GL10 gl在我的SurfaceView中的哪里创建此对象?
–user3011902
2015年1月7日,10:50
当我阅读有关您的问题的信息时,我深信您的方法应该有效。在stackoverflow上有关于它的链接:stackoverflow.com/questions/4742868/…我的方法使用GLSurfaceView和GLWrapper。请参阅:stackoverflow.com/questions/5979069/…
–Zielony
15年1月7日,11:05
更新的代码,请参阅问题。它仍然不能正常工作。
–user3011902
15年1月7日,11:53
使用Canvas渲染到Surface永远不会加速硬件(从5.0开始),因此尝试使用GLES捕获像素没有帮助。如果启用了硬件加速,则渲染到视图将使用GLES,但是惯用的图形缓存方法更加可靠。
–淡淡
2015年1月7日在16:47
#4 楼
在API 28中不推荐使用Update 2020 view.setDrawingCacheEnabled(true)。如果您使用的是普通View,则可以使用指定的位图创建画布。然后,要求视图在画布上绘制并返回由Canvas填充的位图。或者您可以在使用视图绘制画布之前用默认颜色填充画布: br /> /**
* Copy View to Canvas and return bitMap
*/
fun getBitmapFromView(view: View): Bitmap? {
var bitmap =
Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
上述方法不适用于将其绘制为屏幕截图中的孔的表面视图。
要从Android 24开始使用曲面视图,您需要使用Pixel Copy。
/**
* Copy View to Canvas and return bitMap and fill it with default color
*/
fun getBitmapFromView(view: View, defaultColor: Int): Bitmap? {
var bitmap =
Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888)
var canvas = Canvas(bitmap)
canvas.drawColor(defaultColor)
view.draw(canvas)
return bitmap
}
这种方法可以截取Surface Vie的任何子类的屏幕快照,例如VideoView
/**
* Pixel copy to copy SurfaceView/VideoView into BitMap
*/
fun usePixelCopy(videoView: SurfaceView, callback: (Bitmap?) -> Unit) {
val bitmap: Bitmap = Bitmap.createBitmap(
videoView.width,
videoView.height,
Bitmap.Config.ARGB_8888
);
try {
// Create a handler thread to offload the processing of the image.
val handlerThread = HandlerThread("PixelCopier");
handlerThread.start();
PixelCopy.request(
videoView, bitmap,
PixelCopy.OnPixelCopyFinishedListener { copyResult ->
if (copyResult == PixelCopy.SUCCESS) {
callback(bitmap)
}
handlerThread.quitSafely();
},
Handler(handlerThread.looper)
)
} catch (e: IllegalArgumentException) {
callback(null)
// PixelCopy may throw IllegalArgumentException, make sure to handle it
e.printStackTrace()
}
}
评论
我的代码基于该帖子的已接受答案。仍然无法使用单击此处获取答案。它将为您提供帮助:)
@TastyLemons Pls删除单词“工作代码:”。它会误导读者。