2013-08-17 14 views
13

Ho un'app che voglio essere in grado di catturare uno screenshot. Lo sfondo del layout è un surfaceView che mostra i video dalla telecamera posteriore. Il seguente codice è in grado di acquisire uno screenshot ma il contenuto di surfaceView viene salvato come nero. Ecco il codice:Acquisire schermate a livello di codice non acquisisce il contenuto della superficie.

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

Ho aggiornato il codice. Ora creo 2 bitmap, 1 per il layout xml e 1 per il surfaceView e quindi li sovrappongo in una singola bitmap. Ma la bitmap SurfaceView è ancora nero

+0

probabilmente lo stesso con la superficie OpenGL e la cattura dello schermo GDI-base Win32. Buona domanda per Android. –

+0

@ David-SkyMesh Vedi la mia risposta se sei interessato – mremremre1

risposta

9

Ho finalmente risolto questo. Di seguito fornisco del codice per chiunque voglia sapere come acquisire schermate di un layout, immagini dalla telecamera senza intento, schermate (sorta di) del contenuto di un surfaceView e salvare la schermata in una cartella:

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

Se si desidera eseguire uno screenshot semplice (non è necessario alcun caricamento della telecamera), è possibile utilizzare solo il metodo TakeScreenshot.

Se si desidera catturare uno screenshot di una superficie con Vista non è possibile eseguire direttamente dalla vista di superficie, utilizzare mPicture, impostare l'immagine acquisita come sfondo e quindi chiamare TakeScreenshot per acquisire lo screenshot. (Come visto sopra)

Se si desidera scattare una foto con la fotocamera senza chiamare un'altra app con fotocamera, utilizzare il TakePicture con mPicture e il materiale SurfaceView dal codice sopra.

Ciò che il codice precedente fa se usato "così com'è" è quello di fare uno screenshot del contenuto del layout (pulsanti, immagini, ecc.) E impostare come sfondo un'immagine dalla fotocamera.

Sotto Ho anche fornire un xml layout di base per il codice precedente:

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

Non dimenticate di importare ciò che deve essere importato

+0

Credo che fare una foto sia un superamento qui. Hai già una fotocamera aperta e una superficie di anteprima. È sufficiente chiedere la richiamata di anteprima (nel tuo caso potrebbe essere [setOneShotPreviewCallback()] (http://developer.android.com/reference/android/hardware/Camera.html#setOneShotPreviewCallback (android.hardware.Camera.PreviewCallback))), e convertire la risultante YUV in bitmap. Questo dovrebbe essere molto più veloce (meno ritardo) di Capture. –

+0

Ciao @ mremremre1, il tuo codice funziona, ma l'immagine viene ruotata, perché questo accade? –

+0

@SagarZala prova a bloccare l'attività in orizzontale.Aggiungi questo al tuo manifest all'interno del android : screenOrientation = "landscape" – mremremre1

3

accordo con il commento (https://groups.google.com/forum/#!topic/android-developers/jYjvm7ItpXQ) da un ingegnere quadro Android, SurfaceView non viene trattato come una vista normale:

La superficie vista è in realtà dietro la finestra, e un foro perforato nella finestra per vederlo. In questo modo puoi sistemare le cose nella finestra, ma nella finestra non può apparire nulla dietro di essa.

Poiché è "DIETRO" la finestra dell'app, di conseguenza il disegno per la vista della finestra non lo contiene. Suppongo che l'unico modo è quello di fornire un metodo nella classe SurfaceView per disegnare il proprio contenuto sulla tela/bitmap fornita.

Problemi correlati