밍비
프로그램의 편린
밍비
전체 방문자
오늘
어제
  • 분류 전체보기 (64)
    • Spring (2)
    • TIL (23)
    • 프로그래머스 (12)
    • Udemy (16)
    • Typescript (2)
    • MERN (1)
    • AWS (7)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • useNavigate
  • state 끌어올리기
  • 리액트 reducer
  • 컴포넌트트리
  • 리액트 생애주기
  • 리스트 조회
  • useRef
  • 네이버커넥트
  • 리액트
  • useParams
  • 한입크기로잘라먹는리액트
  • overflow-wrap
  • useState
  • API 호출
  • 수평 스케일링
  • Points of Presence
  • State 합치기
  • AWS Regions
  • Page Moving
  • react
  • state 관리
  • 서비스아키텍처
  • Edge Locations
  • 분산저장소
  • 데이터 수정
  • 한입 크기로 잘라먹는 리액트
  • Availability Zones
  • 리액트 프로젝트 만들기
  • 함수형 update
  • DOM

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
밍비

프로그램의 편린

#0720 [안드로이드][코틀린] 계산기 앱 만들기 - 2
TIL

#0720 [안드로이드][코틀린] 계산기 앱 만들기 - 2

2022. 7. 21. 21:16
728x90

시작하기 앞서, 이때까지 한 작업을 되돌아보자.

splash 액티비티와 main 액티비티의 xml파일

사진과 애니메이션(lottie json)을 통해 로딩화면을 구상하였고, 비동기처리를 통해 3초후에 계산기 화면이 띄워지도록 하였다.

계산기 화면도 간단하게 구상하였다.

그럼 먼저 계산기 버튼을 다듬어보자

 

1. 계산기 버튼 다듬기

 

우선, 버튼의 text를 아래와 같이 수정한다.

2. 버튼 입력이 UI에 반영되게 하는 코드 작성

class MainActivity : AppCompatActivity() {
    private var activityMainBinding : ActivityMainBinding? = null
    private var liveExpr : MutableLiveData<String> = MutableLiveData("")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(activityMainBinding!!.root)

        liveExpr.observe (this){ data ->
            activityMainBinding!!.calcInput.setText(data)
        }

버튼을 누를 때마다 하나하나 온클릭리스너 쓰는 것은 쓰기도 힘들 뿐더러, 매번 UI를 업데이트 하는 것이 번거롭고 코드도 복잡해진다.

이를 해결하기 위해 나온 것이 LiveData인데, 이를 이용해 상태가 변하는지 감시할 부분을 따로 설정할 수 있게 된다.

(Observer와 LiveData에 관한 설명은 https://todaycode.tistory.com/49 여기에서 더 자세하게 볼 수 있다)

 

LiveData(라이브 데이터)란?

1. LiveData란?  1-1. Observer  1-2. LiveData  1-3. LiveData의 장점 2. 사용법  2-1. gradle  2-2. LiveData 객체 생성  2-3. Observer 객체 생성 3. 예제 4. 더 알아보기  3-1. LifeCycleOwner  3-2...

todaycode.tistory.com

여기서는 LiveData 변수를 만든 후, 바뀔 때마다 계산기의 화면에 해당 데이터를 띄우도록 코드를 작성하였다.

여기서 .text가 아닌 .setText()를 쓰는 이유는 .text를 쓸 경우 String이 아닌 Editable 데이터를 받아야 하기 때문이다.

val numberBtns : Array<Button> = arrayOf(
            activityMainBinding!!.btn0,
            activityMainBinding!!.btn1,
            activityMainBinding!!.btn2,
            activityMainBinding!!.btn3,
            activityMainBinding!!.btn4,
            activityMainBinding!!.btn5,
            activityMainBinding!!.btn6,
            activityMainBinding!!.btn7,
            activityMainBinding!!.btn8,
            activityMainBinding!!.btn9
        )
        for (btn in numberBtns){
            btn.setOnClickListener {
                liveExpr.value = "${btn.text}"
            }
        }

그 후, 숫자 버튼을 바인딩해 배열로 만들고 onClickListener를 달아준다.

liveData를 쓰니까 한 번에 처리할 수 있어서 너무 좋다!!

        val operatorBtns: Array<Button> = arrayOf(
            activityMainBinding!!.btnPlus,
            activityMainBinding!!.btnMinus,
            activityMainBinding!!.btnMul,
            activityMainBinding!!.btnDiv
        )
        for (btn in operatorBtns){
            btn.setOnClickListener {
                liveExpr.value = "${btn.text}"
            }
        }
        activityMainBinding!!.btnCancel.setOnClickListener {
            liveExpr.value = "0"
        }

        activityMainBinding!!.btnCalc.setOnClickListener {
            liveExpr.value = "Calced result"
        }

연산자(+,-,*,/) 기호, 취소(C)버튼, 계산(=)버튼에 대해서도 똑같이 작성해준다.

현재까지 진행된 모습

3. 실제 계산기처럼 되도록 UI 수정

 

실제 계산기는 숫자가 하나씩 나오지 않는다...여러 개 나올 경우 같이 나와야 한다.

 

우선 이전에 입력받은 값이 숫자인지 확인 후, 지금 입력받은 값도 숫자일 경우 이전 값에 붙여서 출력한다.

그리고 전에 입력받은 값은 숫자이고 지금 입력받은 값은 연산자일 경우, 이전 값과 띄어쓰기 해서 출력한다.

ex)

123

12 + 34

