web-dev-qa-db-ja.com

プログラムでスクリーンショットを撮っても、surfaceVIewのコンテンツはキャプチャされません

スクリーンショットをキャプチャできるようにしたいアプリがあります。レイアウトの背景は、リアカメラからのビデオを表示するsurfaceViewです。次のコードはスクリーンショットを撮ることができますが、surfaceViewのコンテンツは黒として保存されます。コードは次のとおりです。

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

コードを更新しました。次に、2つのビットマップを作成します。1つはレイアウトxml用、もう1つはsurfaceView用です。次に、それらを1つのビットマップにオーバーレイします。しかし、surfaceViewビットマップはまだ黒です

14
mremremre1

私はついにこれを解決しました。以下に、レイアウトのスクリーンショット、意図せずにカメラから写真を撮る方法、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;
    }
}

簡単なスクリーンショットを撮りたい場合(カメラフィードは必要ありません)、TakeScreenshotメソッドを単独で使用できます。

サーフェスビューのスクリーンショットを撮りたい場合は、mPictureを使用してサーフェスビューから直接行うことはできません。キャプチャした画像を背景として設定し、TakeScreenshotを呼び出してスクリーンショットを撮ります(上記を参照)。

意図的に他のカメラアプリを呼び出さずにカメラで写真を撮りたい場合は、上記のコードのmPictureとsurfaceViewのものでtakePictureを使用してください。

前のコードを「そのまま」使用した場合は、レイアウトコンテンツ(ボタン、画像ビューなど)のスクリーンショットを撮り、カメラからの画像を背景として設定します。

以下に、前のコードの基本的なレイアウトxmlも示します。

<?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>

インポートする必要があるものをインポートすることを忘れないでください

11
mremremre1

コメント( https://groups.google.com/forum/#!topic/Android-developers/jYjvm7ItpXQ )によるとAndroidフレームワークエンジニア、SurfaceViewは通常のビューのように扱われません:

表面ビューは実際にはウィンドウの背後にあり、ウィンドウに穴を開けて表示します。したがって、ウィンドウ内でその上に物を置くことはできますが、ウィンドウ内の何も背後に表示されることはありません。

これはアプリウィンドウの「背後」にあるため、ウィンドウのビューの図面には含まれません。その場合の唯一の方法は、SurfaceViewクラスにメソッドを提供して、提供されたCanvas/Bitmapに独自のコンテンツを描画することだと思います。

3
Kai