그림과 같이 입력한 값을 보여주는 입력상자(EditText) 하나와 15개의 버튼으로 구성된 간단한 계산기를 만들면서 안드로이드 프로그래밍 기초를 정리한다.

정리 방법은 다음과 같이 단계별로 예제를 구현하고, 예제 소스는 GitHub에서 받을 수 있다.

  1. 계산기 디자인과 기본 코드 (branch: master)
  2. 생성한 클래스를 이벤트 리스너로 사용 (branch: step1)
  3. 코드 줄이기  (branch: step2)
  4. 실제 계산 기능 구현 (branch: step3)
  5. 후위 표기법으로 계산 기능 구현 (branch: step4)
  6. 동적 생성 (branch: step5)

각각의 단계별 예제는 GitHub의 branch에서 받을 수 있다.


앞서의 예제에서는 계산기 화면을 쉽고 간단하게 만드는 방법을 정리했다.


네번째 예제로, 이번에는 실제 계산이 가능하도록 구현한다.

실제 계산기로 계산하는 과정을 정리하면,

     ① 사용자가 숫자를 입력하고

     ② 사칙 연산자를 선택한 뒤

     ③ 다시 숫자를 입력하고,

     ④ “=” 버튼을 클릭하여 계산을 하게 된다.

이처럼 계산기의 사용법 자체는 단순하다


이것을 조금 더 세분화해서 정리하면

     ① 사용자가 숫자를 입력하고

     ② 사칙 연산자를 선택하면

          - 현재까지 입력한 숫자는 첫 입력 값으로 저장하여 보관하고

          - 두 번째 값의 입력을 위해 기존 숫자는 지운다.

          - 선택된 연산자는 연산을 위해 보관한다.

     ③ 다시 숫자를 입력하고

     ④ “=” 버튼을 클릭하면 다시 입력한 숫자(③)를 두 번째 값으로 처리하여 두 값에 대하여 사칙 연산을 처리하고 결과 값을 출력한다.


여기까지는 쉬우니 (?) 직접 해보길 바라고, 조금 더 기능을 부여해서 구현해 본다.


① ,②, ③번의 과정은 동일하지만

④ 번에서 “=” 대신에 사칙 연산자를 선택할 수 있다.

④ 번을 세부화하여,

두 번째 값을 입력하고 사용자가 사칙 연산을 입력하면 앞서 입력한 두 값과 앞서 입력한 사칙 연산을 이용하여 계산한다.

“=” 버튼을 클릭한 것과 동일하다.

사칙 연산자를 선택한 것은 계산을 계속하겠다는 의미이므로, 계산 결과를 출력한 뒤 ⑤ 계산 결과를 첫 번째 값으로 보관한다.

사용자가 다시 숫자를 입력하고 사칙연산자나 “=”를 누르면 연산을 실행한다.

사칙 연산자를 누르면 ⑤번 과정을 반복하게 된다.

사칙 연산자가 아닌 “=”를 선택했을 경우, 그냥 계산 결과를 출력하고 사용자가 입력한 값들과 연산자를 초기화 해주면 된다.

다만, 다시 계산하기 위해 값을 입력하면 출력한 결과 값을 지우고, 사용자 입력 값이 출력되도록 하면 된다.

이 외에도 필요한 기능은 더 많지만 이 정도 수준에서 구현해 본다.

이 정도 수준이면 제법 복잡하고, 잡아야 할 버그도 제법 많다.


다시 정리하면

     ① 사용자가 숫자를 입력하고

     ② 사칙 연산자를 선택하면

          - 현재까지 입력한 숫자는 첫 입력 값으로 저장하여 보관하고

          - 두 번째 값의 입력을 위해 기존 숫자는 지운다.

          - 선택된 연산자는 연산을 위해 보관한다.

     ③ 다시 숫자를 입력하고

     ④ “=” 나 사칙 연산자 버튼을 클릭하면

          - 다시 입력한 숫자(③)를 두 번째 값으로 처리하여, 두 값에 대하여 사칙 연산을 처리하고 결과 값을 출력한다.

          - 클릭한 것이 사칙 연산자이면 계산 결과를 첫 번째 값으로 보관하고, 클릭한 사칙 연산자를 보관한다.

          - 사칙 연산자가 아닌 “=”를 선택했을 경우, 그냥 계산 결과를 출력하고 사용자가 입력한 값들과 연산자를 초기화 해주면 된다.


전체 코드

기존 예제의 OnClickListener 리스너를 다음과 같이 수정하면 된다.

코드가 복잡해 보이지만

앞서 정리한 문장을 이해하면 쉽게 이해할 수 있는 코드이다.

따라서 다음과 같이 문장과 코드를 같이 보면 된다.


① 사용자가 숫자를 입력하고 [라인 51]

② 사칙 연산자를 선택하면 [라인 18~44]

  - 현재까지 입력한 숫자는 첫 입력 값으로 저장하여 보관하고 [라인 18]

  - 두 번째 값의 입력을 위해 기존 숫자는 지운다 [라인 20].

  - 선택된 연산자는 연산을 위해 보관한다 [라인 42].

