[여러가지 시도]/코틀린 결과물

[결과물_앱] 계산기

시간 확보러 2022. 10. 31. 22:19
728x90

이번에는 계산기 앱을 아래와 같이 제작했다.

핸드폰의 기본어플로만 사용하다가 직접 만들어보니 생각보다 많은 부분을 고려해야 했다.

각 칸의 위치부터 시작하여 눌렀을때 색깔 변환 등

 

해당 강의에서도 3개의 강의로 할 만큼 내용이 많았다.

생각보다 오래 걸렸지만 포기하지 않고 끝까지 실시한 나에게 칭찬한다

 

한걸음 한걸음 가보자!

 

@file:OptIn(ExperimentalMaterial3Api::class)

package com.jeong.mycalculator2

import android.nfc.Tag
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.*
import androidx.compose.material3.CardDefaults.cardElevation
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.jeong.mycalculator2.MainActivity.Companion.TAG
import com.jeong.mycalculator2.ui.theme.ActionButtonBgColor
import com.jeong.mycalculator2.ui.theme.MyCalculator2Theme
import com.jeong.mycalculator2.ui.theme.Purple40
import com.jeong.mycalculator2.ui.theme.PurpleGrey80
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

    companion object {
        const val TAG = "메인"
    }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)


        setContent {
            MyCalculator2Theme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Calculator()
                }
            }
        }
    }
}


@Composable
fun Calculator(){

    val numbers : List<Int> = listOf<Int>(0,1,2,3,4,5,6,7,8,9)
    val actions : Array<CalculateAction> = CalculateAction.values()

    val buttons = listOf(
        CalculateAction.Divide,
        7,8,9, CalculateAction.Multiply,
        4,5,6, CalculateAction.Minus,
        1,2,3, CalculateAction.Plus,
        0
    )
    //첫번째 입력
    var firstInput by remember { mutableStateOf("0")}

    //두번째 입력
    var secondInput by remember { mutableStateOf("")}

    val selectedAction: MutableState<CalculateAction?> = remember {
        mutableStateOf(null)
    }

    val selectedSymbol : String = selectedAction.value?.symbol?:""

    val calculateHistories : MutableState<List<String>> = remember{ mutableStateOf(emptyList())}

    val coroutineScope = rememberCoroutineScope()
    val scrollState = rememberLazyListState()

    var isCalcuateHistoryVisible by remember { mutableStateOf(true)}

    val watchHistoryToggleTitle : String = if(isCalcuateHistoryVisible) "계산 기록 안보기" else "계산 기록 보기"

    val verticalSpacerWeight : Float =  if (isCalcuateHistoryVisible) 0.1f else 1f

    Column(modifier = Modifier.fillMaxSize()){

        Card(
            elevation = CardDefaults.cardElevation(8.dp),
            onClick = {
                isCalcuateHistoryVisible = !isCalcuateHistoryVisible

            }

        ){
            Text(text = watchHistoryToggleTitle,
                modifier = Modifier
                    .padding(horizontal = 6.dp)
                    .padding(3.dp)
            )
        }

        AnimatedVisibility(
            visible = isCalcuateHistoryVisible,
                    modifier = Modifier.weight(1f)
        ) {
            LazyColumn(
                state = scrollState,
                verticalArrangement = Arrangement.spacedBy(4.dp),
                contentPadding = PaddingValues(vertical = 10.dp),
                reverseLayout =true,

                content = {
                    items(calculateHistories.value){ aHistory ->
                        Text(text = aHistory, modifier = Modifier.background(PurpleGrey80))

                    }
                })
        }

        Spacer(modifier = Modifier.weight(verticalSpacerWeight))

        LazyVerticalGrid(columns = GridCells.Fixed(4),
            horizontalArrangement = Arrangement.spacedBy(8.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
            content ={

                item(span={GridItemSpan(maxLineSpan) }){
                    NumberText(
                        firstInput,
                        secondInput,
                        selectedSymbol,
                        modifier = Modifier.fillMaxSize()
                    )

                }

                item(span={GridItemSpan(2) }){
                    ActionButton(
                        action = CalculateAction.AllCelar,
                        onClicked = {
                            firstInput = "0"
                            secondInput = ""
                            selectedAction.value = null
                        }
                    )
                }

                item(span={GridItemSpan(1) }){
                    ActionButton(action = CalculateAction.Del, onClicked = {

                        if (secondInput.length>0){
                            secondInput = secondInput.dropLast(1)
                            return@ActionButton
                        }


                        if (selectedAction.value != null){
                            selectedAction.value = null
                            return@ActionButton
                        }

                        firstInput = if(firstInput.length ==1) "0" else firstInput.dropLast(1)
                    })
                }

                items(buttons) { aButton ->
                    when(aButton){
                        is CalculateAction -> ActionButton(aButton, selectedAction.value,
                            onClicked ={
                                selectedAction.value = aButton


                            })
                        is Int -> NumberButton(aButton, onClicked = {
                            if (selectedAction.value == null){
                                if (firstInput == "0") firstInput = aButton.toString() else firstInput += aButton
                            } else{
                                secondInput += aButton
                            }


                        })

                    }
                }

                item(span={GridItemSpan(maxCurrentLineSpan) }){
                    ActionButton(action = CalculateAction.Calculate,
                        onClicked = {

                            if (secondInput.isEmpty()){
                                return@ActionButton
                            }

                            selectedAction.value?.let{
                                val result = doCalculate(
                                    firstNumber = firstInput.toFloat(),
                                    secondNumber = secondInput.toFloat(),
                                    action = it
                                )

                                val calculateHistory = "$firstInput $selectedSymbol $secondInput = $result"

                                calculateHistories.value += calculateHistory

                                coroutineScope.launch {scrollState.animateScrollToItem(calculateHistories.value.size)}

                                firstInput = result.toString()
                                secondInput = ""
                                selectedAction.value = null

                                Log.d(TAG, "계산결과 : $result")
                            } ?: Log.d(TAG, "선택된 연산이 없습니다!")


                        })
                }

                item(span = {GridItemSpan(maxLineSpan)}){
                    Text("구글 광고 처리",
                        textAlign = TextAlign.Center,
                        modifier = Modifier
                            .border(1.dp,Color.Blue)
                            .height(60.dp)
                            .wrapContentHeight()

                        )
                }



            } )

    }


}

@Composable
fun NumberText(
    firstInput: String,
    secondInput: String,
    selectedSymbol: String,
    modifier: Modifier = Modifier
){
    Row(
        modifier = modifier,
        verticalAlignment = Alignment.Bottom,
        horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End)

    ){
        Text(
            text = firstInput,
            fontSize = 50.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center,
            lineHeight = 50.sp,
            maxLines = 1,
            color = Color.Black

        )
        Text(
            text = selectedSymbol,
            fontSize = 50.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center,
            lineHeight = 50.sp,
            maxLines = 1,
            color = Purple40

        )
        Text(
            text = secondInput,
            fontSize = 50.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center,
            lineHeight = 50.sp,
            maxLines = 1,
            color = Color.Black

        )
       }
}


