Group Study (2024-2025)/Android

[Android] ์นด์นด์˜ค๋ฑ…ํฌ ํด๋ก  ์ฝ”๋”ฉ (2)

z_ero 2024. 11. 27. 23:14

๐Ÿ“ Compose๋กœ ๊ตฌํ˜„ํ•œ ์ปค์Šคํ…€ ์ปดํฌ๋„ŒํŠธ์™€ ๋ฆฌ์ŠคํŠธ ํ™”๋ฉด ์ •๋ฆฌ

 

โœ… BankComponent

  • More ์•„์ด์ฝ˜: ์ถ”๊ฐ€ ์˜ต์…˜ ๋ฒ„ํŠผ ํ‘œ์‹œ.
  • ์ปค์Šคํ…€ ๋ฐฐ๊ฒฝ ์ƒ‰์ƒ: ํ˜ธ์ถœ ์‹œ ์ƒ‰์ƒ ์ง€์ • ๊ฐ€๋Šฅ.

  • ์ปดํฌ์ €๋ธ” ํ•จ์ˆ˜ ์ž‘์„ฑ ๋ฐฉ์‹
    • @Composable ์• ๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ์„ ์–ธ.
    • ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ๋ณธ ํŒจํ„ด์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์Œ.
  • ๋ ˆ์ด์•„์›ƒ ๊ตฌ์„ฑ ๋ฐ ์ •๋ ฌ
    • Box๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ UI ์š”์†Œ๋ฅผ ๊ฒน์น˜๊ฑฐ๋‚˜ ํŠน์ • ์œ„์น˜์— ๋ฐฐ์น˜ํ•˜๋Š” ๋ฐฉ์‹์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์Œ.
    • Modifier.align()์„ ํ†ตํ•ด Alignment ์˜ต์…˜์œผ๋กœ ์œ„์น˜๋ฅผ ์กฐ์ •.
  • UI ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
    • ์ž…๋ ฅ๊ฐ’: name, amount, backgroundColor๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„ ๋™์ ์œผ๋กœ UI๋ฅผ ๋ณ€๊ฒฝ.
    • ๊ธฐ๋ณธ๊ฐ’ ์ œ๊ณต: backgroundColor์— ๊ธฐ๋ณธ๊ฐ’์„ ์„ค์ •ํ•˜์—ฌ ์œ ์—ฐ์„ฑ์„ ๋†’์ž„.

 

BankComponent ์ฝ”๋“œ

// src/main/java/com/gdg/kakaobank/component/Bank.kt
package com.gdg.kakaobank.component

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.gdg.kakaobank.R
import com.gdg.kakaobank.ui.theme.B4_R
import com.gdg.kakaobank.ui.theme.Black
import com.gdg.kakaobank.ui.theme.H6_B
import com.gdg.kakaobank.ui.theme.Main_Yellow

@Composable
fun BankComponent(name: String, amount: String, backgroundColor: Color = Main_Yellow) {
    Box(
        modifier = Modifier
            .size(340.dp, 140.dp)
            .background(backgroundColor, shape = RoundedCornerShape(20.dp))
            .padding(16.dp)
    ) {
        Image(
            painter = painterResource(id = R.drawable.ic_bank),
            contentDescription = "KaKao Bank Icon",
            contentScale = ContentScale.Fit,
            modifier = Modifier
                .size(31.dp)
                .align(Alignment.TopStart)
                .offset(y = 10.dp) // ์•„๋ž˜๋กœ ์ด๋™
        )
        Text(
            text = "${name}์˜ ํ†ต์žฅ",
            style = B4_R,
            color = Black,
            modifier = Modifier
                .align(Alignment.TopStart)
                .padding(start = 40.dp)
                .offset(y = 10.dp) // ์•„๋ž˜๋กœ ์ด๋™
        )
        Text(
            text = "${amount}์›",
            style = H6_B,
            color = Black,
            modifier = Modifier
                .align(Alignment.TopStart)
                .padding(start = 40.dp)
                .offset(y = 26.dp) // ์•„๋ž˜๋กœ ์ด๋™
        )
        Image(
            painter = painterResource(id = R.drawable.more),
            contentDescription = "More Icon",
            contentScale = ContentScale.Fit,
            modifier = Modifier
                .size(29.dp)
                .align(Alignment.TopEnd)
        )
    }
}

 

