반응형
앱을 만들다 보면 프로필 가져오기 기능을 구현해 보는 경우가 간혹 생긴 게 된다.
오늘은 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
또한 compose에서 구현은 아래 site에서 참고했음을 밝힌다.
https://yjyoon-dev.github.io/android/2022/04/09/android-05/
반응형
'모바일 앱(안드로이드)' 카테고리의 다른 글
안드로이드 앱 만들기 : 코드 난독화 해소 (5) | 2022.05.19 |
---|---|
안드로이드 앱 만들기 : 코드 난독화, 축소의 폐해(?) (0) | 2022.05.16 |
안드로이드 앱 만들기 : Jetpack Compose 에 admob banner 달아보기 (feat 인터넷 펌) (2) | 2022.05.09 |
안드로이드 앱 만들기 : 펌 글... google One Tap login ... (Jetpack Compose) (0) | 2022.05.08 |
안드로이드 앱 만들기 : 채팅창 만들어 보기 (Jetpack Compose 에 AndroidView Binding) (0) | 2022.05.03 |