* 본 포스팅은 부스트 코스 안드로이드 프로그래밍 강의를 바탕으로 제작되었습니다.
* [부스트 코스] 안드로이드 프로그래밍 강좌 링크 : www.edwith.org/boostcourse-android/lecture/17103/
레이아웃에 카메라 화면을 미리 보여주고, 버튼을 눌렀을 때, 그 순간을 캡처해서 이미지 뷰에 나타내는 프로젝트를 학습해보았습니다.
자세한 설명은 코드를 제시하면서 해보도록 해보겠습니다.
1.manifests
먼저, 안드로이드 카메라를 사용할려면 매니페스트에 가서 카메라 권한을 부여해야 합니다.
(카메라 권한 부여 : <uses-permission android:name ="android.permission.CAMERA"/>)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication111">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.camera" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2. layout
Button (사진 찍기) : 버튼을 눌렀을 때, 카메라를 통해 미리 보이는 순간을 캡처합니다.
ImageView : 캡처된 순간을 사용자에게 보여줍니다.
SurfaceView : 카메라를 미리 보여줍니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/button"
android:text="사진찍기"/>
</LinearLayout>
<ImageView
android:layout_width="100dp"
android:layout_height="150dp"
android:src="@mipmap/ic_launcher"
android:id="@+id/imageView"
android:layout_gravity="center"/>
<FrameLayout
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center"
android:layout_margin="20dp">
<com.example.myapplication111.CameraSurfaceView
android:layout_width="300dp"
android:layout_height="300dp"
android:id="@+id/surfaceView"/>
</FrameLayout>
</LinearLayout>
3. CameraSufaceView.java
이 클래스는 SurfaceView를 상속하는 클래스입니다. 이 클래스에서는 SurfaceHolder의 Callback 인터페이스를 구현하도록 합니다.
- SurfaceView ->껍데기 역할, SurfaceHolder -> 실제 컨트롤 역할
- 뷰의 상태 변화를 알 수 있도록 Callback 인터페이스를 제공, 인터페이스 구현 시 뷰가 만들어졌을 때, 변경될 때, 없어질 때를 자동으로 메서드가 호출됩니다.
- SurfaceView 객체가 메모리에 만들어질 때 자동으로 호출되는 surfaceCreated 메소드 안에서는 카메라 객체를 오픈하고 카메라 객체에 서피스 홀더 객체를 설정합니다.
- Camera 객체는 단말의 하드웨어 카메라를 참조하며 이 클래스 안에 변수로 선언되어 있습니다.
뷰의 크기가 변경되는 시점에는 미리 보기 화면이 보이도록 만들어줍니다.
- 뷰가 메모리에서 사라지는 시점에는 카메라 참조를 해제합니다.
capture 메서드는 이 객체를 통해 사진을 찍을 수 있도록 합니다.
package com.example.myapplication111;
import android.content.Context;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback{
SurfaceHolder holder;
Camera camera = null;
public CameraSurfaceView(Context context) {
super(context);
init(context);
}
public CameraSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context){
//초기화를 위한 메소드
holder = getHolder();
holder.addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//만들어지는시점
camera = Camera.open();//카메라 객체 참조
try{
camera.setPreviewDisplay(holder);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
//변경
camera.startPreview(); //렌즈로 부터 들어오는 영상을 뿌려줌
camera.stopPreview();
camera.setDisplayOrientation(90);//카메라 미리보기 오른쪽 으로 90 도회전
camera.startPreview();
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
//소멸
camera.stopPreview();//미리보기중지
camera.release();
camera = null;
}
public boolean capture(Camera.PictureCallback callback){
if(camera != null){
camera.takePicture(null,null,callback);
return true;
}
else{
return false;
}
}
}
4. MainActivity.java
- 버튼을 클릭할 때, 카메라 미리 보기를 위해 만들었던 객체의 capture 메서드를 호출하도록 해서 사진을 찍은 결과를 바이트 배열로 받습니다.
- 사진을 찍은 결과를 비트맵 객체로 변환하여 이미지 뷰에 띄워줍니다.
package com.example.myapplication111;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
ImageView imageView;
CameraSurfaceView surfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = findViewById(R.id.imageView);
surfaceView = findViewById(R.id.surfaceView);
Button button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//카메라 사진 캡쳐
capture();
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case 101:
if(grantResults.length > 0){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "카메라 권한 사용자가 승인함",Toast.LENGTH_LONG).show();
}
else if(grantResults[0] == PackageManager.PERMISSION_DENIED){
Toast.makeText(this, "카메라 권한 사용자가 허용하지 않음.",Toast.LENGTH_LONG).show();
}
else{
Toast.makeText(this, "수신권한 부여받지 못함.",Toast.LENGTH_LONG).show();
}
}
}
}
public void capture(){
surfaceView.capture(new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
//bytearray 형식으로 전달
//이걸이용해서 이미지뷰로 보여주거나 파일로 저장
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8; // 1/8사이즈로 보여주기
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); //data 어레이 안에 있는 데이터 불러와서 비트맵에 저장
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int newWidth = 200;
int newHeight = 200;
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
matrix.postRotate(90);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0,0,width,height,matrix,true);
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
imageView.setImageDrawable(new BitmapDrawable(resizedBitmap));//이미지뷰에 사진 보여주기
camera.startPreview();
}
});
}
}
5. 실행화면
학습 후기 : 현재 진행하고 있는 연구에 필요한 항목 중 하나인 안드로이드 카메라를 학습해보았습니다. 액티비티 전환을 통해 바로 스마트폰에 내장되어 있는 카메라를 불러 들일 수도 있지만, 저희 연구의 취지에 맞게 카메라를 미리 띄워 사용자에게 보여주는 카메라 미리보기를 학습해보았습니다. 많은 구글링을 해보았지만, 이해가 잘 되지 않았고, 많은 고민을 했었지만, 수많은 고민들이 부스트 코스 안드로이드 강의가 많이 해결해주었습니다. 부스트 코스 안드로이드 강의는 안드로이드 프로그래밍을 시작하시는 분에게는 많은 도움이 될 것 같습니다.
'Android' 카테고리의 다른 글
[안드로이드 기초 : 버튼 사용하기] (0) | 2021.01.30 |
---|---|
[안드로이드 스튜디오 : 스플래시 화면 구현 하기] (0) | 2021.01.30 |
[부스트코스:안드로이드 프로그래밍] 4. 화면 내비게이션 (0) | 2021.01.30 |
[부스트코스:안드로이드프로그래밍]프로젝트 C. 한줄평 화면으로 전환하기 (0) | 2021.01.30 |
[부스트코스:안드로이드 프로그래밍] 프로젝트 B 좋아요와 한줄평 리스트. (0) | 2021.01.30 |