일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 프래그먼트
- 북한살둘레길
- 어떻게 나답게 살 것인가
- 끝말잇기
- 베드테이블
- 슬기로운 온라인 게임
- 한단어의힘
- 면접
- 소프시스 밤부 좌식 엑슬 테이블
- 좌식테이블
- 베드트레이
- 테트리스
- 자취필수템
- 캐치마인드
- 1일1커밋
- 지지않는다는말
- 목적 중심 리더십
- 소프시스
- 리얼하다
- 함수형 프로그래밍
- 한달브런치북만들기
- 한달독서
- 커스텀린트
- 브런치작가되기
- 한달어스
- T자형인재
- 재택근무
- 안드로이드
- 목적중심리더십
- 아비투스
- Today
- Total
정상에서 IT를 외치다
[Android, Camera] 1. 카메라 프리뷰를 이용한 화면 캡처 및 배경 이미지 적용 본문
안녕하세요. 블랙진입니다.
Capture the camera preview with image in android
이번에는 Camera api 를 사용하여 화면을 캡처하고 캡처한 화면에 배경 이미지를 적용하는 방법에 대해 포스팅 해보겠습니다.
먼저 위 문서를 보면 아래와 같이 Note 가 있습니다.
Camera2 Api 사용을 권장하는 문구입니다. 안드로이드 카메라에 관한 다양한 기능을 사용할 수 있다고 소개되어 있는데 저는 아래와 같은 이유로 Camera api 를 사용했습니다.
1. Camera2 Api 는 안드로이드 5.0 (롤리팝) 이상부터 사용 가능합니다. 저는 안드로이드 4.4 (킷캣) 사용자도 사용 할 수 있게 만들었습니다.
2. Camera2 Api 는 카메라의 많은 기능을 사용 할 수 있지만 그만큼 초기 코드 설정이 복잡합니다. 공부양이 방대하여 일단 Camera Api 를 사용했습니다.
그럼 코드 리뷰 포스팅을 시작해 보겠습니다.~!
0. 상단 바 제거
_style.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<!-- 타이틀 바를 안보이도록 합니다. -->
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
</resources>
카메라를 띄울 화면 상단에 바를 제거하는 코드입니다.
1. 권한 설정
안드로이드 6.0 (마시멜로우) 이상 버전부터는 권한 설정이 필수 입니다. 저희는 카메라 사용과 캡처된 이미지 저장을 위한 2가지 권한이 필요합니다.
_AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
_MainActivity.kt
class MainActivity : AppCompatActivity() {
private val TAG = "MyTag"
private val PERMISSIONS_REQUEST_CODE = 100
private val REQUIRED_PERMISSIONS =
arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 상태바를 안보이도록 합니다.
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN)
// 화면 켜진 상태를 유지합니다.
window.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
setContentView(R.layout.activity_main)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// OS가 Marshmallow 이상일 경우 권한체크를 해야 합니다.
val permissionCheckCamera
= ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
val permissionCheckStorage
= ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (permissionCheckCamera == PackageManager.PERMISSION_GRANTED&& permissionCheckStorage == PackageManager.PERMISSION_GRANTED) {
// 권한 있음
Log.d(TAG, "권한 이미 있음")
startCamera()
} else {
// 권한 없음
Log.d(TAG, "권한 없음")
ActivityCompat.requestPermissions(this,
REQUIRED_PERMISSIONS,
PERMISSIONS_REQUEST_CODE)
}
} else {
// OS가 Marshmallow 이전일 경우 권한체크를 하지 않는다.
Log.d("MyTag", "마시멜로 버전 이하로 권한 이미 있음")
startCamera()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
// grantResults[0] 거부 -> -1
// grantResults[0] 허용 -> 0 (PackageManager.PERMISSION_GRANTED)
Log.d(TAG, "requestCode : $requestCode, grantResults size : ${grantResults.size}")
if(requestCode == PERMISSIONS_REQUEST_CODE) {
var check_result = true
for(result in grantResults) {
if(result != PackageManager.PERMISSION_GRANTED) {
check_result = false
break
}
}
if(check_result) {
startCamera()
} else {
Log.e(TAG, "권한 거부")
}
}
}
}
위와 같은 방법으로 카메라와 저장파일 접근 권한을 획득하시면 됩니다.
접근권한에 관한 설명은 이곳 을 참고해 주세요.
2. 카메라 프리뷰 연결 하기
MainActivity 에 카메라 프리뷰를 연결해 보겠습니다.~!
_activity_main.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" >
<FrameLayout
android:id="@+id/cameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
FrameLayout 에 카메라 프리뷰를 연결합니다.
_MainActivity.kt
private var CAMERA_FACING = Camera.CameraInfo.CAMERA_FACING_FRONT
private var myCameraPreview: MyCameraPreview? = null
MainActivity 에 2개의 변수를 추가해 줍니다.
CAMERA_FACING 은 프리뷰에 보여질 카메라의 전면, 후면을 결정하는 변수입니다.
만약 후면을 사용하고 싶으신 분은 CAMERA_FACING = Camera.CameraInfo.CAMERA_FACING_BACK 변수로 변경해 주시면 됩니다.
private fun startCamera() {
Log.e(TAG, "startCamera")
// Create our Preview view and set it as the content of our activity.
myCameraPreview = MyCameraPreview(this, CAMERA_FACING)
cameraPreview.addView(myCameraPreview)
}
카메라 프리뷰를 설정하기 위한 startCamera() 함수를 추가해 줍니다.
xml 에서 설정한 FrameLayout 에 카메라 프리뷰를 addView 해줍니다.
_MyCameraPreview.java
이 부분은 자바 코드로 작성했습니다.
카메라 프리뷰의 가장 핵심이 되는 부분입니다. 안드로이드 기기 별로 카메라의 방향이 다른데 이에 맞춰 프리뷰를 설정할 수 있도록 했습니다.
public class MyCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private final String TAG = "MyTag";
private SurfaceHolder mHolder;
private int mCameraID;
private Camera mCamera;
private Camera.CameraInfo mCameraInfo;
private int mDisplayOrientation;
public MyCameraPreview(Context context, int cameraId) {
super(context);
Log.d(TAG, "MyCameraPreview cameraId : " + cameraId);
// 0 -> CAMERA_FACING_BACK
// 1 -> CAMERA_FACING_FRONT
mCameraID = cameraId;
try {
// attempt to get a Camera instance
mCamera = Camera.open(mCameraID);
} catch (Exception e) {
// Camera is not available (in use or does not exist)
Log.e(TAG, "Camera is not available");
}
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// get display orientation
mDisplayOrientation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation();
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated");
// retrieve camera's info.
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraID, cameraInfo);
mCameraInfo = cameraInfo;
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed");
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.d(TAG, "surfaceChanged");
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
Log.e(TAG, "preview surface does not exist");
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
Log.d(TAG, "Preview stopped.");
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation);
mCamera.setDisplayOrientation(orientation);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
Log.d(TAG, "Camera preview started.");
} catch (Exception e) {
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
/**
* 안드로이드 디바이스 방향에 맞는 카메라 프리뷰를 화면에 보여주기 위해 계산합니다.
*/
public int calculatePreviewOrientation(Camera.CameraInfo info, int rotation) {
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
}
코드는 위와 같습니다.
MyCameraPreview 는 SurfaceView 를 상속하며 SurfaceHolder.Callback 인터페이스를 가지고 있습니다.
이에 대한 설명은 주석과 카메라 API 문서를 보면 자세히 나와있습니다.
1. MyCameraPreview(Context context, in cameraId)
생성자를 통해 Camera.open 을 합니다. 이때 cameraId 를 받아오는 데 이 값을 통해 전면과 후면 카메라를 결정합니다.
2. surfaceCreated -> surfaceChanged 순서로 동작합니다.
3. surfaceChanged 에서 아래 코드를 통해 안드로이드 디바이스 별 카메라의 회전 방향을 조정해 줍니다.
int orientation = calculatePreviewOrientation(mCameraInfo, mDisplayOrientation);
mCamera.setDisplayOrientation(orientation);
위와 같이 코드를 짜면 프리뷰 화면에 카메라를 띄우는데 성공하셨을 겁니다. 축하합니다.~!~!
프리뷰화면 캡처와 배경 이미지 적용은 다음 포스터에서 진행하겠습니다.
< 참고 사이트 >
'안드로이드' 카테고리의 다른 글
[Android, Camera] 3. 카메라 프리뷰를 이용한 화면 캡처 및 배경 이미지 적용 (6) | 2018.08.22 |
---|---|
[Android, Camera] 2. 카메라 프리뷰를 이용한 화면 캡처 및 배경 이미지 적용 (1) | 2018.08.22 |
[Android, Custom Switch] 안드로이드 스위치 버튼 꾸미기 (4) | 2018.08.17 |
[Android, Customizing Ripple Effect] 내맘대로 리플 효과 변경하기 (0) | 2018.08.13 |
[Android, Ripple Effect, Selector] 클릭시 뷰에 효과 주기 (4) | 2018.08.13 |