본문 바로가기
[ Developer ]/Android

[Android] 안드로이드 카메라 구동시키기

by 김현섭. 2016. 8. 2.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
Android Camera

앱에서 카메라를 접근해보기 위해서 우선 프로젝트를 하나 생성한다
우선 권한을 획득하기 위해서 AndroidManifest.xml에서 두 가지를 추가해준다


*AndroidManifest.xml
1
2
3
4
5
    <!-- 카메라 사용을 사용자에게 알림 -->
    <uses-feature android:name="android.hardware.camera2" />
 
    <!-- 사진을 외장 메모리에 저장하기 위해서 외장 USB에 쓰기 권한 요청 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
cs


그런 후 activity_main에 와서 Text View를 버튼으로 바꿔주고 ImageView를 설정해준다




각각에 아이디를 주고 layout을 지정해준다
그런 후 MainActivity에 와서 Button과 ImageView를 가져온다




이제는 BUtton을 누르면 카메라 앱이 열리게 끔 설정을 해주면 된다
우선 카메라가 있는지에 대해 조사하기 위해서 메소드를 생성해서 조사하는 로직을 작성해본다
로직은 다음과 같다

* isExistCameraApplication - 카메라 처리 App 있는지에 대해 확인 메소드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    /**
     * Android에 Camera Application이 설치되어 있는지 확인
     * @return
     */
    private boolean isExistsCameraApplication() {
 
        // Android의 모든 Application을 얻어온다
        PackageManager packageManager = getPackageManager();
 
        // Camera Application
        Intent cameraApp = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 
        // MediaStore.ACTION_IMAGE_CAPTURE의 Intent를 처리할 수 있는 Application 정보 가져옴
        List<ResolveInfo> cameraApps = packageManager.queryIntentActivities(cameraApp, PackageManager.MATCH_DEFAULT_ONLY);
 
        return cameraApps.size() > 0;
    }
cs


위의 메소드를 이용해서 카메라를 처리해줄 수 있는 App이 있는지에 대해 검사를 해주면 된다
이제는 Button을 클릭 시 카메라를 확인하는 메소드를 검사하고 카메라 앱을 실행 시켜주는 로직을 작성해준다

* btnTakePicture OnClick
1
2
3
4
5
6
7
8
9
10
11
12
13
        btnTakePicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                // Camera App이 있는지 조사
                if ( isExistsCameraApplication() ) {
                    // Camera Application을 실행
                    Intent cameraApp = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                    startActivityForResult(cameraApp, 10000);
                }
 
            }
        });
cs


Android Studio에서는 확인이 불가능 하지만 폰으로 진행을 하면 확인을 할 수 있다
이제는 사진을 촬영하고 다시 앱으로 보내 앱 내에서 보여지는 방식을 진행해본다




위의 구문을 OnClick안에 넣어주면 된다 그러나 어느 곳에 이미지를 넣을지를 적어주면 된다
이제 어느 위치에 넣을지 메소드로 처리를 해본다
처리를 위해서 하나의 클래스 파일을 추가해준다


* PermissionRequester - Class File
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
 
/**
 * Created by Minchang Jang on 2016-06-12.
 */
public class PermissionRequester {
 
    /**
     * 요청 AndroidOS의 버젼이 마쉬멜로우 이상 버젼이 아닐 경우
     */
    public static final int NOT_SUPPORT_VERSION = 2;
 
    /**
     * 요청 권한을 이미 가지고 있을 경우
     */
    public static final int ALREADY_GRANTED = -1;
 
    /**
     * 권한을 System에게 요청한 경우
     * Activity의 onRequestPermissionsResult() 로 결과 리턴됨.
     */
    public static final int REQUEST_PERMISSION = 0;
 
    private Activity context;
    private Builder builder;
 
    private void setBuilder(Builder builder) {
        this.builder = builder;
    }
 
    private PermissionRequester(Activity context) {
        this.context = context;
    }
 
