Android 以编程方式拍摄屏幕截图不会捕获surfaceVIew的内容
我有一个应用程序,我想能够捕获一个屏幕截图。布局的背景是一个surfaceView,显示来自后置摄像头的视频。下面的代码可以截图,但surfaceView的内容保存为黑色。代码如下:Android 以编程方式拍摄屏幕截图不会捕获surfaceVIew的内容,android,camera,storage,screenshot,surfaceview,Android,Camera,Storage,Screenshot,Surfaceview,我有一个应用程序,我想能够捕获一个屏幕截图。布局的背景是一个surfaceView,显示来自后置摄像头的视频。下面的代码可以截图,但surfaceView的内容保存为黑色。代码如下: btn.setOnClickListener(new OnClickListener() { public void onClick(View v) { Random num = new Random(); int nu=num.nextInt(1000); Bitmap bmp;
btn.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
Random num = new Random();
int nu=num.nextInt(1000);
Bitmap bmp;
CamView.setDrawingCacheEnabled(true);
CamView.buildDrawingCache(true);
Bitmap bmp2 = Bitmap.createBitmap(CamView.getDrawingCache()); //Screenshot of the layout
CamView.setDrawingCacheEnabled(false);
SurView.setDrawingCacheEnabled(true);
SurView.buildDrawingCache(true);
Bitmap bmp1 = Bitmap.createBitmap(SurView.getDrawingCache()); //Screenshot of the surfaceView
SurView.setDrawingCacheEnabled(false);
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth(), bmp1.getHeight(),bmp1.getConfig());
Canvas canvas = new Canvas(bmOverlay); //Overlaying the 2 bitmaps
canvas.drawBitmap(bmp1, 0,0, null);
canvas.drawBitmap(bmp2, 0,0, null);
bmp=bmOverlay;
//saving the file
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bmp.compress(CompressFormat.JPEG, 100, bos);
byte[] bitmapdata = bos.toByteArray();
ByteArrayInputStream fis = new ByteArrayInputStream(bitmapdata);
String picId=String.valueOf(nu);
String myfile="Ghost"+picId+".jpeg";
File dir_image = new File(Environment.getExternalStorageDirectory()+
File.separator+"Ultimate Entity Detector");
dir_image.mkdirs();
try {
File tmpFile = new File(dir_image,myfile);
FileOutputStream fos = new FileOutputStream(tmpFile);
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) > 0) {
fos.write(buf, 0, len);
}
fis.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
我更新了代码。现在我创建了两个位图,一个用于布局xml,一个用于surfaceView,然后将它们叠加到一个位图中。但是surfaceView位图仍然是黑色的根据Android框架工程师的评论(),surfaceView并不像普通视图那样被对待:
曲面视图实际上在您的窗口后面,并在其中打了一个孔
窗户是给你看的。这样你就可以把东西放在上面了
您的窗口,但窗口中的任何内容都不能显示在其后面
由于它位于应用程序窗口的“后面”,因此窗口视图的图形不会包含它。我想唯一的方法就是在SurfaceView类中提供一个方法,将自己的内容绘制到提供的画布/位图中。我最终解决了这个问题。下面我为那些想知道如何拍摄版面截图、无意中从相机拍摄照片、surfaceView内容截图(某种)并将截图保存在文件夹中的人提供一些代码:
public class Cam_View extends Activity implements SurfaceHolder.Callback {
protected static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 0;
private SurfaceView SurView;
private SurfaceHolder camHolder;
private boolean previewRunning;
final Context context = this;
public static Camera camera = null;
private RelativeLayout CamView;
private Bitmap inputBMP = null, bmp, bmp1;
private ImageView mImage;
@SuppressWarnings("deprecation")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera);
CamView = (RelativeLayout) findViewById(R.id.camview);//RELATIVELAYOUT OR
//ANY LAYOUT OF YOUR XML
SurView = (SurfaceView)findViewById(R.id.sview);//SURFACEVIEW FOR THE PREVIEW
//OF THE CAMERA FEED
camHolder = SurView.getHolder(); //NEEDED FOR THE PREVIEW
camHolder.addCallback(this); //NEEDED FOR THE PREVIEW
camHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//NEEDED FOR THE PREVIEW
camera_image = (ImageView) findViewById(R.id.camera_image);//NEEDED FOR THE PREVIEW
Button btn = (Button) findViewById(R.id.button1); //THE BUTTON FOR TAKING PICTURE
btn.setOnClickListener(new OnClickListener() { //THE BUTTON CODE
public void onClick(View v) {
camera.takePicture(null, null, mPicture);//TAKING THE PICTURE
//THE mPicture IS CALLED
//WHICH IS THE LAST METHOD(SEE BELOW)
}
});
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,//NEEDED FOR THE PREVIEW
int height) {
if(previewRunning) {
camera.stopPreview();
}
Camera.Parameters camParams = camera.getParameters();
Camera.Size size = camParams.getSupportedPreviewSizes().get(0);
camParams.setPreviewSize(size.width, size.height);
camera.setParameters(camParams);
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
previewRunning=true;
} catch(IOException e) {
e.printStackTrace();
}
}
public void surfaceCreated(SurfaceHolder holder) { //NEEDED FOR THE PREVIEW
try {
camera=Camera.open();
} catch(Exception e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
finish();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) { //NEEDED FOR THE PREVIEW
camera.stopPreview();
camera.release();
camera=null;
}
public void TakeScreenshot(){ //THIS METHOD TAKES A SCREENSHOT AND SAVES IT AS .jpg
Random num = new Random();
int nu=num.nextInt(1000); //PRODUCING A RANDOM NUMBER FOR FILE NAME
CamView.setDrawingCacheEnabled(true); //CamView OR THE NAME OF YOUR LAYOUR
CamView.buildDrawingCache(true);
Bitmap bmp = Bitmap.createBitmap(CamView.getDrawingCache());
CamView.setDrawingCacheEnabled(false); // clear drawing cache
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bmp.compress(CompressFormat.JPEG, 100, bos);
byte[] bitmapdata = bos.toByteArray();
ByteArrayInputStream fis = new ByteArrayInputStream(bitmapdata);
String picId=String.valueOf(nu);
String myfile="Ghost"+picId+".jpeg";
File dir_image = new File(Environment.getExternalStorageDirectory()+//<---
File.separator+"Ultimate Entity Detector"); //<---
dir_image.mkdirs(); //<---
//^IN THESE 3 LINES YOU SET THE FOLDER PATH/NAME . HERE I CHOOSE TO SAVE
//THE FILE IN THE SD CARD IN THE FOLDER "Ultimate Entity Detector"
try {
File tmpFile = new File(dir_image,myfile);
FileOutputStream fos = new FileOutputStream(tmpFile);
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) > 0) {
fos.write(buf, 0, len);
}
fis.close();
fos.close();
Toast.makeText(getApplicationContext(),
"The file is saved at :SD/Ultimate Entity Detector",Toast.LENGTH_LONG).show();
bmp1 = null;
camera_image.setImageBitmap(bmp1); //RESETING THE PREVIEW
camera.startPreview(); //RESETING THE PREVIEW
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private PictureCallback mPicture = new PictureCallback() { //THIS METHOD AND THE METHOD BELOW
//CONVERT THE CAPTURED IMAGE IN A JPG FILE AND SAVE IT
@Override
public void onPictureTaken(byte[] data, Camera camera) {
File dir_image2 = new File(Environment.getExternalStorageDirectory()+
File.separator+"Ultimate Entity Detector");
dir_image2.mkdirs(); //AGAIN CHOOSING FOLDER FOR THE PICTURE(WHICH IS LIKE A SURFACEVIEW
//SCREENSHOT)
File tmpFile = new File(dir_image2,"TempGhost.jpg"); //MAKING A FILE IN THE PATH
//dir_image2(SEE RIGHT ABOVE) AND NAMING IT "TempGhost.jpg" OR ANYTHING ELSE
try { //SAVING
FileOutputStream fos = new FileOutputStream(tmpFile);
fos.write(data);
fos.close();
//grabImage();
} catch (FileNotFoundException e) {
Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(getApplicationContext(),"Error",Toast.LENGTH_LONG).show();
}
String path = (Environment.getExternalStorageDirectory()+
File.separator+"Ultimate EntityDetector"+
File.separator+"TempGhost.jpg");//<---
BitmapFactory.Options options = new BitmapFactory.Options();//<---
options.inPreferredConfig = Bitmap.Config.ARGB_8888;//<---
bmp1 = BitmapFactory.decodeFile(path, options);//<--- *********(SEE BELOW)
//THE LINES ABOVE READ THE FILE WE SAVED BEFORE AND CONVERT IT INTO A BitMap
camera_image.setImageBitmap(bmp1); //SETTING THE BitMap AS IMAGE IN AN IMAGEVIEW(SOMETHING
//LIKE A BACKGROUNG FOR THE LAYOUT)
tmpFile.delete();
TakeScreenshot();//CALLING THIS METHOD TO TAKE A SCREENSHOT
//********* THAT LINE MIGHT CAUSE A CRASH ON SOME PHONES (LIKE XPERIA T)<----(SEE HERE)
//IF THAT HAPPENDS USE THE LINE "bmp1 =decodeFile(tmpFile);" WITH THE METHOD BELOW
}
};
public Bitmap decodeFile(File f) { //FUNCTION BY Arshad Parwez
Bitmap b = null;
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int IMAGE_MAX_SIZE = 1000;
int scale = 1;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = (int) Math.pow(
2,
(int) Math.round(Math.log(IMAGE_MAX_SIZE
/ (double) Math.max(o.outHeight, o.outWidth))
/ Math.log(0.5)));
}
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return b;
}
}
公共类Cam_视图扩展活动实现SurfaceHolder.Callback{
受保护的静态最终整数捕获\图像\活动\请求\代码=0;
私人表面视图;
私用手提摄像机;
私有布尔预览运行;
最终上下文=此;
公共静态摄像机=空;
私人关系专家卡姆维尤;
私有位图输入bmp=null,bmp,bmp1;
私有图像视图;
@抑制警告(“弃用”)
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.camera);
CamView=(RelativeLayout)findViewById(R.id.CamView);//RelativeLayout或
//XML的任何布局
SurView=(SurfaceView)findviewbyd(R.id.sview);//用于预览的SurfaceView
//摄影机提要的
camHolder=SurView.getHolder();//预览需要
camHolder.addCallback(this);//预览需要
camHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);//预览需要
camera_image=(ImageView)findViewById(R.id.camera_image);//预览所需
按钮btn=(按钮)findViewById(R.id.button1);//用于拍照的按钮
setOnClickListener(新的OnClickListener(){//按钮代码
公共void onClick(视图v){
拍照(空,空,mPicture);//拍照
//该结构称为
//哪种是最后一种方法(见下文)
}
});
}
@凌驾
public void surfacechange(SurfaceHolder holder,int格式,int宽度,//预览需要
整数高度){
如果(预览运行){
camera.stopPreview();
}
Camera.Parameters camParams=Camera.getParameters();
Camera.Size Size=camParams.getSupportedPreviewSizes().get(0);
camParams.setPreviewSize(大小.宽度,大小.高度);
摄像机参数设置(camParams);
试一试{
摄像头。设置预览显示(支架);
camera.startPreview();
预览运行=真;
}捕获(IOE异常){
e、 printStackTrace();
}
}
已创建公共空白表面(表面文件夹持有者){//预览需要
试一试{
camera=camera.open();
}捕获(例外e){
e、 printStackTrace();
Toast.makeText(getApplicationContext(),“Error”,Toast.LENGTH_LONG.show();
完成();
}
}
@凌驾
public void surfaceDestroyed(SurfaceHolder holder){//预览需要
camera.stopPreview();
相机。释放();
摄像机=零;
}
public void TakeScreenshot(){//此方法获取屏幕截图并将其保存为.jpg
随机数=新随机数();
int nu=num.nextInt(1000);//为文件名生成一个随机数
CamView.setDrawingCacheEnabled(true);//CamView或您所在区域的名称
CamView.buildDrawingCache(true);
位图bmp=Bitmap.createBitmap(CamView.getDrawingCache());
CamView.setDrawingCacheEnabled(false);//清除图形缓存
ByteArrayOutputStream bos=新建ByteArrayOutputStream();
压缩(CompressFormat.JPEG,100,bos);
字节[]位图数据=bos.toByteArray();
ByteArrayInputStream fis=新的ByteArrayInputStream(位图数据);
String picId=String.valueOf(nu);
字符串myfile=“Ghost”+picId+“.jpeg”;
File dir\u image=新文件(Environment.getExternalStorageDirectory())+//可能与Win32上的OpenGL surface和基于GDI的屏幕截图相同。对于android来说,这是一个好问题。@David SkyMesh如果您对@Anjaminttal感兴趣,请查看我的答案可能与@Anjaminttal重复这是一个已经回答了5年的问题:PI认为在这里拍照是一个过火的问题。您已经打开了一个摄像头,并有一个预览曲面。这很好它足以请求预览回调(在您的情况下,可能是这样),并将结果YUV转换为位图。这应该更快(更少延迟)而不是捕获。您好@mremremre1,您的代码正在工作,但图像正在旋转,为什么会发生这种情况?@SagarZala尝试锁定横向中的活动。将此添加到android内的清单中:screenOrientation=“横向”@mremremre1我已实现
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/camview">
<SurfaceView
android:id="@+id/sview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
<ImageView
android:id="@+id/camera_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/app_name" />
<Button
android:id="@+id/button1"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
</RelativeLayout>