    private fun isNumber(digit: Char): Boolean{
    return (digit == '0') or
            (digit == '1') or
            (digit == '2') or
            (digit == '3') or
            (digit == '4') or
            (digit == '5') or
            (digit == '6') or
            (digit == '7') or
            (digit == '8') or
            (digit == '9')
    }

이전에 입력한 값이 숫자인지 판별하기 위한 isNumber() 함수이다.

    private fun addDigits(prevState : String, digit : Char): String {
        //0 혹은 공백이 있을 경우 지우고 출력
        if((prevState == "0") or (prevState == "0.0") or (prevState == "")) return digit.toString()

        //숫자일 경우 붙여서 출력
        else if(isNumber(prevState.last()) and isNumber(digit)) return prevState+digit

        //연산자일 경우 띄워서 출력
        else return prevState+" "+digit
    }

그리고 숫자인지 판별한 후 출력 형식을 만들어 반환하는 addDigits() 함수이다.

여기서 잠깐!! 코드를 좀더 Kotlinish하게 나타내기 위해 toString()과 문자열 덧셈을 $로 대체하고, return을 위로 올리자.

    private fun addDigits(prevState : String, digit : Char): String {
        //0 혹은 공백이 있을 경우 지우고 출력
        return if((prevState == "0") or (prevState == "0.0") or (prevState == "")) "$digit"

        //숫자일 경우 붙여서 출력
        else if(isNumber(prevState.last()) and isNumber(digit)) "$prevState$digit"

        //연산자일 경우 띄워서 출력
        else "$prevState $digit"
    }

이렇게 쓰면 조금 더 깔끔하고 세련된 코틀린 코드가 된다.

 

그럼 함수를 적용해보자.

        for (btn in numberBtns){
            btn.setOnClickListener {
            	//liveExpr.value에서 오류발생
                liveExpr.value = addDigits(liveExpr.value, btn.text[0])
            }
        }

이전에 있던 값으로 liveExpr.value를 넣었더니 빨간 줄이 생긴다.

null 처리를 안 해서 그렇다.

null이 아닐 경우에만 처리되도록 ?.let 구문으로 감싸준다.

        for (btn in numberBtns){
            btn.setOnClickListener {
                liveExpr.value = liveExpr.value?.let { it1 -> addDigits(it1, btn.text[0]) }
            }
        }
        for (btn in operatorBtns){
            btn.setOnClickListener {
                liveExpr.value = liveExpr.value?.let { it1 -> addDigits(it1, btn.text[0]) }
            }
        }

 

numberBtns와 operatorBtns에 대해 모두 적용해준다.

 

4. 계산 프로그램 틀 짜기

 

이제 실제 계산을 하는 부분을 만들어보겠다.

util이라는 이름의 새 패키지를 생성한 후, CalcUtil이라는 코틀린 클래스를 만들어준다.

프로젝트 폴더의 구조.

 

package com.example.calculator.util


class CalcUtil {
    fun getResult(expr: String): Double{

        return 0.0
    }
}

값을 반환하는 함수의 틀을 간단하게 잡아준다.

 

        activityMainBinding!!.btnCalc.setOnClickListener {
            //코틀린은 static이 없어서 객체를 생성해야함
            val util = CalcUtil()
            //전처럼 liveExpr.value에서 오류 발생
            liveExpr.value = util.getResult(liveExpr.value).toString()
        }

이후 = 버튼을 눌렀을 때 위의 함수가 실행되도록 한다.

주석에도 써놓았듯이 코틀린은 static이 없어서 새 변수(객체)를 생성한 후 여기서 함수를 참조해 사용한다.

activityMainBinding!!.btnCalc.setOnClickListener {
    //코틀린은 static이 없어서 객체를 생성해야함
    val util = CalcUtil()
    liveExpr.value = liveExpr.value?.let { it1 -> util.getResult(it1).toString() }
}

이전에 했듯이, ?.let 구문을 이용해 null을 처리해준다.

현재까지의 진행상황

'=' 버튼을 눌렀을 때 0.0이 출력된다면 CalcUtil이 제대로 참조된 것이다.

 

그럼 먼저 띄어쓰기를 기준으로 연산자와 피연산자를 분리시켜 받도록 하자.

expr문자열을 띄어쓰기 기준으로 분리해 calcUnit 배열에 넣은 후, 하나씩 로그에 올린다.

class CalcUtil {
    fun getResult(expr: String): Double{
        val calcUnit: List<String> = expr.split(" ")

        for(s in calcUnit){
            Log.d("exprUnit: ",s)
        }

        return 0.0
    }
}

 

화면에 123, +, 456을 입력하고 =을 누르면 exprUnit 태그로 이렇게 로그가 뜨는 것을 확인할 수 있다.

 

나머지 계산 파트와 xml 꾸미는 건 다음 글에서 이어서 하겠다.

728x90

'TIL' 카테고리의 다른 글

[Swift][iOS][SwiftUI] #0801 json mock 데이터 파싱해서 화면에 뿌리기  (0) 2022.08.01
[코틀린][안드로이드] #0721 계산기 앱 만들기 - 3  (0) 2022.07.21
#0718 [안드로이드][코틀린] 계산기 앱 만들기 - 1  (0) 2022.07.20
#0711 코루틴과 비동기처리  (0) 2022.07.18
[안드로이드] [코틀린] 리사이클러뷰에 뷰바인딩 적용하기  (0) 2022.07.05
    밍비
    밍비

    티스토리툴바