@Composable
fun NumberButton(number: Int, onClicked : () -> Unit){
    Card(
        elevation = CardDefaults.cardElevation(8.dp),
        onClick = onClicked
    ) {
        Text(number.toString(),
            modifier = Modifier
                .padding(16.dp)
                .fillMaxSize(),
            textAlign = TextAlign.Center,
            fontSize = 30.sp,
            fontWeight = FontWeight.Bold
        )
    }
}


@Composable
fun ActionButton(action: CalculateAction,
                 selectedAction : CalculateAction?=null,
                 onClicked: (() -> Unit)?=null){

    val isSelected : Boolean = selectedAction == action

    val cardContainerColor  : Color = if(isSelected) Purple40 else ActionButtonBgColor

    val cardContentColor : Color = if(isSelected) Color.White else Color.Black

    Card(
        elevation = CardDefaults.cardElevation(8.dp),
        colors = CardDefaults.cardColors(cardContainerColor, cardContentColor),
        onClick = {
            Log.d(TAG, "Card가 클릭되었다.")
            onClicked?.invoke()
        }
    ) {
        Text(
            action.symbol,
            modifier = Modifier
                .padding(16.dp)
                .fillMaxSize(),
            textAlign = TextAlign.Center,
            fontSize = 30.sp,
            fontWeight = FontWeight.Bold
        )
    }
}

fun doCalculate(
    firstNumber: Float,
    secondNumber: Float,
    action: CalculateAction) : Float?{

    return when(action){
        CalculateAction.Plus -> firstNumber + secondNumber
        CalculateAction.Minus -> firstNumber - secondNumber
        CalculateAction.Multiply -> firstNumber * secondNumber
        CalculateAction.Divide -> firstNumber / secondNumber
        else -> null
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyCalculator2Theme {
        Greeting("Android")
    }
}
 
728x90

'[여러가지 시도] > 코틀린 결과물' 카테고리의 다른 글

[결과물_앱] 챗봇앱  (0) 2022.11.12
[결과물_앱] 타이머  (0) 2022.10.29
[결과물_앱] 로또번호 생성기  (0) 2022.10.28