저번에 혼자서 만들던 계산기가 DB를 연동해서 계산 기록을 저장하려고 했는데 굉장히 많은 오류가 생겨서
새로 갈아 엎고 구글링을 하면서 코딩을 했다. 너무 슬펐다.
계산기의 디자인은 이렇다.[항상 느끼는 건데 디자인도 진짜 어려운 영역이다. ]
각 버튼의 기능은 계산기를 한 번이라도 써본 사람은 알 거라고 믿고 있기에 설명을 생략한다.
초기 디자인이다.
View를 사용해서 1대1로 화면을 나눴다.
app:layout_constraintVertical_weight="num" // num:1으로 화면을 나누는 코드이다.
두 개의 textview를 만들어서 각각 input_text와 result_text를 입력받게끔 만들었다.
💻TableLayout
계산기 같이 같은 간격으로 같은 크기의 버튼을 넣어줄 때는 TableLayout이 굉장히 유용했다.
처음 계산기를 만들때는 TableLayout을 만들어주지 않고 수동으로 버튼을 생성한 다음 chain을 걸어줬지만
이렇게 TableLayout으로 각 열마다 버튼을 붙이고 아래의 코드를 입력하면 알맞은 비율로 이쁘게 버튼이 들어간다.
android:shrinkColumns="*"
💡각각의 열마다 가중치를 둬서 비율을 정할수도 있다.
<TableRow android:layout_weight="1">
💡버튼의 모양은 drawble 폴더에서 xml 파일을 만들어서 바꿔줄 수 있다.
android:background="@drawble/파일경로"
💡그리고 각 버튼마다 onClick을 줘서 버튼이 클릭될 때마다 해당 함수를 실행하도록 만들 수도 있다.
android:onClick="함수명"
💡시계 모양의 버튼처럼 버튼 안에 이미지를 넣고 싶은 경우는 ImageButton을 사용하면 된다.
vector asset에 들어가서 넣고 싶은 이미지를 고르고 그 파일을 만든다.
android:src="@drawble/파일경로"
해당 버튼에 파일 경로에 해당하는 이미지가 버튼에 들어가게 된다.
🔎Button과 AppcompatButton의 차이!
일반적으로 Button을 선언하게 되면 Background 등 색깔과 모양이 안 바뀌는 오류가 생길 수도 있어서 대부분 AppcompaButton으로 Button을 선언한다고 한다!!
androidx.appcompat.widget.AppCompatButton
더 자세한 차이점은 아래 링크를 통해서 보면 될 것 같다.
💡버튼의 음영을 제거하고 싶으면 아래의 코드를 입력!
android:stateListAnimator="@null"
위에서 버튼을 누르면 실행되는 함수를 android:onClick으로 줘야 한다고 설명했다.
fun buttonClicked(v: View) {
when (v.id) {
R.id.num_0_button -> numberButtonClicked("0")
R.id.num_1_button -> numberButtonClicked("1")
R.id.num_2_button -> numberButtonClicked("2")
R.id.num_3_button -> numberButtonClicked("3")
R.id.num_4_button -> numberButtonClicked("4")
R.id.num_5_button -> numberButtonClicked("5")
R.id.num_6_button -> numberButtonClicked("6")
R.id.num_7_button -> numberButtonClicked("7")
R.id.num_8_button -> numberButtonClicked("8")
R.id.num_9_button -> numberButtonClicked("9")
R.id.plus_button -> operatorButtonClicked("+")
R.id.minus_button -> operatorButtonClicked("-")
R.id.times_button -> operatorButtonClicked("X")
R.id.divide_button -> operatorButtonClicked("/")
R.id.mod_button -> operatorButtonClicked("%")
}
}
해당 버튼이 숫자인지 연산자인지를 구분해서 넣어줬다.
numberButtonClicked와 operatorButtonClicked에는 각각의 예외처리를 해줬다.
💡숫자 버튼을 눌렀을 때 실행되는 함수
private fun numberButtonClicked(number : String) {
if (isOperator) { //연산자를 입력받았는지를 체크
txtInput.append(" ")
}
isOperator = false
val itext = txtInput.text.split(" ")
if (itext.isNotEmpty() && itext.last().length >= 15) {
Toast.makeText(this, "15자리까지만 사용할수 있습니다", Toast.LENGTH_SHORT).show()
return
} else if (itext.last().isEmpty() && number == "0") {
Toast.makeText(this, "0은 제일 앞에 올수 없습니다.", Toast.LENGTH_SHORT).show()
return
}
txtInput.append(number)
txtResult.text = calculateExpression()
}
만약 연산자를 입력받았다면 4 + 5 이렇게 입력되도록 처리했다.
그래야 나중에 공백 단위로 숫자, 연산자를 넣어줄 수 있기 때문이다.
Toast는 간단하게 화면에 메시지를 띄우는 것이다.
뒤에 LENGTH_SHORT는 짧게 메시지를 화면에 띄우는 것이고, LENGTH_LONG은 화면에 메시지를 오래 띄우는 것이다.
예외처리를 다 통과하면 Input_text view에 숫자를 넣어줬다.
💡연산자 버튼을 눌렀을 때의 코드이다.
//isOperator : 연산자를 작성하고 있는지 아닌지를 체크.
//hasOperator : 내가 지금 연산자를 사용했는지 체크. 있다면 true, 없으면 false
private fun operatorButtonClicked(operator: String) {
if (txtInput.text.isEmpty()) { //연산자를 눌럿는데 숫자가 없다면 바로 return
return
}
when {
isOperator -> { //isOperator이 1이면 실행
val text = txtInput.text.toString()
txtInput.text = text.dropLast(1) + operator
}
hasOperator -> { //hasOperator이 1이면 실행
Toast.makeText(this, "연산자는 한번만 사용가능합니다", Toast.LENGTH_SHORT).show()
return
}
else -> {
txtInput.append(" $operator") //만약
}
}
val ssb= SpannableStringBuilder(txtInput.text)
ssb.setSpan(
ForegroundColorSpan(getColor(R.color.green)),
txtInput.text.length-1, txtInput.text.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
txtInput.text=ssb
isOperator = true
hasOperator = true
}
이번 계산기를 만들면서 처음 봤는데 Span은 텍스트의 스타일을 지정할 때 사용한다고 한다.
자세한 내용은 따로 공부하면서 정리할 예정이고
간단하게 말하면 class로 하고 싶은 것을 선언하고, 적용범위와, span이 적용된 곳에 문자열이 추가될 때 어떠한 처리를 하는지를 나타낸다고 합니다.
💡=버튼을 눌렀을 때의 함수
fun resultButtonClicked(v:View) {
val itext = txtInput.text.split(" ") //입력받은 문자열을 공백 단위로 쪼갬
if (txtInput.text.isEmpty() || itext.size == 1) {
return
}
if (itext.size != 3 && hasOperator) {
//공백 단위로 쪼개면 무조건 itext는 3이여야한다. 그리고 연산을 하기 위해서는 연산자가 있어야 하기에 hasOperator값이 1이어야한다.
Toast.makeText(this, "수식을 완성해주세요", Toast.LENGTH_SHORT).show()
return
}
if (itext[0].isNumber().not() || itext[2].isNumber().not()) { //만약 숫자가 아니라면 오류 발생
Toast.makeText(this, "오류가 발생했습니다.", Toast.LENGTH_SHORT).show()
return
}
val input_text = txtInput.text.toString()
val result_text = calculateExpression()
Thread(Runnable {
db.historyDao().insertHistory(History(null,input_text,result_text))
}).start()
txtResult.text = " "
txtInput.text = result_text
//계산이 끝낫으므로 연산자에 대한 옵션을 다시 초기값으로 줌
isOperator = false
hasOperator = false
}
💡계산하는 함수
private fun calculateExpression() : String {
val input_text = txtInput.text.split(" ")
if (hasOperator.not() || input_text.size != 3) {
return ""
} else if (input_text[0].isNumber().not() || input_text[2].isNumber().not()) {
return ""
}
// A + B
val n1 = input_text[0].toBigInteger() //A를 의미
val n2 = input_text[2].toBigInteger() //B를 의미
val op = input_text[1] //+를 의미
return when (op) { //해당 결과를 return 해줌.
"+" -> (n1 + n2).toString()
"-" -> (n1 - n2).toString()
"X" -> (n1 * n2).toString()
"%" -> (n1 % n2).toString()
"/" -> (n1 / n2).toString()
else -> ""
}
}
💡버튼 C를 눌렀을 때의 함수 -> 모든 text를 초기화하고 연산자 옵션도 초기화한다.
fun clearButtonClicked(v: View) {
txtInput.text = ""
txtResult.text = ""
isOperator = false
hasOperator = false
}
💡숫자인지 아닌지를 판별하는 함수
private fun String.isNumber(): Boolean {
return try{
this.toBigInteger()
true
}catch ( e:NumberFormatException){
false
}
}
이제 다음 글은 계산 기록을 저장하는 단계이다.
'Skils > Android' 카테고리의 다른 글
[Android] - Layout이란 (0) | 2022.10.07 |
---|---|
[Android] 애플리케이션 기본항목 - 4대 구성요소(Component) (1) | 2022.09.23 |
[Android] - 계산기 만들기(클론 코딩) - 계산 기록O (0) | 2022.09.18 |
[Android] Room (로컬 데이터베이스에 데이터 저장) (0) | 2022.09.03 |
[Android Studio] 계산기 어플 앱 만들기(Kotlin) #1 (0) | 2022.08.25 |