    public int request(final String permission, final int requestCode, final OnClickDenyButtonListener denyAction) {
 
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            /*
             * 해당 App이 특정 권한을 가지고 있는지 검사함.
             * 리턴결과는 PackageManager.PERMISSION_DENIED 와 PackageManager.PERMISSION_GRANTED로 나눠짐.
             * PackageManager.PERMISSION_DENIED : 권한이 없음
             * PackageManager.PERMISSION_GRANTED : 권한이 있음.
             */
            int permissionCheck = ContextCompat.checkSelfPermission(context, permission);
 
            /*
             * 해당 권한이 없을 경우 처리 방법
             */
            if (permissionCheck == PackageManager.PERMISSION_DENIED) {
 
                /*
                 * 권한을 취득할 때 사용자로부터 확인을 받아야 하는지 확인
                 * 여기서 true가 나올 경우는 해당 앱에서 한번이라도 권한을 Deny한 경우일 때 말고는 없음.
                 * 권한에 대해서 허가하지 않은 경우 다시 한번 권한의 취득을 위해 사용자에게 이유를 고지해야 함.
                 * Marshmellow 버젼 이상부터 사용가능함.
                 */
                if (context.shouldShowRequestPermissionRationale(permission)) {
 
                    /*
                     * 권한 취득해야 하는 이유를 Dialog 등을 통해서 알린다.
                     */
                    AlertDialog.Builder dialog = new AlertDialog.Builder(context);
                    dialog.setTitle(builder.getTitle())
                            .setMessage(builder.getMessage())
                            .setPositiveButton(builder.getPositiveButtonName(), new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    /*
                                     * 권한의 취득을 요청한다.
                                     * 취득하고자 하는 권한을 배열에 넣고 요청한다.
                                     * 뒤에 들어가는 파라미터(requestCode)는 onRequestPermissionsResult() 에서 권한 취득 결과에서 사용된다.
                                     * startActiviryForResult의 Request Code와 유사함.
                                     */
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                        context.requestPermissions(new String[]{permission}, requestCode);
                                    }
                                }
                            })
                            .setNegativeButton(builder.getNegativeButtonName(), new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    denyAction.onClick(context);
                                }
                            }).create().show();
 
                    return REQUEST_PERMISSION;
                } else {
                    /*
                     * 권한의 취득 요청을 처음 할 때
                     * 권한의 취득을 요청한다.
                     * 취득하고자 하는 권한을 배열에 넣고 요청한다.
                     * 뒤에 들어가는 파라미터(1000)는 onRequestPermissionsResult() 에서 권한 취득 결과에서 사용된다.
                     * startActiviryForResult의 Request Code와 유사함.
                     */
                    context.requestPermissions(new String[]{permission}, requestCode);
                    return REQUEST_PERMISSION;
                }
 
            } else {
                /*
                 * 이미 권한을 가지고 있을 경우
                 * 해야할 일을 수행한다.
                 */
                return ALREADY_GRANTED;
            }
 
        }
 
        return NOT_SUPPORT_VERSION;
    }
 
    public static class Builder {
 
            private PermissionRequester requester;
 
            public Builder(Activity context) {
                requester = new PermissionRequester(context);
            }
 
            private String title = "권한 요청";
            private String message = "기능의 사용을 위해 권한이 필요합니다.";
            private String positiveButtonName = "네";
            private String negativeButtonName = "아니요";
 
            public String getTitle() {
                return title;
            }
 
            public Builder setTitle(String title) {
                this.title = title;
            return this;
            }
 
            public String getMessage() {
                return message;
            }
 
            public Builder setMessage(String message) {
                this.message = message;
                return this;
            }
 
            public String getPositiveButtonName() {
                return positiveButtonName;
            }
 
            public Builder setPositiveButtonName(String positiveButtonName) {
                this.positiveButtonName = positiveButtonName;
                return this;
            }
 
            public String getNegativeButtonName() {
                return negativeButtonName;
            }
 
            public Builder setNegativeButtonName(String negativeButtonName) {
                this.negativeButtonName = negativeButtonName;
                return this;
            }
 
            public PermissionRequester create() {
                this.requester.setBuilder(this);
                return this.requester;
            }
 
    }
 
    public interface OnClickDenyButtonListener {
 
        public void onClick(Activity activity);
 
    }
 
}
cs


