도전과제 2. 글자 수를 표시하는 SMS 입력 화면
Do it! 안드로이드 앱 프로그래밍(정재곤) 참조
미션: 화면의 위쪽에 텍스트 입력상자, 아래쪽에 [전송]과 [닫기] 버튼을 수평으로 배치하세요.
현재 사용하고 있는 핸드폰들의 문자 화면이 아닌, 예전 핸드폰들의 화면을 생각하면 이해하기 쉽다. 아래가 최종 결과 화면 샘플이다.

추가 조건
- 입력된 문자 갯수 표시하기 [해설]
- 전송 버튼 클릭시 입력 받은 내용을 화면에
토스트(Toast)
로 표시하기 [해설] - 종료 버튼 클릭시 앱 종료 [해설]
- 두 개의 버튼을 수평으로 배치하기 [해설]
- 글자 수 제한은 80자 [해설]
- 한 줄에 한글 8글자가 들어가도록 사이즈 조절 [해설]
코드는 역시나 Github에서 확인가능.
레이아웃 선택하고 뷰 배치하기
도전과제 1에서 소개한 이유로 인해서 사이즈를 조절하기 편한 LinearLayout
및 layout_weight
속성을 사용하였다. 역시나 원하는 사이즈로 layout_weight
를 적절히 분해하여 수직 방향의 레이아웃을 조절하였다.
글자 수를 보여주기 위해서는 TextView
를 선택하였으며 문자 입력칸은 EditText
를 선택하였다. 두 개의 버튼을 같은 수직적 위치에 수평으로 배치하기 위하여 마지막 뷰는 TableLayout
을 사용하였다. 제시 조건과 다르게 문자 수를 표시하는 TextView
는 위아래로 2개를 배치하였다.
도전과제 1과 다르게 TableLayout
을 사용한 이유는 좀 더 깔끔하게 버튼을 배치하기 위해서 이다.
간단한 코드는 아래와 같다.
<LinearLayout>
<TextView
android:layout_weight="1" />
<EditText
android:layout_weight="8" />
<TextView
android:layout_weight="1" />
<TableLayout>
<Button />
<Button />
</TableLayout>
</LinearLayout>
입력된 문자의 수 표시하기
입려된 문자의 수를 표시하는 방법은 [텍스트뷰 추가기능]을 참조하면 간단하다. EditText
뷰의 addTextChangedListener
메소드를 통해 원하는 기능을 추가한 TextWatcher
구현 클래스를 할당하면 된다. 자세한 내용은 [텍스트뷰 추가기능]를 참고하기 바란다.
public void onCreate(...) {
et1.addTextChangedListener(new myWatcher());
}
public class myWatcher implements TextWatcher {
public void afterTextChanged(Editable s) {
tv1.setText(s.length() + " / 80 바이트");
tv2.setText(s.length() + " / 80 바이트");
}
}
버튼 클릭 시 동작
이번 과제에서도 역시 버튼이 2개이다. 하나는 입력받은 문자열을 Toast(토스트)
로 표시해야 하고 하나는 앱을 종료시켜야 한다.
둘 모두 setOnClickListener()
메소드를 통해 간단히 처리할 수 있다.
전송 버튼 동작
전송 버튼 시 Toast
로 화면에 내용을 보여주는 것은 매우 간단하므로 코드로 대체한다.
bt1.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast.makeText(getApplicationContext(),
"You sent " + et1.getText(), Toast.Length_LONG).show();
}
});
종료 버튼 동작
앱을 종료하는 메소드는 아마도 여러개가 있을 것이다. 귀찮아서 조사는 하지 않는다.
내가 알기로 finish()
라는 메소드가 대표적인데, java
에서와 동일한 함수로 현재 동작중인 activity
나 class
를 종료 시키고 onDestroy
상태로 가도록 한다. 정확한 건 각자 공부하면 좋겠다. 나도 나중에 필요하면 공부할래..
간단하므로 이것도 코드로 대체한다.
bt2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
finish();
}
});
글자 수 제한하기
글자 수를 제한하는 것도 [EditText 추가기능]에서 모두 설명하였다. 여러가지 방법 중에서 InputFilter
를 사용하여 제한하였다. 글자 수 입력 제한만을 했고, 따로 사용자에게 알림을 주지는 않았다.
특이한 점은 별달리 개발자가 직접 byte
단위의 카운팅을 코딩하지 않는 이상, 한글과 영어와 특수문자 모두 1글자
로 인식하도록 되어있다.
et1.setFilters(new InputFilter[] { new InputFilter.LengthFilter(80) });
글자 크기 조절하기
이번 도전과제를 진행하면서 가장 조사를 많이 한 부분이다. 도전과제의 미션에는 너무 심플하게 써두었다.
입력되는 글자의 크기와 줄 간격을 조정하여 한 줄에 한글 8글자가 들어가도록 만들어 봅니다.
위의 내용을 봤을 때는 글자 크기(textSize)와 줄 간격(lineSpacing)을 정말 잘 조절하면 될 것처럼 되어있다. 하지만!! 정확하게 한글 8글자가 들어가도록 설정하기 위해서 몇가지 조사 끝에 정확한 사이즈 조절을 수행할 수 있었다.
보통 TextView
와 같은 텍스트를 보여주는 뷰들에서 글자의 사이즈를 표기하는 것은 sp
단위로 하도록 권장한다. 하지만 이 단위를 사용할 경우 정확히 표현할 수 없기 때문에 화면의 픽셀 수를 계산해서 사이즈를 조절하도록 px
단위를 선택하였다.
textSize
조절은 setTextSize()
메소드를 통해 java
코드에서 직접 조정하였으며, 총 3단계를 거쳤다.
EditText
의 너비 구하기- 한글 한글자의 크기 구하기
- 한글 8글자만 들어가도록 텍스트 사이즈 조절하기
EditText의 너비 구하기
안드로이드 각각의 뷰들은 너비를 가지고 있고, 해당 너비를 반환해주는 메소드인 getWidth()
를 제공한다. 그러므로 원하는 EditText
뷰의 너비를 getWidth
를 통해 가져와야 한다.
하지만 너무 중요한 사실이 있다. onCreate()
메소드에서 getWidth()
를 호출할 경우 0
을 반환한다. 이게 무슨 청천벽력같은 일인가. 처음 0
이 반환될때는 어처구니가 없었지만 조사를 통해 이유를 알아냈다.
앱이 생성되고 레이아웃을 배치할때는 위치만 지정하고 아직 뷰들을 그리지 않은 상태라고 한다. 그래서 onCreate()
에서는 뷰들의 크기가 정해지지 않은 상태라는 것이다. 이후 각 뷰들을 실제로 그리게 되는 데, 다 그리고 나서 앱의 포커싱
상태가 변화하는 시점에 측정하면 된다는 것이다. 물론 더 정확한 시점이 존재할 것이지만, 구글님의 검색결과에는 대부분이 아래의 onWindowFocusChanged()
메소드에서 측정하라고 지시하고 있다.
결론은 메인 클래스의 onWindowFocusChanged()
메소드 내에서 너비를 구하면 된다는 것이다. 코드는 아래와 같다.
public void onWindowFocusChanged(boolean hasFocus) {
int etWidth = et1.getWidth();
}
위의 코드를 통해 너비를 구한 경우 반환되는 값은 px
단위이다. 그래서 글자 크기를 px
단위로 설정하게 된 것이다.
한글 크기 구하기
그 다음으로 할 일은 한글 1글자의 크기를 구하는 것이다. 이를 통해서 실제 textSize
를 한글 크기의 비율과 적절히 조절하여 정확하게 8글자가 들어가도록 하기 위함이다. 글자의 크기를 구하는 방법은 당연히 구글링을 통해 간단히 알아내서 copy & paste
를 수행하였다.
et1.setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);
float korSize = et1.getPaint().measureText("한");
위의 코드를 통해 textSize
가 100일 때의 한글 1글자의 비율을 얻어낼 수 있었다. COMPLEX_UNIT_PX
는 사이즈를 px
단위로 설정하기 위한 매개변수이다.
텍스트 사이즈 조절하기
이제 전체 너비와 한글 1글자의 비율도 알아냈으므로 텍스트 사이즈만 조절하면 끝이다. 역시 setTextSize()
메소드를 통해 간단한 수식으로 정의 하였다. 수식의 의미는 당연히 알 것이라고 생각한다.
et1.setTextSize(TypedValue.COMPLEX_UNIT_PX, etWidth / 8 / (korSize/100));
완성
위의 작업들을 통해 미션2를 완성하였다. 오랜기간 공부도 하지 않은 상태에서 별 시덥지 않은거에 시간 낭비하고 텍스트 사이즈 조절하는거나 찾아보느라 실제 작업시간은 좀 걸린거 같다. 실행화면은 아래와 같다.

전체 소스코드는 역시나 Github에서 확인가능.