Android > SurfaceView - 카메라 프리뷰 띄우기
등록일 : 2017-07-06 17:51
조회수 : 94,138
SurfaceView?? ?표면의 뷰??
SurfaceView는 TextView나 ImageView 와 같이 위젯를 표시할 수 있는 View의 일종입니다.
하지만 이 SurfaceVIew라는 이름에서 알 수 있듯이 다른 VIew들과는 달리 직접 SurfaceView가 위젯를 표시하지 않습니다.
다른 View는 화면에 뷰를 표시하는 과정과 사용자와의 상호작용 처리 등이 모두 하나의 쓰레드(메인)에서 처리됩니다.
이러한 특성으로 인해 ANR이 발생합니다.
즉, ANR은 어플리케이션이 5초 이상 동작을 멈췄을 때 발생하는데 이는 같은 쓰레드 내에서 처리되기 때문에 시간이 지연되어 발생하는 것입니다.
그런데 카메라를 사용하기 위해서는 고성능 - 고화질의 프레임이 엄청나게 빠른 속도로 화면을 업데이트를 하여 사람의 눈에는
카메라뷰가 보이는 것입니다.
이런 많은 작업을 하는데 일반 뷰를 사용한다면 분명 쓰레드에 과부하가 걸려서 ANR이 발생할 것입니다.
그렇기 때문에 SurfaceView를 사용하는 것입니다.
이 SurfaceView는 화면 업데이트를 백그라운드 쓰레드로 수행하여 원활하게 뷰를 업데이트해줍니다.
이미지뷰나 텍스트뷰는 뷰 자체가 바로 컨텐츠를 표시하지만,SurfaceView는 조금 특이합니다.
SurfaceView는 단순히 틀 역할을 하고 , 컨텍츠가 표현되는 곳은 SurfaceView 내에 Surface 객체입니다.
위 그림에서 보듯 SurfaceHolder 클래스가 실제로 Surface에 접근하여 화면을 처리해주는 구조를 가지고 있습니다.
SurfaceHolder라는 이름 그대로 Surface 객체를 잡고(Hold) 관리해 주는 것으로 생각하면 될 것 같습니다.
class Preview extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder mHolder;
Preview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
}
}
위에서 말했듯이 SurfaceView를 사용하기 위해서는 SurfaceView객체를 직접 사용하는 것이 아니라 SurfaceView를 상속한다.
또한 SurfaceHolder.Callback을 구현하는 클래스를 생성해야 한다.
SurfaceHolder mHolder;
클래스의 멤버로 SurfaceHolder 객체인 mHolder 객체를 선언. 이 객체를 통해 실제 컨텐츠가 표시되는 영역인 Surface를 관리합니다.
mHolder = getHolder();
getHolder() 메소드를 통해 현재 SurfaceView의 SurfaceHolder 인스턴스를 연결해주고 있는 것을 확인할 수 있습니다.
이로써 mHolder 객체를 통해 이 SurfaceView의 Surface에 접근할 수 있게 되었습니다.
앞의 글에서 SurfaceView는 SurfaceView 자체가 내용을 표시하는 것이 아니라 내부의 SurfaceHolder를 통해 최종적으로는 Surface라는 객체 위에
그 내용을 표시한다고 하였는데, 위의 경우는 아래와 같이 카메라의 영상이 처리된다고 볼 수 있습니다.
그럼, 카메라로 받은 영상을 프리뷰로 뿌리는 예제를 한번 만들어 보도록 하겠습니다.
package com.example.study;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
public class CameraTest extends Activity {
protected static final String TAG = null;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private Camera camera;
private ImageView imageview;
private boolean inProgress;
// 2. 그다음 시작하는게 onCreate. Activity 생성시 잴 먼저 실행된다.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.cameratest);
imageview = (ImageView)findViewById(R.id.imageView1);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(surfaceListener);
//surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
findViewById(R.id.button1).setOnClickListener(
new Button.OnClickListener() {
@Override
public void onClick(View v) {
if(camera !=null && inProgress == false)
{
camera.takePicture(null, null, takePicture);
inProgress = true;
}
}
}
);
}
private Camera.PictureCallback takePicture = new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
// TODO Auto-generated method stub
Log.i(TAG, "샷다 누름 확인");
if(data!=null)
Log.i(TAG, "JPEG 사진 찍었음!");
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
imageview.setImageBitmap(bitmap);
camera.startPreview();
inProgress = false;
}
};
private SurfaceHolder.Callback surfaceListener = new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
camera.release();
camera = null;
Log.i(TAG, "카메라 기능 해제");
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
camera = Camera.open();
Log.i(TAG, "카메라 미리보기 활성");
try {
camera.setPreviewDisplay(holder);
}catch(Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
// TODO Auto-generated method stub
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewSize(width, height);
camera.startPreview();
Log.i(TAG,"카메라 미리보기 활성");
}
};
}
휴대폰 권한은..... 아래처럼...
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.CAMERA"/>