하나의 클래스를 생성하고 위의 소스를 패키지를 제외하고 지운 후 붙여넣는다
마쉬멜로우 버전에서 권한을 체크해야 하는 부분을 간단하게 하려는 클래스 소스파일이다
위의 권한을 하는 이유는 외부 저장소에 쓰기 권한을 받아야 하기 때문이다

이제 외부 저장소에 파일을 쓰는 권한을 받고 사진 파일의 이름과 저장 위치를 지정한 후
파일을 저장하는 메소드를 작성한다


* savePictureFile 사진 파일 외부 저장소에 저장하는 메소드
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    /**
     * Camera에서 찍은 사진 파일을 외부 저장소에 저장
     * @return
     */
    private File savePictureFile() {
 
        // 외부 저장소 쓰기 권한 받기
        PermissionRequester.Builder requester = new PermissionRequester.Builder(this);
 
        int result = requester  .create()
                                .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, 20000new PermissionRequester.OnClickDenyButtonListener() {
                                    @Override
                                    public void onClick(Activity activity) {
 
                                    }
                                });
 
        // 권한 사용 거부를 누르지 않았을 때
        if ( result == PermissionRequester.ALREADY_GRANTED || result == PermissionRequester.REQUEST_PERMISSION ) {
 
            // 사진 파일의 이름 설정
            String timestamp = new SimpleDateFormat("yyyyMMdd HHmmss").format(new Date());
            String fileName = "IMG_" + timestamp;
 
            // 사진 파일 저장 위치 설정
            File pictureStorage = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MYAPP/");
 
            // 사진 파일 저장될 폴더가 없다면 생성함
            if ( !pictureStorage.exists() ) {
                pictureStorage.mkdirs();
            }
 
            try {
                File file = File.createTempFile(fileName, ".jpg", pictureStorage);
                
                // 사진 파일의 절대 경로를 얻어온다
                imagePath = file.getAbsolutePath();
                
                return file;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
cs


위와 같이 변경후에 onClick 이벤트에서 아래의 부분을 변경해주면 된다




이제는 Main에서 StartActivityForResult를 했기 때문에 OnActivityResult를 작성해 준다
onCreate 메소드 밑에 onActivityResult를 작성해주면 된다

imagePath가 필요하므로 멤버 변수로 imagePath를 하나 추가해준다




이제 이미지를 보여주기 위해서 onActivityResult에서 이미지를 표현하는 로직을 작성해주면 된다
로직은 다음과 같다


* onActivityResult - 인텐트 결과를 받아온다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 
        if ( requestCode == 10000 && resultCode == RESULT_OK ) {
            // 사진을 ImageVIew에 보여줌줌
            BitmapFactory.Options factory = new BitmapFactory.Options();
            factory.inJustDecodeBounds = true;
            
            BitmapFactory.decodeFile(imagePath);
            
            factory.inJustDecodeBounds = false;
            factory.inPurgeable = true;
 
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath, factory);
            ivPicture.setImageBitmap(bitmap);
       }
    }
cs


이제는 onCreate를 조금 수정 후 실행을 해본다


* onCreate
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
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        btnTakePicture = (Button) findViewById(R.id.btnTakePicture);
        ivPicture = (ImageView) findViewById(R.id.ivPicture);
 
        btnTakePicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                // Camera App이 있는지 조사
                if ( isExistsCameraApplication() ) {
                    // Camera Application을 실행
                    Intent cameraApp = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 
                    // 찍은 사진을 보관할 파일 객체를 만들어서 보냄
                    File picture = savePictureFile();
 
                    if ( picture != null ) {
                        cameraApp.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picture));
                        startActivityForResult(cameraApp, 10000);
                    }
                }
            }
        });
    }
cs