โœ… HomeScreen

HomeScreen์€ ๊ณ„์ขŒ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฉ”์ธ ํ™”๋ฉด์ž…๋‹ˆ๋‹ค.

  • ์ฃผ์š” ๊ธฐ๋Šฅ
    1. LazyColumn: ๋ฆฌ์ŠคํŠธ ๊ธฐ๋ฐ˜ ๊ณ„์ขŒ ์ •๋ณด๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๋ Œ๋”๋ง.
    2. ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ: bankList๋ฅผ ํ™œ์šฉํ•ด ๋‹ค์ˆ˜์˜ ๊ณ„์ขŒ ์ •๋ณด๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑ.
    3. ์Šคํƒ€์ผ๋ง ๋ฐ ์ •๋ ฌ: ์ƒ๋‹จ ํ—ค๋”์™€ ๊ณ„์ขŒ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌ๋ถ„ํ•ด ๋ฐฐ์น˜.
  • ๊ตฌํ˜„ ํŠน์ง•
    • BankComponent๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋ฆฌ์ŠคํŠธ ๊ตฌ์„ฑ.
    • ๊ณ„์ขŒ ๋ณ„๋กœ ๋‹ค๋ฅธ ๋ฐฐ๊ฒฝ์ƒ‰์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ์ƒ‰์ƒ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์  ์Šคํƒ€์ผ๋ง ๊ตฌํ˜„.

HomeScreen ์ฝ”๋“œ

@Composable
fun HomeScreen() {
    val bankList = listOf(
        Pair("๊น€๋‚˜ํ˜„", "700,000,000"),
        Pair("์กฐ์˜์„œ", "700,000,000"),
        Pair("์ด๊ฐ€์„", "100,000,000"),
        Pair("๋ฐฑ์„œ์—ฐ", "100,000,000"),
        Pair("์ดํ˜„์ง„", "100,000,000")
    )

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(White)
            .padding(top = 24.dp)
    ) {
        // ์ƒ๋‹จ ํ—ค๋”
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 20.dp),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Row(
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(text = "๋ˆˆ์†ก์ด", style = H5_B, color = Black)
                Spacer(modifier = Modifier.width(8.dp))
                Text(text = "๋‚ด ๊ณ„์ขŒ", style = B4_B, color = Black)
            }
            Image(
                painter = painterResource(id = R.drawable.bell),
                contentDescription = "Bell Icon",
                modifier = Modifier.size(30.dp)
            )
        }

        Spacer(modifier = Modifier.height(20.dp))

        // ๊ณ„์ขŒ ๋ฆฌ์ŠคํŠธ
        LazyColumn(
            modifier = Modifier
                .fillMaxWidth()
                .background(White),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // ๋‚ด ๊ณ„์ขŒ ํ‘œ์‹œ
            items(1) {
                MyBankComponent(name = "๋ˆˆ์†ก์ด", amount = "100,000,000")
                Spacer(modifier = Modifier.height(12.dp))
            }
            // ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ๊ณ„์ขŒ ํ‘œ์‹œ
            items(bankList.size) { index ->
                val colors = listOf(Pink, Dark_Mint, Light_Mint, Deep_Blue)
                val (name, amount) = bankList[index]
                BankComponent(
                    name = name,
                    amount = amount,
                    backgroundColor = colors[index % colors.size]
                )
                Spacer(modifier = Modifier.height(12.dp))
            }
        }
    }
}

 

 



๋กœ๊ทธ์•„์›ƒ ๋‹ค์ด์–ผ๋กœ๊ทธ ๊ตฌํ˜„

๋กœ๊ทธ์•„์›ƒ ๋ฐ ํšŒ์› ํƒˆํ‡ด ๊ธฐ๋Šฅ์€ ํŒ์—… ๋‹ค์ด์–ผ๋กœ๊ทธ๋กœ ๊ตฌํ˜„๋˜์–ด ์‚ฌ์šฉ์ž์˜ ์˜์‚ฌ๋ฅผ ๋ช…ํ™•ํžˆ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋””์ž์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

