왕초보 안드로이드 앱 개발 도전기

8. Unit 2 project: Art Space (아트 갤러리, 이미지 뷰어)

joy2learn 2024. 6. 26. 11:37

Art space 는 일반적으로 예술작품을 전시하거나 예술활동이 이루어지는 장소를 의미합니다. 예를 들어 미술관, 갤러리, 스튜디오 등이 그에 속합니다. Unit 2 project 는 Art space app 을 만드는 것인데, 쉽게 말해서 가지고 있는 사진작품이나 그림을 띄워주는 이미지 뷰어를 만드는 것입니다.

 

이번에는 가이드가 상세하지 않아서 조금 어렵고 시간이 걸리기는 했지만 기존에 만들어둔 코드나 chatGPT 와의 대화를 통하여 app 제작을 무사히 마칠 수 있었습니다. 

 

아래와 같은 UI로 앱을 만들었습니다. 작품명, 작가명은 기능 확인을 위해 임의로 지정하였습니다.

 

프로젝트를 하면서 배운 점들 중 중요한 몇 가지는 아래와 같습니다. 

  • Previous, Next 버튼을 통한 사진, 제목, 작가, 연도의 변경은 artImageId 라는 매개변수를 mutableIntStateOf 변수로 선언하고, 각 항목을 이와 연결하여 변경되도록 하였습니다.
  • 처음에는 artImageId 와 원하는 정보의 종류(사진명, 제목, 작가 등)를 input으로 넣으면 값을 return 하는 함수를 정의하여 이용하려고 했는데 코드가 너무 지저분해져서(아래 코드에서 맨 아래 주석처리된 informationGetter 함수 부분),
    결국은 ArtInformation 이라는 class 를 만들고 각 정보들을 class 의 속성으로 하여 입력하고, 출력할 수 있도록 정의했습니다. 이에 해당하는 코드는 val artInformationList = listOf ( 아래에 있는 부분으로 사진 7장에 대해 7줄로 정보 입력이 끝납니다. 
  • 아직 스마트폰에 있는 사진에 직접 접근하는 방법을 몰라서, 7장의 사진을 다운받아서 drawable 에 저장하고 이를 보여주는 형태로 앱을 만들었습니다. 이 경우, next 버튼을 계속 눌러서 맨 마지막 사진 이후에 다시 처음 사진을 보여주게 하는 기능은(previous 버튼도 마찬가지) artImageId 를 7로 나누었을 때 나머지로 정의함으로써 구현했습니다.
    onClick = { artImageId = (artImageId +1) % 7 }
  • Column 컴포저블을 사용하는 경우가 많은데, 대부분 수평/수직 모두 중앙 정렬을 하는 경우가 많으므로 modifier 전에 horizontalAlignment = CenterHorizontally, verticalArrangement = Arrangement.Center 로 선언하는 것이 좋을 듯 합니다. 
  • Surface 컴포저블을 통하여 그림자가 있는 액자틀 효과를 구현할 수 있었습니다. Image 컴포저블을 Surface 로 감싸고, shadowElevation 옵션을 주면 됩니다. 

참고를 위하여 코드의 주요 부분을 공유합니다. 

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            ArtSpaceTheme {
                ArtSpaceApp()

            }
        }
    }
}