Android Studio에서는 카메라가 되지 않아 확인할 수 없지만
핸드폰을 연결해서 시도해보면 사진이 찍히고 확인을 눌렀을 때 사진이 앱 내로 들어가는 것을 볼 수 있다

추가적으로 찍힌 사진을 갤러리에 추가를 해본다




이전에 파일을 외부 저장소에 저장하는 savePictureFile 메소드에서 추가를 해주면 된다


* MainActivity 전체 소스
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
public class MainActivity extends AppCompatActivity {
 
    private Button btnTakePicture;
    private ImageView ivPicture;
 
    private String imagePath;
 
    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        btnTakePicture = (Button) findViewById(R.id.btnTakePicture);
        ivPicture = (ImageView) findViewById(R.id.ivPicture);
 
        btnTakePicture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                // Camera App이 있는지 조사
                if ( isExistsCameraApplication() ) {
                    // Camera Application을 실행
                    Intent cameraApp = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 
                    // 찍은 사진을 보관할 파일 객체를 만들어서 보냄
                    File picture = savePictureFile();
 
                    if ( picture != null ) {
                        cameraApp.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picture));
                        startActivityForResult(cameraApp, 10000);
                    }
                }
            }
        });
    }
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 
        if ( requestCode == 10000 && resultCode == RESULT_OK ) {
            // 사진을 ImageVIew에 보여줌줌
            BitmapFactory.Options factory = new BitmapFactory.Options();
            factory.inJustDecodeBounds = true;
 
            BitmapFactory.decodeFile(imagePath);
 
            factory.inJustDecodeBounds = false;
            factory.inPurgeable = true;
 
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath, factory);
            ivPicture.setImageBitmap(bitmap);
       }
    }
 
 
    /**
     * Android에 Camera Application이 설치되어 있는지 확인
     * @return
     */
    private boolean isExistsCameraApplication() {
 
        // Android의 모든 Application을 얻어온다
        PackageManager packageManager = getPackageManager();
 
        // Camera Application
        Intent cameraApp = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 
        // MediaStore.ACTION_IMAGE_CAPTURE의 Intent를 처리할 수 있는 Application 정보 가져옴
        List<ResolveInfo> cameraApps = packageManager.queryIntentActivities(cameraApp, PackageManager.MATCH_DEFAULT_ONLY);
 
        return cameraApps.size() > 0;
    }
 
    /**
     * Camera에서 찍은 사진 파일을 외부 저장소에 저장
     * @return
     */
    private File savePictureFile() {
 
        // 외부 저장소 쓰기 권한 받기
        PermissionRequester.Builder requester = new PermissionRequester.Builder(this);
 
        int result = requester  .create()
                                .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, 20000new PermissionRequester.OnClickDenyButtonListener() {
                                    @Override
                                    public void onClick(Activity activity) {
 
                                    }
                                });
 
        // 권한 사용 거부를 누르지 않았을 때
        if ( result == PermissionRequester.ALREADY_GRANTED || result == PermissionRequester.REQUEST_PERMISSION ) {
 
            // 사진 파일의 이름 설정
            String timestamp = new SimpleDateFormat("yyyyMMdd HHmmss").format(new Date());
            String fileName = "IMG_" + timestamp;
 
            // 사진 파일 저장 위치 설정
            File pictureStorage = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MYAPP/");
 
            // 사진 파일 저장될 폴더가 없다면 생성함
            if ( !pictureStorage.exists() ) {
                pictureStorage.mkdirs();
            }
 
            try {
                File file = File.createTempFile(fileName, ".jpg", pictureStorage);
 
                // 사진 파일의 절대 경로를 얻어온다
                imagePath = file.getAbsolutePath();
 
                // 찍힌 사진을 갤러리에 저장한다다
                Intent mediaScanIntent = new Intent( Intent.ACTION_MEDIA_SCANNER_SCAN_FILE );
                File f = new File( imagePath );
                Uri contentUri = Uri.fromFile( f );
                mediaScanIntent.setData( contentUri );
                this.sendBroadcast( mediaScanIntent );
 
               return file;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}