โœ… ์ฃผ์š” ๊ตฌํ˜„ ์š”์†Œ

  • ๋‹ค์ด์–ผ๋กœ๊ทธ ์ฐฝ: AlertDialog๋ฅผ ํ™œ์šฉํ•ด ์‚ฌ์šฉ์ž ํ™•์ธ ์ ˆ์ฐจ ์ถ”๊ฐ€.
  • Toast ๋ฉ”์‹œ์ง€: ๋™์ž‘ ์™„๋ฃŒ ์‹œ ํ”ผ๋“œ๋ฐฑ ์ œ๊ณต.
  • ์œ ์—ฐํ•œ UI: DialogProperties๋ฅผ ํ†ตํ•ด ํ”Œ๋žซํผ์— ๋งž๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ ํฌ๊ธฐ ์„ค์ •.


MoreScreen ์ฝ”๋“œ

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.gdg.kakaobank.R
import com.gdg.kakaobank.ui.theme.*
import androidx.compose.ui.window.DialogProperties
import androidx.compose.foundation.shape.RoundedCornerShape
import android.widget.Toast
import androidx.compose.ui.platform.LocalContext

@Composable
fun MoreScreen() {
    val moreInfoList1 = listOf("์„œ๋น„์Šค ์ด์šฉ์•ฝ๊ด€", "๊ฐœ์ธ์ •๋ณด ์ฒ˜๋ฆฌ๋ฐฉ์นจ", "๋ฒ„์ „ ์ •๋ณด")
    val moreInfoList2 = listOf("๊ณ ๊ฐ์„ผํ„ฐ", "๋กœ๊ทธ์•„์›ƒ", "ํšŒ์› ํƒˆํ‡ด")
    var showDialog by remember { mutableStateOf(false) }
    var dialogType by remember { mutableStateOf("") }
    val context = LocalContext.current

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(White)
            .padding(top = 45.dp, start = 16.dp, end = 30.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp)
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth(),
            verticalArrangement = Arrangement.spacedBy(5.dp) //์ค„๊ฐ„๊ฒฉ ์†์„ฑ
        ) {
            Text("๋ˆˆ์†ก์ด๋‹˜", style = H5_B)
            Text("์นด์นด์˜ค๋ฑ…ํฌ ์„œ๋น„์Šค", style = H5_B, color = Main_Yellow)
            Text("๊ด€๋ จ ์•ˆ๋‚ด์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค", style = H5_B)
        }
        Spacer(modifier = Modifier.height(16.dp))
        Text("์ด์šฉ์•ˆ๋‚ด", style = H6_SB)
        Spacer(modifier = Modifier.height(4.dp))
        moreInfoList1.forEach { info ->
            MoreInfo(text = info)
        }
        Spacer(modifier = Modifier.height(16.dp))
        Text("๊ธฐํƒ€", style = H6_SB)
        Spacer(modifier = Modifier.height(4.dp))
        //ํšŒ์› ํƒˆํ‡ด ๋ฐ ๋กœ๊ทธ์•„์›ƒ ํŒ์—… ์•Œ๋ฆผ ๋œจ๋„๋ก ์„ค์ •
        moreInfoList2.forEach { info ->
            if (info == "ํšŒ์› ํƒˆํ‡ด" || info == "๋กœ๊ทธ์•„์›ƒ") {
                MoreInfo(text = info, onClick = {
                    showDialog = true
                    dialogType = info
                })
            } else {
                MoreInfo(text = info)
            }
        }
    }

    if (showDialog) {
        AlertDialog(
            onDismissRequest = { showDialog = false },
            text = {
                Text(
                    "${dialogType} ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?",
                    style = H5_SB,
                    textAlign = TextAlign.Center,
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp, 30.dp, 16.dp, 10.dp)
                )
            },
            confirmButton = {
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.SpaceEvenly
                ) {
                    Button(
                        colors = ButtonDefaults.buttonColors(containerColor = Gray, contentColor = White),
                        onClick = {
                            showDialog = false
                        },
                        modifier = Modifier.size(width = 110.dp, height = 45.dp)
                    ) {
                        Text("์ทจ์†Œ", style = H7_B)
                    }
                    Button(
                        colors = ButtonDefaults.buttonColors(containerColor = Main_Yellow, contentColor = Black),
                        onClick = {
                            showDialog = false
                            Toast.makeText(context, "${dialogType} ๋˜์—ˆ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_SHORT).show()
                        },
                        modifier = Modifier.size(width = 110.dp, height = 45.dp)
                    ) {
                        Text(dialogType, style = H7_B)
                    }
                }
            },
            properties = DialogProperties(usePlatformDefaultWidth = false),
            containerColor = Light_Gray,
            shape = RoundedCornerShape(16.dp),
            modifier = Modifier.size(width = 280.dp, height = 180.dp)
        )
    }
}

