[가계부 시스템] 5. 안드로이드 어플리케이션
2021.08.04 - [Project] - [가계부 시스템] 4. 클래스 구조
[가계부 시스템] 4. 클래스 구조
2021.07.29 - [Project] - [가계부 시스템] 3. API 설계 [가계부 시스템] 3. API 설계 2021.07.15 - [Project] - [가계부 시스템] 2. 시스템 설계 - 아키텍쳐, DB [가계부 시스템] 2. 시스템 설계 - 아키텍쳐, DB..
blog.robinjoon.space
위 포스팅에서 작성한 API를 이용해 안드로이드 어플을 제작해보았다. OKHttp3를 사용해 REST API 호출을 구현하였고, MPAndroidChart 를 이용해 수입, 지출통계화면을 구현하였다.
이번에 안드로이드 어플을 제작하면서, 처음에 생각했던 설계에 큰 오점이 있었다는 것을 알았다. 사용자는 결국 ConversionAccount 와 Account 객체에 담겨진 정보를 같은 화면에서 한번에 보게된다. 사용자 입장에서 이 두 타입은 모두 가계부에 적혀있는 최소단위의 기록이기 때문이다. 따라서, 이 둘은 인터페이스나 상속을 이용해 하나의 타입으로 사용할 수 있도록 했어야 했다. 이 둘을 서로 전혀 다른 타입으로 구현하여 생긴 문제는 다음과 같다.
} else if (msg.what == 2) {
LinearLayout accounts = findViewById(R.id.accounts);
accounts.removeAllViews();
List dtolist = (List) msg.obj;
DtoWithHttpCode<List<Account>> dto = (DtoWithHttpCode<List<Account>>) dtolist.get(0);
DtoWithHttpCode<List<ConversionAccount>> conversionDto = (DtoWithHttpCode<List<ConversionAccount>>)dtolist.get(1);
if(dto.getHttpCode()==200){
List<Account> accountList = dto.getData();
List<ConversionAccount> conversionAccountList = conversionDto.getData();
List<String> accountTextList = new ArrayList<>();
Map<Long,Account> accountMap = new HashMap<>();
Map<Long,ConversionAccount> conversionMap = new HashMap<>();
for(int i=0;i<accountList.size();i++) {
Account account = accountList.get(i);
accountTextList.add(account.toString() + "@account@" + account.getAid());
accountMap.put(account.getAid(),account);
}
for(int i=0;i<conversionAccountList.size();i++) {
ConversionAccount account = conversionAccountList.get(i);
accountTextList.add(account.toString() + "@conversion@" + account.getAid());
conversionMap.put(account.getAid(),account);
}
Collections.sort(accountTextList);
for(int i=0;i<accountTextList.size();i++){
String accountText = accountTextList.get(i);
Log.i("accountText",accountText);
String[] splitAccountText = accountText.split("@");
if(splitAccountText[1].contentEquals("account")){
Account account = accountMap.get(Long.parseLong(splitAccountText[2]));
TextView textView = new TextView(getApplicationContext());
textView.setText(splitAccountText[0]);
if(account.getAccountType().equals("Income"))textView.setTextColor(getColor(R.color.blue));
else textView.setTextColor(getColor(R.color.red));
textView.setBackground(getDrawable(R.drawable.bottom_border));
float h = main.getTextSize()/1.5f;
textView.setTextSize(h);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(),AccountViewActivity.class);
try {
intent.putExtra("account",mapper.writeValueAsString(account));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
startActivity(intent);
}
});
textView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try{
Request request = HttpConnection.deleteAccount(account);
Response response = client.newCall(request).execute();
String json = response.body().string();
Log.i("delete_json",json);
DtoWithHttpCode<Account> dto = mapper.readValue(json,new TypeReference<DtoWithHttpCode<AccountBook>>(){});
}catch (Exception e){
e.printStackTrace();
}
}
}).start();
if(dto.getHttpCode()==200) {
Log.i("delete_json",dto.getHttpCode()+"");
Toast.makeText(getApplicationContext(), "삭제성공", Toast.LENGTH_SHORT).show();
}else{
Log.i("delete_json",dto.getHttpCode()+"");
Toast.makeText(getApplicationContext(), "삭제실패", Toast.LENGTH_SHORT).show();
}
textView.setVisibility(View.INVISIBLE);
return true;
}
});
accounts.addView(textView);
}else{
TextView textView = new TextView(getApplicationContext());
textView.setText(splitAccountText[0]);
textView.setTextColor(getColor(R.color.green));
textView.setBackground(getDrawable(R.drawable.bottom_border));
float h = main.getTextSize()/1.5f;
textView.setTextSize(h);
accounts.addView(textView);
}
}
}
}
이 코드는 MainActivity의 initHandler() 매서드의 일부이다. 이 코드는 사용자가 달력에서 날짜를 선택하였을 때, 그 날짜의 항목들을 화면에 보여주는 기능을 구현한 코드다. 이 때, 당연히 시간순서대로 오름차순으로 정렬되어야 한다. 그러나, 근본적으로 서로 완전히 다른 타입의 두 객체를 비교하여 정렬하는 것은 정렬알고리즘을 직접 구현하지 않는한 불가능하다. 따라서, 나는 Account와 ConversionAccount 두 타입의 객체에서 사용자에게 보여줘야할 정보를 String형태로 빼내고, 이를 List에 넣은 후 정렬해야했다. 그 와중에 각각의 항목을 눌렀을 때 이를 상세히 보여주는 Activity로 이동해야하고, 상세한 정보를 보여주기 위해선 Account 나 ConversionAccount 타입의 객체를 Intent에 담아 넘겨줘야한다. 이를 위해 별도의 Map에 저장할 필요가 있었다. 또한, 아직 미구현이지만, ConversionAccount와 Account가 분리되어있어 상세정보를 보여누는 Activity도 따로 만들어줘야 한다.
다음 프로젝트에서는 이런 문제가 발생하지 않도록 설계시 좀 더 고민을 해야겠다.