③ 다시 숫자를 입력하고 [라인 51]

④ “=” 나 사칙 연산자 버튼을 클릭하면 [라인 18~44]

  - 다시 입력한 숫자(③)를 두 번째 값으로 처리하여 [라인 23],

    두 값에 대하여 사칙 연산을 처리하고[라인 24~30] 결과 값을 출력한다 [라인 32].

  - 클릭한 것이 사칙 연산자이면 계산 결과를 첫 번째 값으로 보관하고[라인 40],

    클릭한 사칙 연산자를 보관한다 [라인 42].

  - 사칙 연산자가 아닌 “=”를 선택했을 경우[라인 36],

    그냥 계산 결과를 출력하고[라인 32]

   사용자가 입력한 값들과 연산자를 초기화 해주면 된다 [라인 46~48].


[주의] 이상의 예제를 실행하면 제법 많은 버그가 나온다. 찾아서 수정해 보길 바란다.

     - 숫자를 입력하고 사칙연산 선택, 또 사칙연산 선택시 오류

     - 숫자를 입력하고 “=” 입력시 오류 등 다양한 예외에 대한 처리가 필요하다.

다섯 번째 예제는 후위 표기법 방식으로 구현하는 것이다.

후위 표기법에 대한 개념은 검색 해보길 바라고 여기서는 간단하게 정리한다.

후위 표기법에 대한 개념이 없으면 다음 내용을 제대로 이해하기 어려울 수 있다.


앞서의 계산기는 연산자나 “=”을 입력하면 연산이 되도록 한 것이고,

이번에는 사용자가 입력한 모든 값과 연산자를 가지고 있다가 “=” 버튼을 클릭하면 계산하는 방식이다.

즉, 1 + 2 + 3 + 4 - 5 (수식) 를 입력하면 입력 박스에 그대로 출력되고,

“=” 버튼을 클릭하면 이 값을 분석해서 계산하게 된다.


위 수식을 중위표기법이라 부르고, 이것을 후위 표기법으로 변환해서 처리하게 된다.

후위 표기법으로 변환하는 이유는 쉽게 코딩 할 수 있기 때문으로,

개인적인 개념으로 정리하면 그냥 숫자와 연산자를 분리 / 보관해서 처리하는 것이다.


따라서 기본 개념을 정리하면

1. 사용자가 입력한 수식을 숫자와 연산자(+-*/)로 분리한다.

2. 첫 번째 숫자와 두 번째 숫자를 첫 번째 연산자로 계산해서 결과를 저장한다.

3. 첫 번째 결과와 세 번째 숫자를 두 번째 연산자로 계산해서 결과를 저장한다.

4. 숫자가 없을 때까지 이전 결과와 다음 숫자를 다음 연산자로 계산해서 결과에 저장한다.

★ 연산시 곱하기(*)과 나누기(/)가 먼저 실행되어야 한다.


이 문장을 자세히 보면 2~4번은 같은 의미를 나타낸다.

2~4번 문장은 이전 결과와 다음 숫자를 다음 연산자로 계산해서 결과에 저장하는 것이다.

이 경우 2번 문장의 첫 번째 숫자가 문제가 된다.

첫 번째 숫자를 계산 전에 결과에 넣고, 결과와 다음 숫자를 연산하면 된다.


따라서, 다음과 같이 정리된다.

1. 사용자가 입력한 수식을 숫자와 연산자(+-*/)로 분리한다.

2. 첫 번째 숫자를 결과에 저장한다.

3. 숫자가 없을 때까지 이전 결과와 다음 숫자를 다음 연산자로 계산해서 결과에 저장한다.

★ 연산시 곱하기(*)과 나누기(/)가 먼저 실행되어야 한다.


마지막으로 ★로 표기한 곱하기(*)과 나누기(/)가 수식에서 어디에 있던 먼저 실행되도록 해야 한다.

먼저 실행되게 하는 방법은,

연산자가 더하기(+)와 빼기(-)일 때는 그냥 넘어가고, 곱하기(*)과 나누기(/)일때만 처리한다.

그리고, 곱하기(*)과 나누기(/)가 끝나면 다시 더하기(+)와 빼기(-)를 처리한다.


따라서, 두 번의 반복문(While)이 필요한데, 약간의 트릭을 사용한다.

곱하기(*)과 나누기(/) 처리시 더하기(+)일 때는 결과 값을 그대로 저장하고, 빼기(-)일 때는 결과값을 음수화( * -1)해서 저장한다.

이렇게 하면 곱하기(*), 나누기(/), 더하기(+)의 결과는 그대로 저장될 것이고, 빼기는 결과값이 음수화되어 저장되니,

결과 값들을 모두 더하기 하면, 연산자 확인 없이 더하기(+)와 빼기(-) 처리가 된다.


다시 정리하면

1. 사용자가 입력한 수식을 숫자와 연산자(+-*/)로 분리한다.

2. 첫 번째 숫자를 결과에 저장한다.

3. 숫자가 없을 때까지 이전 결과와 다음 숫자를 다음 연산자로 계산해서 결과에 저장한다.

