0.기본 변수
private lateinit var binding: ActivityIdRegisterBinding
private var picture_flag = 0
private var fileAbsolutePath: String? = null
1.카메라에서 사진을 찍은 후 갤러리에 저장 후 이미지뷰에 로딩하기
1)Gradle 설정
//테드 퍼미션
implementation "gun0912.ted:tedpermission:2.2.3"
2)메니페스트에 권한 설정 및 프로바이더 추가
Manifest.xml
<!--카메라 갤러리 관련 권한 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
-provider태그안에 authorites에서 com.example.sharelanguage부분을 본인의 패키지명으로 변경한다. (중요)
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.sharelanguage.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
file_paths.xml
-path에서 com.example.sharelanguage부분을 본인의 패키지명으로 변경한다. (중요)
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="Android/data/com.example.sharelanguage/files/Pictures" />
</paths>
3)카메라 관련 권한 설정
-카메라 버튼을 클릭하면 입력값(permis_num) 2번에 해당하는 카메라 관련 권한을 체크하는 팝업창이 뜨고
-권한을 허락하면 카메라로 이동한다.
//4) 카메라
binding.ivCameraRegister.id -> {
//2번 -> 카메라 권한체크
settingPermission(2)
}
//카메라 관련 권한을 설정해주는 함수
// permis_num
// -1번 -> 갤러리 권한
// -2번 -> 카메라 권한
fun settingPermission(permis_num: Int) {
val permis = object : PermissionListener {
//어떠한 형식을 상속받는 익명 클래스의 객체를 생성하기 위해 다음과 같이 작성
override fun onPermissionGranted() {
if (permis_num == 1) {
//갤러리로 이동
move_gallery()
} else if (permis_num == 2) {
//카메라로 이동
move_camera()
}
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
}
}
//1번 -> 갤러리 권한
if (permis_num == 1) {
checkPer_gallery(permis)
}
//2번 -> 카메라 권한
if (permis_num == 2) {
checkPer_camera(permis)
}
}
4)카메라에서 찍은 사진 이미지뷰에 로딩
-카메라에서 찍은 사진을 File형식으로 변환 후 절대경로를 변수에 저장하고 갤러리에 저장하기 위해 Content형식으로 변환한다
*content는 가상 에뮬레이터에서 사용하는 이미지의 경로형식이다.
-카메라에서 사진을 찍은 후 엑티비티로 돌아오는 경우 picture_flag가 2번이 저장된다.
- getUriForFile 메서드의 두번째 입력값에는 패키지이름.fileprovider 형식으로 와야 한다.
//intent를 이용해서 카메라로 이동하는 함수
fun move_camera() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
takePictureIntent.resolveActivity(packageManager)?.also {
//찍은 사진을 File형식으로 변환
val photoFile: File? = try {
createImageFile()
} catch (ex: IOException) {
null
}
//File형식의 Uri를 Content형식의 Uri로 변환
photoFile?.also {
val photoURI: Uri = FileProvider.getUriForFile(
this,
"com.example.sharelanguage.fileprovider",
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
resultLauncher.launch(takePictureIntent)
}
}
//2번 -> 카메라
picture_flag = 2
}
}
-registerForActivityResult에서 카메라에서 갖고온 데이터를 확인하고 picture_flag가 2번(카메라)에 해당하는 조건문에서
카메라에서 찍은 이미지를 이미지뷰에 로딩한다.
//엑티비티에서 받아온 데이터를 반환하는 메서드
resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{
//엑티비티에서 데이터를 갖고왔을 때만 실행
if (it.resultCode == RESULT_OK) {
//1번 -> 갤러리
if (picture_flag == 1) {
//갤러리에서 갖고온 이미지가 있는 경우
it.data?.data?.let { uri ->
//이미지 uri
val imageUri: Uri? = it.data?.data
//image가 있는 경우에만
if (imageUri != null) {
Glide.with(applicationContext).load(imageUri).override(500, 500)
.into(binding.ivProfileImgRegister)
}
}
}
//2번 -> 카메라
else if (picture_flag == 2) {
val file = File(fileAbsolutePath)
var bitmap: Bitmap? = null
//SDK 28버전 미만인 경우 getBitMap 사용
if (Build.VERSION.SDK_INT < 28) {
//카메라에서 찍은 사진을 비트맵으로 변환
bitmap = MediaStore.Images.Media
.getBitmap(contentResolver, Uri.fromFile(file))
//이미지뷰에 이미지 로딩
binding.ivProfileImgRegister.setImageBitmap(bitmap)
} else {
//SDK 28버전 이상인 경우 setImageBitmap 사용
//카메라에서 찍은 사진을 디코딩
val decode = ImageDecoder.createSource(this.contentResolver,
Uri.fromFile(file.absoluteFile))
//디코딩한 사진을 비트맵으로 변환
bitmap = ImageDecoder.decodeBitmap(decode)
//이미지뷰에 이미지 로딩
binding.ivProfileImgRegister.setImageBitmap(bitmap)
//갤러리에 저장
}
if (bitmap != null) {
saveImageFile(file.name, getExtension(file.name), bitmap)
}
}
}
}
5)카메라에서 찍은 사진 갤러리에 저장
//이미지의 확장자를 추출하는 메서드
fun getExtension(fileStr: String): String {
val fileExtension = fileStr.substring(fileStr.lastIndexOf(".") + 1, fileStr.length);
return fileExtension
}
//갤러리에 찍은 사진을 저장하는 메서드
fun saveImageFile(filename: String, mimeType: String, bitmap: Bitmap): Uri? {
//이미지 Uri 생성
//contentValues는 ContentResolver가 사용하는 데이터 정보이다.
var values = ContentValues()
//contentValues의 이름, 타입을 정한다.
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename)
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 파일 저장을 완료하기 전까지 다른 곳에서 해당 데이터를 요청하는 것을 무시
values.put(MediaStore.Images.Media.IS_PENDING, 1)
}
// MediaStore에 파일 등록
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
try {
if (uri != null) {
// 파일 디스크립터 획득
val descriptor = contentResolver.openFileDescriptor(uri, "w")
if (descriptor != null) {
// FileOutputStream으로 비트맵 파일 저장. 숫자는 압축률
val fos = FileOutputStream(descriptor.fileDescriptor)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.close()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 데이터 요청 무시 해제
values.clear()
values.put(MediaStore.Images.Media.IS_PENDING, 0)
contentResolver.update(uri, values, null, null)
}
}
}
} catch (e: java.lang.Exception) {
Log.e("File", "error=")
}
return uri
}
2.갤러리에서 이미지 선택하고 이미지뷰에 로딩하기
//갤러리 관련 권한 체크
fun checkPer_gallery(permis: PermissionListener) {
TedPermission.with(applicationContext)
.setPermissionListener(permis)
.setDeniedMessage("[설정] > [권한] 에서 권한을 허용할 수 있습니다.")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
).check()
}
fun move_gallery() {
val intent = Intent(Intent.ACTION_PICK)
intent.data = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
intent.type = "image/*"
//이미지 여러장 선택하기
//엑티비티이동
resultLauncher.launch(intent)
//1번 -> 갤러리
picture_flag = 1
}
//1번 -> 갤러리
if (picture_flag == 1) {
//갤러리에서 갖고온 이미지가 있는 경우
it.data?.data?.let { uri ->
//이미지 uri
val imageUri: Uri? = it.data?.data
//image가 있는 경우에만
if (imageUri != null) {
Glide.with(applicationContext).load(imageUri).override(500, 500)
.into(binding.ivProfileImgRegister)
}
}
}
전체 코드
package com.example.sharelanguage.Activity.LoginRegister
import android.Manifest
import android.content.ContentValues
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.ImageDecoder
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.view.View
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import androidx.databinding.DataBindingUtil
import com.bumptech.glide.Glide
import com.example.sharelanguage.R
import com.example.sharelanguage.databinding.ActivityIdRegisterBinding
import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.TedPermission
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
class ID_Register : AppCompatActivity(), View.OnClickListener {
private val TAG = "MainActivity"
private lateinit var binding: ActivityIdRegisterBinding
private var picture_flag = 0
private var fileAbsolutePath: String? = null
//갤러리, 카메라에서 데이터(사진) 갖고올 때 사용
lateinit var resultLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_id_register)
//버튼 리스너
binding.btnNextLRegister.setOnClickListener(this) //다음 버튼
binding.btnBackLRegister.setOnClickListener(this) //뒤로가기 버튼
binding.btnRedundancyRegister.setOnClickListener(this)//중복체크 버튼
binding.ivCameraRegister.setOnClickListener(this)//카메라
binding.ivGalleryRegister.setOnClickListener(this)//갤러리
binding.ivSearchDetailRegister.setOnClickListener(this)//비밀번호 보이게하기
//엑티비티에서 받아온 데이터를 반환하는 메서드
resultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{
//엑티비티에서 데이터를 갖고왔을 때만 실행
if (it.resultCode == RESULT_OK) {
//1번 -> 갤러리
if (picture_flag == 1) {
//갤러리에서 갖고온 이미지가 있는 경우
it.data?.data?.let { uri ->
//이미지 uri
val imageUri: Uri? = it.data?.data
//image가 있는 경우에만
if (imageUri != null) {
Glide.with(applicationContext).load(imageUri).override(500, 500)
.into(binding.ivProfileImgRegister)
}
}
}
//2번 -> 카메라
else if (picture_flag == 2) {
val file = File(fileAbsolutePath)
var bitmap: Bitmap? = null
//SDK 28버전 미만인 경우 getBitMap 사용
if (Build.VERSION.SDK_INT < 28) {
//카메라에서 찍은 사진을 비트맵으로 변환
bitmap = MediaStore.Images.Media
.getBitmap(contentResolver, Uri.fromFile(file))
//이미지뷰에 이미지 로딩
binding.ivProfileImgRegister.setImageBitmap(bitmap)
} else {
//SDK 28버전 이상인 경우 setImageBitmap 사용
//카메라에서 찍은 사진을 디코딩
val decode = ImageDecoder.createSource(this.contentResolver,
Uri.fromFile(file.absoluteFile))
//디코딩한 사진을 비트맵으로 변환
bitmap = ImageDecoder.decodeBitmap(decode)
//이미지뷰에 이미지 로딩
binding.ivProfileImgRegister.setImageBitmap(bitmap)
//갤러리에 저장
}
if (bitmap != null) {
saveImageFile(file.name, getExtension(file.name), bitmap)
}
}
}
}
}
override fun onClick(v: View?) {
when (v?.id) {
//1)다음 버튼
binding.btnNextLRegister.id -> {
val intent = Intent(applicationContext, Profile_Register::class.java)
startActivity(intent)
}
//2) 뒤로가기 버튼
binding.btnBackLRegister.id -> {
finish()
}
//3) 중복체크 버튼
binding.btnRedundancyRegister.id -> {
}
//4) 카메라
binding.ivCameraRegister.id -> {
//2번 -> 카메라 권한체크
settingPermission(2)
}
//5) 갤러리
binding.ivGalleryRegister.id -> {
//1번 -> 갤러리 권한체크
settingPermission(1)
}
//6) 비밀번호 보이게하기
binding.ivSearchDetailRegister.id -> {
}
else -> {
}
}
}
//사진을 찍고 이미지를 파일로 저장해 주는 함수
@Throws(IOException::class)
private fun createImageFile(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
//이미지 경로 지정
val storageDir: File? = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${timeStamp}_",
".jpg",
storageDir
).apply {
//절대경로 변수에 저장
fileAbsolutePath = absolutePath
}
}
//이미지의 확장자를 추출하는 메서드
fun getExtension(fileStr: String): String {
val fileExtension = fileStr.substring(fileStr.lastIndexOf(".") + 1, fileStr.length);
return fileExtension
}
fun move_gallery() {
val intent = Intent(Intent.ACTION_PICK)
intent.data = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
intent.type = "image/*"
//이미지 여러장 선택하기
//엑티비티이동
resultLauncher.launch(intent)
//1번 -> 갤러리
picture_flag = 1
}
//intent를 이용해서 카메라로 이동하는 함수
fun move_camera() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
takePictureIntent.resolveActivity(packageManager)?.also {
//찍은 사진을 File형식으로 변환
val photoFile: File? = try {
createImageFile()
} catch (ex: IOException) {
null
}
//File형식의 Uri를 Content형식의 Uri로 변환
photoFile?.also {
val photoURI: Uri = FileProvider.getUriForFile(
this,
"com.example.sharelanguage.fileprovider",
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
resultLauncher.launch(takePictureIntent)
}
}
//2번 -> 카메라
picture_flag = 2
}
}
//갤러리에 찍은 사진을 저장하는 메서드
fun saveImageFile(filename: String, mimeType: String, bitmap: Bitmap): Uri? {
//이미지 Uri 생성
//contentValues는 ContentResolver가 사용하는 데이터 정보이다.
var values = ContentValues()
//contentValues의 이름, 타입을 정한다.
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename)
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 파일 저장을 완료하기 전까지 다른 곳에서 해당 데이터를 요청하는 것을 무시
values.put(MediaStore.Images.Media.IS_PENDING, 1)
}
// MediaStore에 파일 등록
val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
try {
if (uri != null) {
// 파일 디스크립터 획득
val descriptor = contentResolver.openFileDescriptor(uri, "w")
if (descriptor != null) {
// FileOutputStream으로 비트맵 파일 저장. 숫자는 압축률
val fos = FileOutputStream(descriptor.fileDescriptor)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
fos.close()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// 데이터 요청 무시 해제
values.clear()
values.put(MediaStore.Images.Media.IS_PENDING, 0)
contentResolver.update(uri, values, null, null)
}
}
}
} catch (e: java.lang.Exception) {
Log.e("File", "error=")
}
return uri
}
//카메라 관련 권한을 설정해주는 함수
// permis_num
// -1번 -> 갤러리 권한
// -2번 -> 카메라 권한
fun settingPermission(permis_num: Int) {
val permis = object : PermissionListener {
//어떠한 형식을 상속받는 익명 클래스의 객체를 생성하기 위해 다음과 같이 작성
override fun onPermissionGranted() {
if (permis_num == 1) {
//갤러리로 이동
move_gallery()
} else if (permis_num == 2) {
//카메라로 이동
move_camera()
}
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
}
}
//1번 -> 갤러리 권한
if (permis_num == 1) {
checkPer_gallery(permis)
}
//2번 -> 카메라 권한
if (permis_num == 2) {
checkPer_camera(permis)
}
}
//갤러리 관련 권한 체크
fun checkPer_gallery(permis: PermissionListener) {
TedPermission.with(applicationContext)
.setPermissionListener(permis)
.setDeniedMessage("[설정] > [권한] 에서 권한을 허용할 수 있습니다.")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
).check()
}
//카메라 관련 체크
fun checkPer_camera(permis: PermissionListener) {
TedPermission.with(applicationContext)
.setPermissionListener(permis)
.setDeniedMessage("[설정] > [권한] 에서 권한을 허용할 수 있습니다.")
.setPermissions(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).check()
}
}
참고
[Android Studio] Camera 촬영 및 내부/외부 저장소에 저장
[ Kotlin ] 코틀린 안드로이드 카메라 원본 이미지 저장 방법
출처:
https://believecom.tistory.com/724
[BelieveCom]
[Android/Kotlin] 카메라로 사진 찍고 이미지뷰에 넣기
https://kangmin1012.tistory.com/22
[Kotlin] 8장. 앱 개발 - 카메라, 갤러리, 쓰레드
https://velog.io/@hwi_chance/Kotlin-8장.-앱-개발-카메라-갤러리-쓰레드
[android] targetSdkVersion 30, intent.resolveActivity가 null일때
action.IMAGE_CAPTURE가 안될 때 사용
https://shary1012.tistory.com/249
안드로이드 파일의 확장자 알아내기 getFileExtensionFromUrl
https://stickyny.tistory.com/97
'안드로이드 공부 & 앱' 카테고리의 다른 글
[안드로이드] JetPack Navigation 개념 정리 및 예제 (0) | 2022.04.16 |
---|---|
[안드로이드] 버튼 클릭 리스너로 확인하는 옵저버 패턴-1편 (0) | 2022.04.13 |
운동친구 어플 레이아웃 설계화면 (0) | 2022.02.12 |
[안드로이드/코틀린] activityResultLauncher(ActivityForResult 대체) (0) | 2022.02.02 |
[Android/코틀린/PHP] Retrofit2를 이용한 간단한 서버 클라이언트 통신 예제 (0) | 2021.12.05 |
댓글