@Composable
fun ArtSpaceApp() {
    var artImageId: Int by remember { mutableIntStateOf(1) }
    @DrawableRes val fileName = artInformationList[artImageId].fileName
    @StringRes val imageTitle = artInformationList[artImageId].imageTitle
    @StringRes val imageArtist = artInformationList[artImageId].imageArtist
    @StringRes val imageYear = artInformationList[artImageId].imageYear

    val myColor = Color(0xB240C0F3)

    Column(
        horizontalAlignment = CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .verticalScroll(rememberScrollState())) {

        Surface(
            shadowElevation = 16.dp,
            ) {
            Image(
                painter = painterResource(fileName),
                contentDescription = null,
                contentScale = ContentScale.FillHeight,
                modifier = Modifier
                    .height(420.dp)
                    .width(330.dp)

                    .padding(16.dp)
                    .align(alignment = CenterHorizontally)
            )
        }

        Spacer(modifier = Modifier.height(32.dp))
        Column (modifier = Modifier
            .padding(16.dp)
            .background(myColor) ) {
            Spacer(modifier = Modifier.height(16.dp))

            Text(
                text = stringResource(imageTitle),
                fontSize = 24.sp,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .padding(start = 16.dp, end = 16.dp)
                    .fillMaxWidth()
                    .height(60.dp)
            )
            //        Spacer(modifier = Modifier.height(10.dp))
            Text(
                text = "${stringResource(imageArtist)} ( ${stringResource(imageYear)} )",

                fontSize = 20.sp,
                fontWeight = FontWeight.Bold,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .padding(start = 16.dp, end = 16.dp)
                    .fillMaxWidth()
            )
            Spacer(modifier = Modifier.height(16.dp))
        }
        Spacer(modifier = Modifier.height(24.dp))
        Row(modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)) {
            Button(
                onClick = { artImageId = (artImageId - 1 + 7) % 7 },
                modifier = Modifier.weight(1f)) {
                Text(text = "Previous")
            }
            Spacer(modifier = Modifier.weight(0.5f))
            Button(
                onClick = { artImageId = (artImageId + 1) % 7 },
                modifier = Modifier.weight(1f)) {
                Text(text = "Next")
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun ArtSpacePreview() {
    ArtSpaceTheme {
        ArtSpaceApp()
    }

}

class ArtInformation(val fileName: Int, val imageTitle: Int, val imageArtist: Int, val imageYear: Int)

val artInformationList = listOf(
    ArtInformation(R.drawable._0240526_091705, R.string.ImageTitleNo1, R.string.ImageArtistNo1, R.string.ImageYearNo1),
    ArtInformation(R.drawable._0240526_105150, R.string.ImageTitleNo2, R.string.ImageArtistNo2, R.string.ImageYearNo2),
    ArtInformation(R.drawable._0240602_082018, R.string.ImageTitleNo3, R.string.ImageArtistNo3, R.string.ImageYearNo3),
    ArtInformation(R.drawable._0240602_082406, R.string.ImageTitleNo4, R.string.ImageArtistNo4, R.string.ImageYearNo4),
    ArtInformation(R.drawable._0240606_161756, R.string.ImageTitleNo5, R.string.ImageArtistNo5, R.string.ImageYearNo5),
    ArtInformation(R.drawable._0240606_161934, R.string.ImageTitleNo6, R.string.ImageArtistNo6, R.string.ImageYearNo6),
    ArtInformation(R.drawable._0240616_101106, R.string.ImageTitleNo7, R.string.ImageArtistNo7, R.string.ImageYearNo7) )



//
//private fun informationGetter(informationType: String, artImageId: Int): Int {
//    return when (informationType) {
//        "Filename" -> when (artImageId) {
//            1 -> R.drawable._0240526_091705
//            2 -> R.drawable._0240526_105150
//            3 -> R.drawable._0240602_082018
//            4 -> R.drawable._0240602_082406
//            5 -> R.drawable._0240606_161756
//            6 -> R.drawable._0240606_161934
//            7 -> R.drawable._0240616_101106
//            else -> 0
//        }
//
//        "ImageTitle" -> when (artImageId) {
//            1 -> R.string.ImageTitleNo1
//            2 -> R.string.ImageTitleNo2
//            3 -> R.string.ImageTitleNo3
//            4 -> R.string.ImageTitleNo4
//            5 -> R.string.ImageTitleNo5
//            6 -> R.string.ImageTitleNo6
//            7 -> R.string.ImageTitleNo7
//            else -> 0
//        }
//
//        "ImageArtist" -> when (artImageId) {
//            1 -> R.string.ImageArtistNo1
//            2 -> R.string.ImageArtistNo1
//            3 -> R.string.ImageArtistNo1
//            4 -> R.string.ImageArtistNo1
//            5 -> R.string.ImageArtistNo1
//            6 -> R.string.ImageArtistNo1
//            7 -> R.string.ImageArtistNo1
//            else -> 0
//        }
//
//        else -> 0
//    }
//}

 

'왕초보 안드로이드 앱 개발 도전기' 카테고리의 다른 글

7. 람다함수(lambda)  (0) 2024.06.25
6. Unit 2 practice: click behavior - lemonade  (0) 2024.06.21
5. Kotlin 클래스(class)  (0) 2024.06.20
4. Kotlin 조건문  (0) 2024.06.19
3. Unit 1 연습문제  (0) 2024.06.18