원본출처: 티스토리 바로가기
앱을 만들다 보면 프로필 가져오기 기능을 구현해 보는 경우가 간혹 생긴 게 된다.
오늘은 compose을 이용한 구현을 하는 과정에서
갤러리에서 이미지를 불러와서 프로필 사진으로 저장하는 과정을 구현해 보고자 한다.
그림과 같이 구현해 볼 예정이다.
전체 소스의 일부는 아래와 같이 구현이 되었다.
@Composable private fun mainContent(padding: Modifier) { Column(modifier = Modifier .fillMaxHeight() .fillMaxWidth() .padding(16.dp), verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start ) { var imageUri by remember { mutableStateOf<Uri?>(null) } var imageTy by remember { mutableStateOf<Boolean>(false) } val context = LocalContext.current val bitmap = remember { mutableStateOf<Bitmap?>(null) } val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri: Uri? -> imageUri = uri } Card(modifier = Modifier .fillMaxWidth() .height(150.dp) .padding(8.dp) .border(1.dp, Color.Gray), content = { Text(getString(R.string.profileImage)) Row ( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { imageUri?.let { if (Build.VERSION.SDK_INT < 28) { bitmap.value = MediaStore.Images .Media.getBitmap(context.contentResolver,it) } else { val source = ImageDecoder .createSource(context.contentResolver,it) bitmap.value = ImageDecoder.decodeBitmap(source) } bitmap.value?.let { btm -> val baos = ByteArrayOutputStream() btm.compress( Bitmap.CompressFormat.PNG, 100, baos ) val b: ByteArray = baos.toByteArray() val encoded: String = Base64.encodeToString(b, Base64.DEFAULT) editor.putString("profileImage", encoded) editor.commit() Image(bitmap = btm.asImageBitmap(), contentDescription = "profile", contentScale = ContentScale.Crop, modifier = Modifier .clip(shape = RoundedCornerShape(16.dp)) .size(150.dp, 250.dp)) } } if (!"".equals(sp.getString("profileImage","")) && !imageTy) { var encoded = sp.getString("profileImage","") val imageAsBytes: ByteArray = Base64.decode(encoded?.toByteArray(), Base64.DEFAULT) var bitMap = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.size) Image(bitmap = bitMap.asImageBitmap(), contentDescription = "profile", contentScale = ContentScale.Crop, modifier = Modifier .clip(shape = RoundedCornerShape(16.dp)) .size(150.dp, 250.dp)) } Spacer(modifier = Modifier.padding(10.dp)) IconButton(onClick = { imageTy = true launcher.launch("image/*") }) { Icon(imageVector = Icons.Default.PhotoAlbum, contentDescription = "Search Profile", tint = Color.Blue) } } }) Text(getString(R.string.title_translate_ty)) Spacer(modifier = Modifier.padding(10.dp)) Card(modifier = Modifier .fillMaxWidth() .height(60.dp) .padding(8.dp) .border(1.dp, Color.Gray), content = { Row (verticalAlignment = Alignment.CenterVertically) { if (isTranslate.value) { Text(text = getString(R.string.msgAutoTranslate)) } else { Text(text = getString(R.string.msgTranslateNo)) } Switch(checked = isTranslate.value, onCheckedChange = { isTranslate.value = it }) } }) Spacer(modifier = Modifier.padding(10.dp)) Row (verticalAlignment = Alignment.CenterVertically) { Text(getString(R.string.title_master_language)) } Spacer(modifier = Modifier.padding(10.dp)) Card(modifier = Modifier .fillMaxWidth() .height(60.dp) .padding(8.dp) .border(1.dp, Color.Gray), content = { if (languages.size > 0) { DropdownDemo() } }) } }
여기서 살펴 보아야 하는 부분은 다음과 같다.
갤러리에 있는 이미지를 불러오는 실행은 launcher을 호출해서 실행을 하고 있고.
launcher.launch("image/*")
실행된 결과를 아래와 같은 코드 구현을 통해서 bitmap 이미지를 변환 하여 화면에 Image에 속성을 넣어주고 있는 것을 확인할 수 있었다.
imageUri?.let { if (Build.VERSION.SDK_INT < 28) { bitmap.value = MediaStore.Images .Media.getBitmap(context.contentResolver,it) } else { val source = ImageDecoder .createSource(context.contentResolver,it) bitmap.value = ImageDecoder.decodeBitmap(source) } bitmap.value?.let { btm -> val baos = ByteArrayOutputStream() btm.compress( Bitmap.CompressFormat.PNG, 100, baos ) val b: ByteArray = baos.toByteArray() val encoded: String = Base64.encodeToString(b, Base64.DEFAULT) editor.putString("profileImage", encoded) editor.commit() Image(bitmap = btm.asImageBitmap(), contentDescription = "profile", contentScale = ContentScale.Crop, modifier = Modifier .clip(shape = RoundedCornerShape(16.dp)) .size(150.dp, 250.dp)) }
이런 코드를 Kotlin을 통해서 구현을 한다고 하면 아마 다음과 같은 구현이 될 것 같다.
intent을 통해서 갤러리에 있는 이미지를 열어 오는 구현을 하고...
Intent(Intent.ACTION_GET_CONTENT).apply { type = "image/*" startActivityForResult( Intent.createChooser(this, "Get Album"), REQ_SELECT_IMG ) }
onActivityResult을 통해서 받아온 이미지를 처리하는 모양으로 구현이 될 것 같은데...
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { super.onActivityResult(requestCode, resultCode, intent) if (resultCode == Activity.RESULT_OK) { when (requestCode) { REQ_SELECT_IMG -> { val currentImageUri = intent?.data ?: return //이미지 URL val needAdjust = true if (needAdjust) { setAdjImgUri(currentImageUri) //방법 2 } else { setImgUri(currentImageUri) //방법 1 } } } } }
위에서 구현된 코드와 비교를 해 보면 훨씬 간략해졌음을 느끼게 된다.
java 코드로 구현된 예시는 아래 링크를 참고해 보면 좋을 것 같다.
https://billcorea.tistory.com/40
안드로이드 앱 만들기 갤러리 에서 이미지 받아오기 (자동회전 방지)
앱을 만들다가... 갤러리에서 이미지 받아오는 와서 사용하는 것을 구현하고 있는 중인데... 사진이 돌아간다. 흑~ 그래서 구글링 신에서 질문을 했다... 답... Exif 을 구현해서 사진을 돌리는 코드
billcorea.tistory.com
또한 compose에서 구현은 아래 site에서 참고했음을 밝힌다.
https://yjyoon-dev.github.io/android/2022/04/09/android-05/
[Android] 서버에 이미지 업로드하기(feat. Android 10, Compose)
Android 10 이상에서 Jetpack Compose를 통해 기기의 이미지를 선택한 뒤 서버에 업로드해보는 과정을 알아보자
yjyoon-dev.github.io
댓글
댓글 쓰기