2026/03/03

오늘의 이야기

이미지 저장 예시



앱을 만들다 보면 프로필 가져오기 기능을 구현해 보는 경우가 간혹 생긴 게 된다. 


 


오늘은 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




 





댓글 없음:

댓글 쓰기

오늘의 이야기

  청사옆 숲길 점심을 먹고 나서면 이제 제법 낮볓이 뜨겁다.  청사가 가까이에 있어 주변에 조성된 숲길(?)이 이제 제법 시원하게 느껴 지기도 한다. 점심을 먹고 나면 잠깐 낮잠이 오히려 필요한 것 같은 요즘이기는 하나, 갈수록 무거워지는 몸을 지탱...