@Composable
fun MoreInfo(text: String, onClick: () -> Unit = {}) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .background(White)
            .padding(8.dp)
            .clickable(onClick = onClick),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween,
    ) {
        Text(text, style = B1_SB, color = Dark_Gray)
        Image(painter = painterResource(id = R.drawable.icn_next), contentDescription = null)
    }
}

@Composable
@Preview(showBackground = true)
fun MoreScreenPreview() {
    MoreScreen()
}

 

๋‹ค์ด์–ผ๋กœ๊ทธ ์ฐฝ๊ณผ Toast ๋ฉ”์‹œ์ง€ ์ •๋ฆฌ

โœ… ๋‹ค์ด์–ผ๋กœ๊ทธ ์ฐฝ (AlertDialog)

  • ๊ธฐ๋Šฅ: ์‚ฌ์šฉ์ž ํ™•์ธ ์ ˆ์ฐจ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹ค์ˆ˜ ๋ฐฉ์ง€.
  • ๊ตฌํ˜„: AlertDialog๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์•„์›ƒ ๋˜๋Š” ํšŒ์› ํƒˆํ‡ด ์‹œ ํ™•์ธ ์ฐฝ ํ‘œ์‹œ.
  • ๊ตฌ์„ฑ ์š”์†Œ:
    • ์ทจ์†Œ ๋ฒ„ํŠผ: ์ž‘์—…์„ ์ค‘๋‹จํ•˜๊ณ  ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๋‹ซ์Œ.
    • ํ™•์ธ ๋ฒ„ํŠผ: ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๋‹ซ์Œ.
if (showDialog) {
    AlertDialog(
        onDismissRequest = { showDialog = false },
        text = {
            Text(
                "${dialogType} ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?",
                style = H5_SB,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(16.dp, 30.dp, 16.dp, 10.dp)
            )
        },
        confirmButton = {
            Row(
                modifier = Modifier.fillMaxWidth(),
                horizontalArrangement = Arrangement.SpaceEvenly
            ) {
                Button(
                    colors = ButtonDefaults.buttonColors(containerColor = Gray, contentColor = White),
                    onClick = {
                        showDialog = false
                    },
                    modifier = Modifier.size(width = 110.dp, height = 45.dp)
                ) {
                    Text("์ทจ์†Œ", style = H7_B)
                }
                Button(
                    colors = ButtonDefaults.buttonColors(containerColor = Main_Yellow, contentColor = Black),
                    onClick = {
                        showDialog = false
                        Toast.makeText(context, "${dialogType} ๋˜์—ˆ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_SHORT).show()
                    },
                    modifier = Modifier.size(width = 110.dp, height = 45.dp)
                ) {
                    Text(dialogType, style = H7_B)
                }
            }
        },
        properties = DialogProperties(usePlatformDefaultWidth = false),
        containerColor = Light_Gray,
        shape = RoundedCornerShape(16.dp),
        modifier = Modifier.size(width = 280.dp, height = 180.dp)
    )
}

 

โœ… Toast ๋ฉ”์‹œ์ง€

  • ๊ธฐ๋Šฅ: ์ž‘์—… ์™„๋ฃŒ ํ›„ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ”ผ๋“œ๋ฐฑ ์ œ๊ณต.
  • ๊ตฌํ˜„: Toast.makeText()๋ฅผ ํ†ตํ•ด ์ž‘์—… ์„ฑ๊ณต ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅ.
Toast.makeText(context, "${dialogType} ๋˜์—ˆ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_SHORT).show()