正在开发一款简单的相机应用。我有代码截取整个活动的截图并将其写入Sd卡。问题是Surfaceview返回黑屏。
我想知道如何独立拍摄surfaceview的截图。 这是获取整个活动屏幕截图的代码。
findViewById(R.id.screen).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final RelativeLayout layout = (RelativeLayout) findViewById(R.id.RelativeLayout1);
layout.setVisibility(RelativeLayout.GONE);
Bitmap bitmap = takeScreenshot();
Toast.makeText(getApplicationContext(),"Please Wait", Toast.LENGTH_LONG).show();
saveBitmap(bitmap);
}
});
}
public Bitmap takeScreenshot() {
View rootView = findViewById(android.R.id.content).getRootView();
rootView.setDrawingCacheEnabled(true);
return rootView.getDrawingCache();
}
public void saveBitmap(Bitmap bitmap) {
final MediaPlayer cheer = MediaPlayer.create(PicShot.this, R.raw.shutter);
cheer.start();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Image-"+ n +".png";
final RelativeLayout layout = (RelativeLayout) findViewById(R.id.RelativeLayout1);
File imagePath = new File(Environment.getExternalStorageDirectory() + "/" + fname);
FileOutputStream fos;
try {
fos = new FileOutputStream(imagePath);
bitmap.compress(CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
layout.setVisibility(RelativeLayout.VISIBLE);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
Uri uri = Uri.fromFile(imagePath);
share.putExtra(Intent.EXTRA_STREAM,uri);
startActivity(Intent.createChooser(share, "Share Image"));
} catch (FileNotFoundException e) {
Log.e("GREC", e.getMessage(), e);
} catch (IOException e) {
Log.e("GREC", e.getMessage(), e);
}
}
答案 0 :(得分:12)
SurfaceView的曲面独立于绘制View元素的曲面。因此,捕获View内容将不包括SurfaceView。
您需要单独捕获SurfaceView内容并执行自己的合成步骤。捕获的最简单方法可能是重新渲染内容,但使用屏幕外位图作为目标而不是表面。如果您使用GLES渲染到屏幕外的pbuffer,则可以在交换缓冲区之前使用glReadPixels()
。
更新: Grafika's“来自相机的纹理”活动演示了使用OpenGL ES处理来自相机的实时视频。 EglSurfaceBase#saveFrame()
显示了如何将GLES渲染捕获到位图。
更新:另请参阅this answer,其中提供了更多背景信息。
答案 1 :(得分:2)
如果您的情况允许,使用TextureView
代替SurfaceView
将使此问题更容易解决。它有一个方法getBitmap()
,可以在Bitmap
上返回当前帧的TextureView
。
答案 2 :(得分:2)
只需复制并粘贴过去的代码
注意:仅适用于API级别> = 24
private void takePhoto() {
// Create a bitmap the size of the scene view.
final Bitmap bitmap = Bitmap.createBitmap(surfaceView.getWidth(), surfaceView.getHeight(),
Bitmap.Config.ARGB_8888);
// Create a handler thread to offload the processing of the image.
final HandlerThread handlerThread = new HandlerThread("PixelCopier");
handlerThread.start();
// Make the request to copy.
PixelCopy.request(holder.videoView, bitmap, (copyResult) -> {
if (copyResult == PixelCopy.SUCCESS) {
Log.e(TAG,bitmap.toString());
String name = String.valueOf(System.currentTimeMillis() + ".jpg");
imageFile = ScreenshotUtils.store(bitmap,name);
} else {
Toast toast = Toast.makeText(getViewActivity(),
"Failed to copyPixels: " + copyResult, Toast.LENGTH_LONG);
toast.show();
}
handlerThread.quitSafely();
}, new Handler(handlerThread.getLooper()));
}
答案 3 :(得分:0)
我与ExoPlayer有关的情况,需要获取当前帧的位图。
使用API> =24。
private val copyFrameHandler = Handler()
fun getFrameBitmap(callback: FrameBitmapCallback) {
when(val view = videoSurfaceView) {
is TextureView -> callback.onResult(view.bitmap)
is SurfaceView -> {
val bitmap = Bitmap.createBitmap(
videoSurfaceView.getWidth(),
videoSurfaceView.getHeight(),
Bitmap.Config.ARGB_8888
)
copyFrameHandler.removeCallbacksAndMessages(null)
PixelCopy.request(view, bitmap, { copyResult: Int ->
if (copyResult == PixelCopy.SUCCESS) {
callback.onResult(bitmap)
} else {
callback.onResult(null)
}
}, copyFrameHandler)
}
else -> callback.onResult(null)
}
}
fun onDestroy() {
copyFrameHandler.removeCallbacksAndMessages(null)
}
interface FrameBitmapCallback {
fun onResult(bitmap: Bitmap?)
}
答案 4 :(得分:0)
这就是我的方法。将此方法放在一些Util类中
@model trial.Models.RetrieveDemoEntities
@{
ViewBag.Title = "Index";
}
<h2>Retrieving Data From Database</h2>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<style type="text/css">
table, td, th {
border: 1px solid green;
}
th {
background-color: Pink;
color: white;
}
</style>
</head>
<body>
<table style="margin-left: 25%; margin-top: 10px; border: 2px solid LightGray;">
<tr>
<th>ID</th>
<th>Name</th>
<th>Date_of_birth</th>
<th>Blood Pressure</th>
</tr>
@foreach (var item in Model.patient_infoes)
{
<tr>
<td>@item.ID</td>
<td>@item.Name</td>
<td>@item.Date_of_Birth</td>
<td>@Blood_pressure</td>
</tr>
}
</table>
</body>
</html>
用法:
/**
* 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()
}
}
注意:视频视图是SurfaceView的子类,因此该方法也可以获取视频视图的屏幕截图
答案 5 :(得分:0)
对于那些正在寻找API <= 24的解决方案的人来说,这就是我的解决方法。当用户单击捕获按钮时,关闭预览captureSession并创建一个新的captureSession用于静止图像捕获。
mCaptureSession.stopRepeating();
mCaptureSession.abortCaptures();
mCaptureSession.close();
mCaptureSession = null;
mPreviewReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.JPEG, MAX_IMAGES);
mPreviewReader.setOnImageAvailableListener(mImageAvailableListener, mBackgroundHandler);
mCameraDevice.createCaptureSession(
Arrays.asList(mPreviewReader.getSurface()),
//Arrays.asList(mSurface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
mCaptureSession = cameraCaptureSession;
try {
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mPreviewReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(mOrientation));
Log.d(TAG, "Capture request created.");
mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException cae) {
Log.d(TAG, cae.toString());
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
}
},
mBackgroundHandler
);
然后在onImageAvailableListener上,您可以使用imageReader.acquireNextImage()
获取图像以获取仍然捕获的图像。