4. 이때, 곱하기(*)과 나누기(/)이면 연산해서 결과를 저장하고, 더하기(+)면 그대로 결과에 저장하고, 빼기면 음수화해서 저장한다.

5. 모든 결과들을 더해서 계산을 마친다.


전체코드

먼저, 사용자가 입력한 값이

"=" 이 아니면 (숫자나 연산자) 그대로 값을 추가해서 수식을 만들고 [라인 9],

"="이면 Calc() 함수를 호출해서 입력한 수식을 계산한다 [라인 7].


실제 계산을 하는 Calc() 함수는 다른 사람이 작성한 코드를 정리한 것으로

설명과 코드를 일치시켜서 정리하면,

1. 사용자가 입력한 수식(formulaStr)을 숫자[라인 16]와 연산자(+-*/)로 분리한다 [라인 15].

2. 첫 번째 숫자를 결과에 저장한다 [라인 19].

3. 숫자가 없을 때까지[라인 20] 이전 결과와 다음 숫자를 다음 연산자로 계산해서 결과에 저장한다 [라인 27, 31].

4. 이때, 곱하기(*)과 나누기(/)이면 연산해서 결과를 저장하고 [라인 25~32],

   더하기(+)면 그대로 결과에 저장하고, 빼기면 음수화해서 저장한다 [라인 33~38].

    5. 모든 결과들을 더해서 계산을 마친다 [라인 41~44].


계산 결과들을 저장하는 변수를 스택(Stack) 클래스를 사용한다 [라인 18].

스택(Stack)은 한 쪽 끝에서만 자료를 넣거나 뺄 수 있는 선형 구조(LIFO - Last In First Out)로,

왜 Stack을 사용한 것인지 스스로 생각해보길 바란다 (hint: LIFO).


학습을 위해 이상의 두 예제에 대한 개념을

파워포인트 등을 이용하여 플로우차트(Flowchart) 로 작성해 보면 많은 도움이 될 것이니 시도해 보길 바란다.


여섯 번째 예제는 동적 생성이다.

이전 예제는 XML 파일에 위젯들을 선언하고, Java 파일에서 이 위젯을 찾아서 사용했다.

이번에는 XML 파일이 아닌 Java파일에서 위젯들을 생성하는 방법을 정리한다.


개념을 익히기 위한 것이라 모든 위젯을 생성하지 않고, 4개의 레이아웃과 15개의 버튼만 동적으로 생성한다.

이 문제를 구현하기 위해서는 알아야 할 것은

동적으로 위젯(클래스)를 생성하는 방법 (new 클래스)과 하나의 레이아웃에 4개씩 버튼을 추가하는 방법이다.



그리고, 4개의 레이아웃과 15개의 버튼을 각각 생성하면 아주 많은 코드를 작성해야 한다.

따라서 다소 간단하게 생성하는 방법을 찾아야 한다.


위 그림을 토대로 구현 방법을 정리하면

    1. 생성할 버튼의 개수만큼 반복해서 버튼을 생성한다.

    2. 생성한 버튼에 필요한 속성을 지정하고,

    3. Layout에 추가한다.

    4. Layout에 버튼이 4개가 추가 되면, 새로운 Layout을 생성한다.

15개 버튼이니 4개의 Layout이 생성됨.


이 개념을 가지고 다음 코드를 살펴 본다.


    1. 생성할 버튼[라인 7]의 개수만큼 반복해서[라인 13] 버튼을 생성한다 [라인 20].

    2. 생성한 버튼에 필요한 속성을 지정하고 [라인 21~23],

    3. Layout에 추가한다 [라인 24].

    4. Layout에 버튼이 4개가 추가 되면 [라인 14], 새로운 Layout을 생성한다 [라인 15].

추가한 Layout과 버튼의 속성은 XML 파일에서 지정한 것과 동일하게 지정하면 된다.

“=” 버튼은 좀더 크게 만들어야 하는데 (예외), 생략했으니 직접 구현해 보길 바란다.


위 개념에는 없는 클래스(위젯) 생성과 부모에 추가하는 방법을 정리하면,

클래스는 new로 생성한다 [라인 15, 20].

생성된 클래스는 추가할 부모에 addView() 함수로 추가한다 [라인 18, 24].

XML에서는 태그 사이에 작성하는 것과 같다.


이상의 코드에서 버튼의 Layout 속성을 버튼을 생성할 때마다 지정하지 않고,

속성 클래스(LayoutParams) 하나를 생성해서, 생성하는 버튼에 지정하는 방식을 사용했다 [라인 10],

LinearLayout은 LinearLayout를 생성할 때 마다 매번 생성해서(new) 지정했다 [라인 16].

어느 방법이 더 좋을지 판단해 보길 바라고,

14라인에서 사용된 IF 문의 의미를 이해하고 넘어가길 바란다.


코드를 실행하기 전에 activity_main.xml에서

최상위 LinearLayout과 EditText를 제외하고는 모두 삭제한다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
tools:context="com.gujc.mycalc1.MainActivity">

<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="text"
android:gravity="right" />
</LinearLayout>



+ Recent posts