본문 바로가기
Study/Android

surfaceview 에서 screenshot 찍기

by Answer Choi 2015. 2. 11.
반응형


서피스뷰 예제


서피스뷰는 카메라의 프리뷰를 보여줄 때 많이쓰는데요.

 

카메라 프리뷰를 서피스뷰로 보여주는 이유는 카메라의 실시간 영상을 다른뷰로 보여주려면 부하가 많이 걸리지만,


서피스뷰를 이용하면, 그 뒷단에서 작업한 후 서피스뷰를 통해 카메라 프리뷰를 띄워주기 때문에 부하를 줄일 수 있어


많이 쓰입니다.


어플을 만들다 보면 카메라 프리뷰를 스크린샷으로 찍어야 할 때가 있는데요.


저같은 경우에는 카메라 위에 오버레이(이미지및 텍스트)를 띄워서 함께 스크린샷을 찍을려고 이 방법을 사용했습니다.









예를 들면 위와같이 Frame Layout안에 Surface View와 각종 Overlay를 넣은 화면을 생각할 수 있습니다.


그리고 Surface View에는 카메라를 그 위에는 정보에 해당하는 Overlay를 띄어 줄 생각입니다.


우선 Surface View입니다.

 

  1. public class SurfacePreview extends SurfaceView implements SurfaceHolder.Callback{
  2.    
  3.     SurfaceHolder holder;   //서피스홀더
  4.     Camera cam=null;        //카메라
  5.    
  6.     public SurfacePreview(Context context, AttributeSet attrs) {
  7.         super(context, attrs);
  8.         init(context);
  9.     }
  10.     public SurfacePreview(Context context) {
  11.         super(context);
  12.         init(context);
  13.     }
  14.     public void init(Context context){
  15.        
  16.         holder=getHolder();
  17.         holder.addCallback(this);
  18.         if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
  19.             getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
  20.        
  21.        
  22.     }
  23.     @Override
  24.     public void surfaceChanged(SurfaceHolder holder, int format, int width,
  25.             int height) {
  26.         cam.setDisplayOrientation(90);      //카메라 각도를 포트레이트로(90도)
  27.         cam.startPreview();                 //프리뷰 시작
  28.        
  29.     }
  30.     @Override
  31.     public void surfaceCreated(SurfaceHolder holder) {
  32.         cam=Camera.open();                  //카메라 객체를 오픈(퍼미션 되어있어야 됨)
  33.         try{
  34.             cam.setPreviewDisplay(holder);  //프리뷰를 홀더로
  35.         }catch(Exception e){
  36.             e.printStackTrace();
  37.         }
  38.     }
  39.     @Override
  40.     public void surfaceDestroyed(SurfaceHolder holder) {
  41.         cam.stopPreview();
  42.         cam.release();                      //카메라 죽이기
  43.         cam=null;
  44.        
  45.     }  
  46. }

보시는 봐와 같이 custom surfaceview입니다.


위와같이 작성하셔서 스크린샷을 찍으시면 검은 화면으로 나올겁니다.


이게 카메라를 바로 서피스뷰로 뿌리는게 아니라 뒤쪽에서 작업후 뿌리는 거라 다르다고 하네요.


그래서 이 카메라의 서피스뷰를 카메라 프리뷰콜백 함수로 bitmap을 가져올 수 있습니다.


서피스뷰에서 impleament로 카메라 프리뷰 콜백을 추가시켜 주시면 됩니다.


위 서피스뷰 클래스에 아래 코드처럼 impleament를 추가해 주세요.


  1. public class SurfacePreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback

그럼 onPreviewFrame 메소드가 추가됩니다.


onPreviewFrame메소드는 아래처럼 입력해줍니다.


  1.     @Override
  2.     public void onPreviewFrame(byte[] data, Camera camera) {
  3.        
  4.         Camera.Parameters params = camera.getParameters();
  5.         int w = params.getPreviewSize().width;
  6.             int h = params.getPreviewSize().height;
  7.             int format = params.getPreviewFormat();
  8.             YuvImage image = new YuvImage(data, format, w, h, null);
  9.  
  10.             ByteArrayOutputStream out = new ByteArrayOutputStream();
  11.             Rect area = new Rect(00, w, h);
  12.             image.compressToJpeg(area, 100, out);
  13.             Bitmap bm = BitmapFactory.decodeByteArray(out.toByteArray()0, out.size());
  14.            
  15.             Matrix matrix = new Matrix();
  16.         matrix.postRotate(90);
  17.         Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 00,w, h, matrix, true);
  18.             camWeather.shareBitmap=rotatedBitmap;
  19.     }


onPreviewFrame은 매 순간 자동으로 호출이 됩니다.


코드에서 보시면 13번째 줄까지는 카메라의 프리뷰를 bitmap으로 바꿔주는 코드이고요.


15~17까지는 처음 서피스로 뿌릴때 90도 돌려서 뿌렸기 때문에 프리뷰에서도 각도를 맞춰줘야 해서 90도 돌리는


코드입니다.


18번째줄은 오버레이와 합쳐주기위해 메인쪽에 bitmap값을 전달해주기 위해 넣어놓은 코드입니다.


그리고 onPreviewFrame callback함수를 실행하기 위해서는 surfaceCreated 메소드안에 

 

  1. cam.setPreviewCallback(this);

를 추가해 줘야합니다.


그리고 method called after release() exception이 나는걸 방지하기 위해


surfaceDestroyed안에 


  1. cam.setPreviewCallback(null);

를 추가해 주셔야 합니다.


이제 메인쪽(스크린샷을 호출하는 코드가 있는곳)에서 아래의 코드가 호출 됩니다.


  1.     public void capture(){
  2.         Bitmap overlay=Bitmap.createBitmap(shareBitmap.getWidth(),shareBitmap.getHeight(),shareBitmap.getConfig());
  3.         Canvas canvas=new Canvas(overlay);
  4.         canvas.drawBitmap(shareBitmap, 0,0null);
  5.         shareLayout.buildDrawingCache();
  6.         Bitmap bm=shareLayout.getDrawingCache();
  7.         canvas.drawBitmap(bm,0,0,null);
  8.         FileOutputStream out;
  9.        
  10.         String filename=System.currentTimeMillis()+".jpg";
  11.         String temp="/"+filename;
  12.        
  13.         try{
  14.             out=new FileOutputStream(Environment.getExternalStorageDirectory().toString()+temp);
  15.             overlay.compress(Bitmap.CompressFormat.JPEG,100, out);
  16.             Toast.makeText(getApplicationContext(),temp+"에 저장되었습니다", Toast.LENGTH_SHORT).show();
  17.         }catch(Exception e){
  18.             Log.d("screenshot",String.valueOf(e));
  19.             e.printStackTrace();
  20.            
  21.         }
  22.  
  23.        
  24.     }

2~3라인은 서피스뷰쪽에서 전달해준 비트맵을 가져와서 새로운 비트맵을 그리는 코드입니다.


같은 크기로 그려주기위해 속성을 그대로 줬습니다.


그리고 canvas를 그려주고요.


4번줄에 카메라 프리뷰로 가져온 전달받은 녀석을 그려줬습니다.


5~6은 오버레이에 해당되는 레이아웃입니다. 이건 서피스뷰가 아니라서 원래의 방법대로 스크린샷을~


그리고 7번라인에서 canvas에 오버레이를 그려줍니다.


여기까지하시면 사진위에 오버레이가 그려집니다.


8번부터는 파일로 저장하는 코드입니다.



 


 

 

구현된 코드입니다.

반응형

인기글