일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 노트북
- javascript
- Python
- 패널 교체
- as?
- 안드로이드
- 연산자
- 파이썬
- 자바
- 자바스크립트
- 배열
- adapter
- 리스트 뷰
- Overloading
- Java
- js
- Kotlin
- Android
- go
- golang
- 싱글 스레드
- 노트북 추천
- 함수
- Array
- var
- 코틀린
- 오버로딩
- ListView
- node.js
- HP
- Today
- Total
Bbaktaeho
[Android] 계산기 앱 만들기 (안드로이드, 계산기 애플리케이션, 후위 표기법, Calculator, infix, postfix) 본문
[Android] 계산기 앱 만들기 (안드로이드, 계산기 애플리케이션, 후위 표기법, Calculator, infix, postfix)
Bbaktaeho 2020. 9. 14. 18:272020-09-14 수정 : (우리가 일상에서 이용하는 수식은 중위 표기법입니다)
들어가며
자바 언어를 검색을 통해서 익히다 보니 어떤 util이 더 좋은지 아직 감이 안 잡혔고 메서드를 만들어 사용하는 것도 많이 부족합니다.
때문에 자바 언어가 많이 미숙해서 코드가 좋지 못한 점 양해 부탁드립니다.
제가 구현한 코드는 답이 아닙니다. 참고로만 봐주시면 감사하겠습니다.
이번에 만들어볼 계산기는 윈도우 계산기입니다.
간단한 연산만 할 수 있도록 구현하겠습니다.
참고로 계산기를 구현하는 것은 쉬운 일이 아닙니다. 저 역시 구현하면서 논리적인 생각, 자료구조를 떠올리며 많은 노력을 쏟았습니다.
프로젝트 생성
위의 포스팅에서 프로젝트 생성과 Splash 구현까지 그대로 따라 하면 됩니다.
res/values/colors.xml
프로젝트에서 사용할 색상을 먼저 작성합니다.
<color name="colorBlack">#000000</color>
<color name="colorWhite">#FFFFFF</color>
<color name="colorMyGray">#E6E6E6</color>
검은색, 흰색, 회색을 추가했습니다.
res/values/styles.xml
화면에 보일 스타일을 따로 작성했습니다.
메인 파일에서 추가하지 않고 어떻게 해야 스타일을 적용시킬 수 있을까 찾아보다가 styles.xml 에서 원하는 스타일을 작성하면 layout들의 스타일, 테마까지 쉽게 적용시킬 수 있었습니다.
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@color/colorWhite</item>
</style>
<style name="MainTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowBackground">@color/colorMyGray</item>
</style>
Splash 화면, Main 화면에 적용시킬 스타일만 작성했습니다.
res/layout/activity_main.xml
이제 UI를 만들어보겠습니다.
가장 먼저 루트 레이아웃으로 LinearLayout을 선택했고 그 안에 RelativeLayout, TableLayout 으로 구현했습니다.
각 TableRow에 버튼을 균형적으로 배치할 수 있도록 TableLayout의 속성 값으로 android:stretchColumns="*" 옵션을 추가했습니다.
<TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<Button
android:id="@+id/btn_mod"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:backgroundTint="#3EBAB4B4"
android:onClick="buttonClick"
android:text="%"
android:textSize="18sp" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:backgroundTint="#3EBAB4B4"
android:clickable="false"
android:enabled="false"
android:text="CE"
android:textSize="18sp" />
<Button
android:id="@+id/btn_clear"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:backgroundTint="#3EBAB4B4"
android:onClick="clearClick"
android:text="C"
android:textSize="18sp" />
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:backgroundTint="#3EBAB4B4"
android:text="Del"
android:onClick="deleteClick"
android:textSize="18sp" />
</TableRow>
하나의 TableRow에 들어있는 Button들입니다.
여기서 버튼에 onClick 속성이 있는데 자바, 코틀린 코드에서 리스너 메서드를 구현하지 않아도 각 뷰 컴포넌트들에게 직접적으로 메서드를 연결할 수 있었습니다.
이러한 방법을 추천하지 않는다고 하지만 단순히 어떤 버튼이 클릭되었는지만 아는 기능뿐이고 메인 코드가 줄어드는 장점이 있기 때문에 적용했습니다.
다음은 수식과 결과가 나오는 TextView입니다.
여기는 RelativeLayout을 이용해서 구현했습니다.
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="171dp">
<TextView
android:id="@+id/txt_expression"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginTop="44dp"
android:layout_marginRight="20dp"
android:hint="expression"
android:textSize="18sp" />
<TextView
android:id="@+id/txt_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/txt_expression"
android:layout_alignRight="@+id/txt_expression"
android:layout_marginTop="25dp"
android:hint="result"
android:textColor="@color/colorBlack"
android:textSize="36sp"
android:textStyle="bold" />
</RelativeLayout>
MainActivity.java
앞서 말씀드렸던 onClick 속성을 적용하려면 View 타입의 매개변수가 있는 메서드를 선언해야 합니다.
public void buttonClick(View v) {...}
특히 public 제어자로 선언하지 않으면 사용할 수 없습니다. (엄청 삽질함)
자꾸 새로 만들라는 경고 메시지만 나옵니다.
다음은 MainActivity 클래스의 필드입니다.
private TextView txtExpression;
private TextView txtResult;
private List<Integer> checkList; // -1: 이콜, 0: 연산자, 1: 숫자, 2: . / 예외 발생을 막는 리스트
private Stack<String> operatorStack; // 연산자를 위한 스택
private List<String> infixList; // 중위 표기
private List<String> postfixList; // 후위 표기
TextView들은 수식과 계산 결과 UI입니다.
나머지 스택과 리스트는 계산, 예외 처리를 하기 위한 필드입니다.
다음으로 전체 MainActivity 클래스의 메서드들입니다.
예외나 추가적인 기능을 처리하느라 메서드가 많아지고 서로 연관성이 좀 생겼습니다..
전체 코드는 깃허브 링크로 공유하겠습니다. 포스팅에서 몇 가지 메서드들만 다뤄보겠습니다.
init()
// 필드 초기화
void init() {
txtExpression = findViewById(R.id.txt_expression);
txtResult = findViewById(R.id.txt_result);
checkList = new ArrayList<>();
operatorStack = new Stack<>();
infixList = new ArrayList<>();
postfixList = new ArrayList<>();
ActionBar actionBar = getSupportActionBar();
assert actionBar != null;
actionBar.hide();
}
필드들을 초기화하고 ActionBar를 숨김 처리합니다.
getWeight(String)
// 연산자 가중치 (우선순위 *,/,%,+,-)
int getWeight(String operator) {
int weight = 0;
switch (operator) {
case "X":
case "/":
weight = 5;
break;
case "%":
weight = 3;
break;
case "+":
case "-":
weight = 1;
break;
}
return weight;
}
연산자의 우선순위에 따라 가중치를 부여하는 메서드입니다.
후위 표기법으로 나타낼 때 가장 중요한 역할을 합니다.
infixToPostfix()
// 전위 -> 후위
void infixToPostfix() {
for (String item : infixList) {
// 피연산자
if (isNumber(item)) postfixList.add(String.valueOf(Double.parseDouble(item)));
// 연산자
else {
if (operatorStack.isEmpty()) operatorStack.push(item);
else {
if (getWeight(operatorStack.peek()) >= getWeight(item)) postfixList.add(operatorStack.pop());
operatorStack.push(item);
}
}
}
while (!operatorStack.isEmpty()) postfixList.add(operatorStack.pop());
}
중위 표기법을 후위 표기법으로 변환하는 메서드입니다.
미리 저장했던 infixList(중위 표기법)에서 operatorStack을 이용해 연산자 우선순위에 따라 postfixList(후위 표기법)에 추가합니다.
calculate()
// 계산
String calculate(String num1, String num2, String op) {
double first = Double.parseDouble(num1);
double second = Double.parseDouble(num2);
double result = 0.0;
try {
switch (op) {
case "X": result = first * second;break;
case "/": result = first / second;break;
case "%": result = first % second;break;
case "+": result = first + second;break;
case "-": result = first - second;break;
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "연산할 수 없습니다.", Toast.LENGTH_SHORT).show();
}
return String.valueOf(result);
}
실질적인 계산을 하는 메서드입니다.
결과 값을 문자열로 변환하고 리턴해줍니다.
실행
동영상은 팝업창 또는 전체 화면으로 봐주세요.
GitHub
참고 자료
wayhome25.github.io/cs/2017/04/18/cs-22/
'개발 (Develop) > 안드로이드 (Android)' 카테고리의 다른 글
[Android] ListView 사용하기 (안드로이드, 리스트뷰, Adapter) (0) | 2020.09.20 |
---|---|
[Android] AlertDialog 사용하기 (안드로이드, 경고창, 팝업창) (0) | 2020.09.20 |
[Android] 액티비티 생명 주기 (Activity Lifecycle) (0) | 2020.09.13 |
[Android] Intent 다뤄보기 (startActivity, getIntent, putExtra, getExtra) (1) | 2020.09.12 |
[Android] 기본적인 View 요소들과 역할 (0) | 2020.09.06 |