[Android / Java]
TextWatcher 클래스를 이용한 계산기 로직 만들기
* 목표 : EditText에 숫자를 입력하면 자동으로 금액이 합산되는 계산기 Class를 만들어보자.
설계는 다음과 같다.
Total Income - Total Expense ( detail expense...+ etc ) = Total Net Income
먼저 상세 Expense 항목의 값들이 변화하면 그 합을 Total Expense에 나타내고 최종적으로는 Income에서 금액을 빼 Net Income을 나타내려고 한다.
저번 시간에 EditText에 addTextChangedListener() 함수를 이용해서 new TextWatcher Class를 상속받는 것을 보여주었다.
이번에는 TextWatcher를 상속받는 Class를 생성하여 그 안에 이벤트를 작성하려고 한다.
큰 숫자를 다루기 위해서 BigDecimal Class를 이용했고 해당 로직은 100% 창작으로 이루어져 있다.. (물론 누군가 먼저 만들었을 수도..)
먼저 전체소스를 한 번 보자.
AmountTextFormatter.java
package com.m3s.skylark.converter;
import android.text.Editable;
import android.text.Selection;
import android.text.TextWatcher;
import android.widget.EditText;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import cz.msebera.android.httpclient.util.TextUtils;
public class AmountTextFormatter implements TextWatcher {
private final String TAG = this.getClass().getSimpleName().trim();
private BigDecimal preA, A, B;
private EditText mEditText;
private EditText mOutputText;
private String status;
private String strAmount = ""; // 임시 저장값 (콤마)
private boolean isEditing = false;
/**
* comma 만 적용
* @param editText input EditText
*/
public AmountTextFormatter(EditText editText) {
mEditText = editText;
// int maxLength = 23;
// mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
}
/**
* input 값을 output 에 더하거나 빼기
* @param input
* @param output
* @param s
*/
//for Calculate
public AmountTextFormatter(EditText input, EditText output, String s) {
mEditText = input; //input Text
mOutputText = output; //output Text
status = s; //Status ('+' = add, '-' = subtract)
// int maxLength = 23;
// mEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
}
@Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
if (isEditing) return; //이미 했으면 리턴
Editable e = mEditText.getText();
Selection.setSelection(e, e.length()); //끝으로 커서이동
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
if (isEditing) return; //이미 했으면 리턴
if (mOutputText != null) {
boolean preANull = TextUtils.isEmpty(s.toString());
preA = preANull ? BigDecimal.valueOf(0) : BigDecimal.valueOf(Double.parseDouble(s.toString().replaceAll("[^[0-9\\-]]", ""))); //숫자가 아닌것과 - 표시 제외
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { //텍스트가 변경될때마다 실행
if (!s.toString().equals(strAmount)) { // StackOverflow 방지
if (isEditing) return; //이미 했으면 리턴
isEditing = true; //포맷팅 과정 시작
if (mOutputText != null && status != null) { //output 존재한다면 sum 하려는 용도
A = BigDecimal.valueOf(0); //input
B = BigDecimal.valueOf(0); //output
boolean inputNull = TextUtils.isEmpty(s.toString()) || TextUtils.isEmpty(s) || s.toString().equals("") || s.toString().equals("-");
boolean outputNull = (TextUtils.isEmpty(mOutputText.getText().toString()) || mOutputText.getText().toString().equals("-"));
//input EditText;
A = inputNull ? BigDecimal.valueOf(0) : BigDecimal.valueOf(Double.parseDouble(s.toString().replace(",", "")));
B = outputNull ? BigDecimal.valueOf(0) : BigDecimal.valueOf(Double.parseDouble(mOutputText.getText().toString().replace(",", "")));
if (status.equals("+")) {
B = B.subtract(preA).add(A); //바뀌기 전 숫자 빼고 Change 된 숫자 더하기
} else if (status.equals("-")) {
B = B.add(preA).subtract(A); //바뀌기 전 숫자 더하고 Change 된 숫자 빼기
}
mOutputText.setText(makeStringComma(String.valueOf(B))); //output에 삽입
}
strAmount = makeStringComma(s.toString()); //컴마붙이기
mEditText.setText(strAmount);
isEditing = false;
}
}
protected String makeStringComma(String inputStr) { // 천단위 콤마 처리
if (TextUtils.isEmpty(inputStr))
return inputStr;
String str = inputStr.replace(",", ""); //기존 컴마 제거
BigDecimal bigDecimal = new BigDecimal(str);
DecimalFormat format = new DecimalFormat("###,###"); //포맷팅
String returnStr = format.format(bigDecimal);
return returnStr; //리턴
}
}
두 개의 생성자를 만들어 하나를 컴마만 적용하고 하나는 합과 차를 구한다.
생성자에 주석 친 부분은 MaxLength를 만들어 적용하는 부분이고 취향 껏 하면 된다.
이 코드의 포인트는 바로 EditText의 입력 변화에 따라 이벤트가 발생하는 것이다.
그렇기 위해 beforeTextChanged() 메서드를 이용해 바뀌기 전의 수를 먼저 구하고, 그 수의 차이를 통해 유저가 원하는 대로 보여준다. 단순히 숫자가 늘어나는 순간뿐 아니라 백스페이스로 숫자를 지울 때도 적절하게 이전 값을 구해 계산한다.
실제 소스 적용은 다음과 같이 이루어졌다.
...
totalSaleIncomeTil.getEditText().addTextChangedListener(new AmountTextFormatter(totalSaleIncomeTil.getEditText(), businessTotalNetIncomeEt, "+"));
totalSaleExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(totalSaleExpenseTil.getEditText(), businessTotalNetIncomeEt, "-"));
totalFamilyIncomeTil.getEditText().addTextChangedListener(new AmountTextFormatter(totalFamilyIncomeTil.getEditText(), familyTotalNetIncomeEt, "+"));
totalFamilyExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(totalFamilyExpenseTil.getEditText(), familyTotalNetIncomeEt, "-"));
//Last Total Net Income
businessTotalNetIncomeEt.addTextChangedListener(new AmountTextFormatter(businessTotalNetIncomeEt, TotalNetIncomeEt, "+"));
familyTotalNetIncomeEt.addTextChangedListener(new AmountTextFormatter(familyTotalNetIncomeEt, TotalNetIncomeEt, "+"));
//Total Sale Expense (Sum)
rawMaterialExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(rawMaterialExpenseTil.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
businessBuildingRentingTil.getEditText().addTextChangedListener(new AmountTextFormatter(businessBuildingRentingTil.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
employeeExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(employeeExpenseTil.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
transportationTil.getEditText().addTextChangedListener(new AmountTextFormatter(transportationTil.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
lossOfGoodsTil.getEditText().addTextChangedListener(new AmountTextFormatter(lossOfGoodsTil.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
busOtherExpense1Til.getEditText().addTextChangedListener(new AmountTextFormatter(busOtherExpense1Til.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
busOtherExpense2Til.getEditText().addTextChangedListener(new AmountTextFormatter(busOtherExpense2Til.getEditText(), totalSaleExpenseTil.getEditText(), "+"));
//Total Family Expense (Sum)
costForFoodTil.getEditText().addTextChangedListener(new AmountTextFormatter(costForFoodTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
houseMaintenanceTil.getEditText().addTextChangedListener(new AmountTextFormatter(houseMaintenanceTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
electricWaterPhoneTil.getEditText().addTextChangedListener(new AmountTextFormatter(electricWaterPhoneTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
educationExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(educationExpenseTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
healthyExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(healthyExpenseTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
otherDebtSavingTil.getEditText().addTextChangedListener(new AmountTextFormatter(otherDebtSavingTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
famOtherExpenseTil.getEditText().addTextChangedListener(new AmountTextFormatter(famOtherExpenseTil.getEditText(), totalFamilyExpenseTil.getEditText(), "+"));
...
Material Design의 TextInputLayout 속성을 적용했기 때문에 EditText를 구하는 코드가 조금 길어지긴 했지만..
'Andorid' 카테고리의 다른 글
[Kotlin] Java에서 Kotlin 으로 Migration 작업 (1) (0) | 2021.02.02 |
---|---|
[Android / Java] ListView 특정 아이템 클릭 이벤트 막기 (0) | 2021.01.18 |
[Android / Java] 심플한 Log TAG 설정 (1) | 2021.01.08 |
[Android] Package, Project, Module 이름 바꾸기 (4) | 2021.01.07 |
[Android / Java] TextWatcher를 통한 EditText의 입력 변화 이벤트 (1) | 2020.12.24 |