본문 바로가기

책 요약하기/이것이 안드로이드다

#4. 위젯과 리소스 다루기 2021-03-06

- 요약 -

  • 컨스트레인트 레이아웃 (ConstraintLayout)을 사용하면 간단한 드래그 앤드 드롭만으로 각각의 화면 요소들을 원하는 곳에 배치할 수 있습니다.
  • 리니어 레이아웃(LinearLayout)은 위젯을 가로 또는 세로 한 줄로 배치하기 위한 레이아웃입니다.
  • 프레임 레이아웃(FrameLayout)은 입력되는 위젯의 위치를 결정하기보다는 위젯을 중첩해서 사용하기 위한 레이아웃입니다.

- 미니 퀴즈 4-1 -

1. 안드로이드 스튜디오에서 컨스트레인트 레이아웃에 5개의 버튼 요소를 배치한 후 가장 위에 있는 버튼을 이동시켰을 때 나머지 4개의 버튼도 같이 움직이도록 해보세요.

 

2. 다음처럼 12개의 버튼을 배치하고 화면을 회전했을 때 버튼이 동일한 모양을 유지하도록 설정해보세요.

1) (1, 4, 7, *), (2, 5, 8, 0), (3, 6, 9, #) 를 [마우스 오른쪽 버튼 클릭] - [Chains] - [Create Vertical Chain]
같은 방식으로 (1, 2, 3), (4, 5, 6), (7, 8, 9), (*, 0, #) [Create Horizontal Chain]
2) 전체 클릭 후 layout_width, layout_height를 0dp로 바꾸어 줍니다.
3) 버튼 간 간격을 설정 해 줍니다.

 

3. 앞에서 공부했던 스크롤뷰를 응용해서 가로로 스크롤되도록 해보세요. 스크롤뷰를 하나 만들고 20개 정도의 버튼을 삽입한 우에 가로로 스크롤되는 앱을 만들어보세요.

(가장 바깥 레이아웃을 HorizontalScrollView로 바꾼후에 디자인 편집기에서 horizontal LinearLayout을 드래그 앤 드롭합니다. 이후 버튼을 내부에 생성하여 복사 붙여넣기하면 끝!)

 

4.2 화면에 그려지는 디자인 요소 위젯

TIP. color이나 string(ID 등)는 resource 디렉터리에서 따로 string.xml, color.xml이라는 파일에 별도로 관리 할 수 있습니다. 이와 마찬가지로 TextSize역시 dimens.xml이라는 파일명으로 별도 관리가 가능하며 text의 크기는 dp보다는 sp를 주로 합니다. 이유는 화면상에 sp가 적용된 위젯이 있으면 줌인이나 줌아웃 시에 다른 위젯에는 영향을 주지 않고 해당 위젯의 글자만 커지거나 작아지게 할 수 있습니다.

각각 dimens.xml, color.xml, string.xml파일 내용

TextView Attributes Content
textStyle 텍스트의 스타일을 설정하는 속성입니다. 시스템에서 제공해주는 스타일은 normal, bold, italic 세 가지가 있습니다.
maxLines, minLines 텍스트뷰에 최대, 최소 입력 가능한 줄 수를 설정합니다. 
maxLines를 1로 설정한 상태에서 한 줄 이상의 글자가 입력되면 두 번째 줄부터는 화면에 출력하지 않습니다.
minLines 속성은 반대로 최소 줄 수를 미리 설정하는 것입니다. 3으로 설정하면 글자의 입력 여부와 상관없이 위젯이 세 줄의 공간을 미리 그려둡니다. 값을 설정하지 않으면 입력되는 줄 수에 맞춰서 자동으로 늘어납니다.
singleLine 텍스트뷰를 한 줄로 보이게 하는 속성입니다. maxLines 속성을 1로 설정할 때와 다른 점은 여러 줄이 있을 때 두번째 줄을 없애는 것이 아니라 줄 사이의 '\n'을 없애 한 줄로 보이게 합니다.
ellipsize 텍스트뷰의 maxLines 속성이 1이거나 문자열이 길어서 글자가 잘릴 때에 설정합니다. ellipsize 속성은 처음, 중간 또는 마지막 부분에 말줄임(...) 표시를 하거나 marquee로 글자를 좌우로 움직이게 할 수 있습니다.
▶ none: 설정하지 않습니다.
▶ start: 텍스트의 첫 부분을 말줄임표로 바꿔줍니다.
▶ middle: 텍스트의 중간 부분을 말줄임표로 바꿔줍니다.
▶ end: 텍스트의 마지막 부분을 말줄임표로 바꿔줍니다.
▶ marquee: 글자가 흐르는 효과를 줍니다. 해당 효과를 사용하기 위해서는 singleLine을 'true'로 설정해야 합니다. maxLines 속성을 통해서도 한 줄 기능은 만들 수 있지만 marquee 기능과 충돌이 일어나 사용할 수 없습니다. 그리고 marquee 기능을 주기 위해서는 텍스트가 포커스를 받아야 하므로 focusable 속성은 'auto', focusableInTouchMode 속성은 'true'로 설정하고, 완료한 후 앱을 실행하면 전광판처럼 텍스트가 움직이는데 추가적으로 marqueeRepeatMode 속성을 설정해주면 반복 횟수를 설정할 수 있습니다.
fontFamily 글꼴을 지정하는 속성으로 기본으로 제공하는 글꼴 이외에 외부 폰트도 지정할 수 있습니다.
ems 텍스트뷰의 크기를 나타낼 때 현재 글꼴의 크기를 기준으로 설정하는 상대값입니다. 예를 들어 텍스트뷰에 설정된 크기가 12sp라면, 1em = 12sp가 되고 2em = 24sp가 됩니다. 즉, 글꼴 크기에 텍스트뷰를 맞춰 글꼴이 커질 때 텍스트와 텍스트뷰의 비율을 유지하는 것입니다. 
스마트폰에서 글꼴을 줌인 또는 줌아웃할 때 ems를 기준으로 텍스트뷰의 크기가 커지거나 작아집니다.
lines 텍스트뷰의 높이를 고정할 때 사용합니다. maxLines 속성과는 달리 입력된 문자열과 상관 없이 고정된 높이값으로 보여집니다.
maxLength 텍스트의 전체 길이를 제한하는 속성으로 설정한 값만큼의 글자만 보이고 나머지는 보이지 않게 할 수 있습니다.

 

키보드 모양 설정하기 : InputType

inputType Option
textUri URI 형식의 문자 입력
textEmailAddress email 주소 형식의 문자 입력
textPostalAddress 우편 번호 형식의 문자 입력
textPassword 비밀번호 입력
textVisiblePassword 비밀번호를 문자열 그대로 표시하기
number 숫자 형식
numberPassword 숫자로만 구성된 비밀번호 입력
phone 전화번호 형식
date 날짜 형식

 

이벤트 설정하기 : imeOptions

imeOptions옵션 옵션값
normal
actionUnspecified
actionNone
특별한 기능 없음
특별한 액션 없음
액션을 사용하지 않음
actionGo 어딘가로 넘어감, URL 입력 후 해당 페이지로 넘어가기
actionSearch 검색하기, 구글, 네이버, 다음 검색
actionSend 메일, 메시지 보내기
actionNext 다음으로 넘어가기, 다음 입력창으로 이동
actionDone 입력 완료, 키보드 숨김
actionPrevious 이전 단계로 돌아가기, 이전 입력창으로 이동

 

 

EditText Attributes Content
hint 클릭하면 사라지는 미리보기를 작성 할 수 있습니다.
inputType 해당 속성에 입력되는 옵션에 따라서 나타나는 키보드의 모양이 바뀝니다.
imeOptions 입력 완료 후 실행할 이벤트를 설정합니다. Ime는 input method editor의 약자로 텍스트 편집기를 뜻합니다. inputType 속성을 통해서 어떤 입력을 가능하게 할지 결정했다면 imeOptions 속성에서는 입력이 완료된 상황에서 다음 이벤트로 어떤 처리를 할 것인지 결정합니다.

 

이미지 버튼

버튼, 이미지 버튼 둘 다 백그라운드 속성으로 이미지를 부여할 수 있는데 버튼은 백그라운드 이미지 위에 텍스트만, 이미지버튼은 백그라운드 이미지 위에 아이콘과 같은 이미지를 추가할 수 있습니다. 또한 버튼은 클릭 리스너를 텍스트에 구현하여 텍스트 뷰 속성을, 이미지버튼은 이미지뷰에 구현하여 이미지 뷰 속성을 거의 그대로 사용합니다.

drawable-v24 디렉터리
이미지를 drawable 디렉터리에 붙여넣기(또는 드래그 앤 드롭)하면 다음과 같은 팜업창이 뜹니다.
drawable-v24 디렉터리에 이동하겠냐는 메시지입니다. drawable 뒤에 붙은 24는 안드로이드의 API 버전입니다.
안드로이드 8.0부터 아이콘과 같은 특정 이미지에 background와 foreground로 이미지를 세분화해서 적용하기 위해 생긴 디렉터리 구조입니다. 일반 이미지를 v24에 넣으면 하위 버전의 안드로이드에서는 이미지를 볼 수 없는 문제가 발생할 수 있기 때문에 가급적 -v24를 지우고 drawable 디렉터리에 넣어줍니다.

 

src와 srcCompat
src와 srcCompat는 버튼에 사용할 이미지를 삽입하는 속성으로 src, srcCompat 두 가지가 있는데, 하위 버전과의 호환성을 위해서 srcCompat이 제공되고 있습니다. srcCompat의 경우 '롤리팝 5.0 미만 버전의 호환성 보장'이라는 장점이 있는 것에 반해, 안드로이드 버전에 따라 스마트폰에서 이미지가 나오지 않는 경우가 있기 때문에 코드를 추가해야 하는 불편함이 있습니다. 특별한 경우가 아니라면 src를 사용하면 됩니다. 

 

투명 배경 설정하기

이미지 버튼은 기본적으로 배경에 회색 영역을 포함하고 있는데 속성 중 background 속성에 '@android:color/transparent'를 적용하면 회색 영역을 없애고 투명하게 만들 수 있습니다.

 

이미지 크기 설정하기 : scaleType

scaleType은 이미지버튼뿐만 아니라 이미지뷰에서도 많이 사용하는 속성으로 다음과 같은 효과가 있습니다.

scaleType Options Content
matrix 실제 이미지를 좌측 상단부터 이미지 버튼 크기만큼만 보여줍니다.
fitXY 상하좌우를 이미지뷰 또는 이미지버튼 크기에 맞춰 늘려줍니다.
fitStart 좌측 상단부터 시작해서 비율에 맞게 이미지 크기를 조절하여 위젯 안에 채웁니다.
fitCenter 중앙을 기준으로 비율에 맞게 이미지 크기를 조절하여 위젯 안에 채워줍니다.
fitEnd 우측 하단부터 시작해서 비율에 맞게 이미지 크기를 조절하여 위젯 안에 채워줍니다.
center 실제 이미지 사이즈대로 정중앙에 위치시킵니다. 이미지가 위젯보다 크면 위아래가 잘립니다.
centerCrop 가로세로 사이즈 중 근접한 길이를 기준으로 나머지 한쪽을 잘라서 비율을 맞춰줍니다. 뷰에 이미지를 가득 채워주기 때문에 앨범 이미지를 섬네일로 보여줄 때 많이 사용합니다.
centerInside 이미지가 위젯보다 크면 fitCenter와 동일하게 동작하고, 작으면 위젯의 중앙에 위치시킵니다.

 

이미지 영역에 색 채우기, 투명도 조절하기 : tint, alpha

1. tint는 이미지 영역에 색을 채우는 속성입니다. 2. alpha는 투명도를 조절합니다.

 

2.5 라디오그룹과 라디오버튼

라디오버튼(RadioButton)은 여러 개의 선택지 중에서 하나만 선택할 때 사용합니다. 라디오 그룹(RadioGroup)과 함께 사용하면 다루기가 더 쉽습니다. (rdGroup는 라디오그룹 변수)

// 라디오버튼 클릭 리스너, RadioGroup = 바뀐 라디오그룹, i = 라디오버튼의 Id
rdGroup.setOnCheckedChangeListener { RadioGroup, i -> }

 

라디오버튼 배치 및 미리 선택 설정하기 : orientation, checkedButton

orientation은 라디오버튼들을 가로, 세로 정렬 선택을 할 수 있으며, checkedButton은 미리 선택되어 있는 라디오버튼을 설정할 수 있습니다.

 

2.6 체크박스

체크박스(CheckBox)는 라디오버튼처럼 여러개의 선택지에서 하나 이상을 선택할 때 사용합니다.
(checkBtn은 체크박스 변수)

// 체크박스 클릭 리스너 compoundButton = 상태변화된 체크박스위젯변수, b = boolean타입의 체크 여부
checkBtn.setOnCheckedChangeListener { compoundButton, b -> }

 

컴파운드버튼
체크박스는 컴파운드버튼(compoundButton)이라는 부모를 가지고 있습니다. 체크박스뿐만 아니라 토글버튼(ToggleButton), 스위치(Switch) 위젯도 컴파운드버튼을 기반으로 만들어져 있기 때문에 첫 번째 파라미터로 compounButton이라는 이름이 넘어오는 것입니다.

 

2.7 토글버튼, 스위치 이미지뷰

토글버튼(ToggleButton)은 체크박스와 동일합니다. 부모 클래스인 CompoundButton을 상속받아 사용학 ㅣ때문에 체크박스의 리스너와 구현이 완전히 동일합니다. 단지 화면에 나타나는 모양만 조금 다릅니다.
스위치(Switch)도 체크박스와 구현이 동일하며 체크박스, 토글버튼, 스위치는 모두 CompoundButton을 상속받아 사용하기 때문에 하나의 사용법만 익히면 동일한 리스너로 컨트롤할 수 있습니다.
이미지뷰(ImageView)는 이미지버튼과 사용법은 유사하고 리스너를 달아서 click 이벤트도 받을 수 있지만 이미지를 보여주는 용도로만 사용하는 것이 좋습니다. src, background, scaleType과 같은 주요 속성 또한 이미지버튼과 동일합니다.

 

2.8 프로그래스바

프로그래스바는 진행상태를 나타내는 위젯입니다.
주로 파일 다운로드와 같이 시간이 일정하지 않을 작업을 할 때 '현재 진행 중'임을 보여주는 용도로 많이 사용합니다.
또한 진행 중임과 동시에 얼마정도 진행되었는지 진척도를 %로 보여주는 용도로 사용합니다.

공통 속성 visibility
VISIBLE: 현재 보이는 상태
INVISIBLE: 현재 안보이는 상태, 보이지는 않지만 공간을 차지하고 있습니다. 예를 들어 리니어 레이아웃에서 3개의 버튼을 배치하고 가운데 버튼을 INVISIBLE 처리하면 가운데가 빈 상태로 화면에 나타납니다.
GONE: 현재 안 보이는 상태, 보이지도 않고 공간도 차지하지 않습니다. 리니어 레이아웃에서 3개의 버튼을 배치하고 가운데 버튼을 GONE 처리하면 세 번째 버튼이 두 번째 버튼 자리로 이동합니다.

 

화면을 그려주는 메인 스레드
안드로이드는 메인 스레드(Main Thread)라는 개념이 있는데, 화면에 그림을 그려주는 것이 메인 스레드의 역할입니다. 화면을 그리는 것은 모두 메인 스레드에서 실행되어야 합니다(다른 이름으로 UI Thread라고 불리기도 합니다.)

onCreate() 메서드 안의 코드는 모두 메인 스레드에서 동작하기 때문에 Thread.sleep 함수를 사용하면 화면을 그리는 것도 모두 멈추게 됩니다. 만약 Main.Thread 동작 준비 동안에 프로그레스바를 통한 지연상황을 화면에 그리고 싶다면 thread(start=ture) 함수를 사용하면 함수 블록 안의 코드가 모두 백그라운드에서 동작합니다.

runOnUiThread 메소드에 UI와 관련된 코드는 전부 이 안에서 동작해야만 프로그램이 강제종료 하지 않습니다 :)

 

 

2.9 시크바

시크바(SeekBar)는 볼륨을 조절하거나 뮤직플레이어에서 재생 시간을 조절하는 용도로 많이 사용합니다.

  • seekBar: 리스너가 동작하고 있는 시크바 위젯
  • progress: 현재 시크바의 현재 progress 값
  • fromUser: 사용자 터치 여부(코드에서 시크바를 움직일 수도 있기 때문에 사용자의 터치에 의해 동작하는 것인지를 알기 위한 값)

 

2.10 레이팅바

ratingBar.setOnRatingChangeListener { ratingBar, rating, fromUser ->
	textView.text = "$rating"
}
  • numStars: 전체 
  • rating: 현재 별점
  • fromUser: 사용자 입력 여부
리스너 다음에 오는 중괄호 vs 괄호
리스너(Lisner)는 코틀린 문법에서 배운 인터페이스입니다. 안드로이드는 리스너라는 개념으로 인터페이스를 제공하는데 인터페이스 안에 개발자가 구현해야 되는 함수의 목록이 미리 정의되어 있습니다.

레이팅바(Rating Bar)의 리스너
public interface OnRatingBarChangeListener {
    void onRatingChanged(RatingBar var1, float var2, boolean var3);
}
이렇게 정의되어 있는 함수의 개수가 1개면 함수명을 작성하지 않고 중괄호를 사용해서 처리할 수 있습니다.
중괄호를 사용해서 코드를 축약한 형태를 '람다식'이라고 합니다.

시크바(SeekBar)의 리스너
public interface OnSeekBarChangeListener {
    void onRatingChanged(SeekBar var1, int var2, boolean var3);

    void onStartTrackingTouch(SeekBar var1);

    void onStopTrackingTouch(SeekBar var2);
}

함수가 2개 이상이면 괄호를 사용하고 인터페이스에 정의되어 있는 함수를 모두 구현해야만 정상적으로 동작합니다. 그래서 시크바의 리스너는 괄호를 사용해야 하고, 괄호 안에 오브젝트 형태로 모든 함수를 구현하는 것입니다. 함수가 1개인 리스너에 괄호를 사용하는 것은 코드가 길어질 뿐 정상 동작합니다.

 

2.11 리소스 다루기

drawable

안드로이드는 스마트폰마다 가로세로 화소(픽셀)의 개수가 다르기 때문에 사이즈를 표시하는 단위로 가상 화소 개념인 dp를 사용합니다. dp는 화면 밀도인 DPI에 따라서 실제 픽셀로 변환되는 크기가 달라지는데, drawable 또한 DPI에 따라서 서로 다른 이름의 디렉터리를 사용합니다.

 

DPI

DPI(Dots Per Inch)는 가로세로 1인치(2.54cm)의 정사각형 공간에 들어 있는 픽셀의 숫자를 나타내는 단위입니다. 안드로이드는 160DPI를 기본으로 사용하는데 이른 mdpi라고 합니다. 내 스마트폰의 DPI가 mdpi라면 화면을 켰을 때 1인치의 사각형 안에 160개의 화소가 그려집니다.

표현 1인치 안의 화소수 비고
idpi 120 사용하지 않음
mdpi 160 기준
hdpi 240  
xhdpi 320 1dp=2pixel
xxhdpi 480 1dp=3pixel
xxxhdpi 640 1dp=4pixel

 

dp 또는 dip(Device independent Pixels)

dp는 안드로이드에서 사용하는 독립적 수치 단위로, 해상도에 관계없이 동일한 크기로 화면에 표시하기 위해서 사용됩니다. 예를 들어 가로세로가 각각 3dp인 사각형을 mdpi 스마트폰에 그릴때와 xhdpi 스마트폰에 그릴 때, 두 화면에서 같은 크기로 보이기 위해서 다음과 같이 그립니다.

 

mipmap

안드로이드는 drawable과 더불어 앱의 아이콘에 사용하는 용도로 mipmap 디렉터리를 제공합니다. mipmap은 앱 아이콘 관리용으로만 사용되도록 권장되므로 일반 이미지는 drawable에 넣고 사용해야 합니다.

 

adaptive icon

mipmap-anydpi-v26 디렉터리 안에 있는 ic_launcher.xml 파일을 열어보면 다음과 같은 XML 코드가 작성되어 있는데, 태그명에서 유추할 수 있듯이 백그라운드 이미지와 포어그라운드 이미지 2개를 포개어서 아이콘으로 그려주는 역할을 합니다.

<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background">
    <foreground android:drawable="@drawable/ic_launcher_foreground">
</adaptive-icon>

<background> 태그값에 @drawable/ic_launcher_background 파일명이 지정되어 있는데, 이 파일에 앞서 설명한 벡터 기반의 이미지가 입력되어 있습니다. 이런 구조를 adaptive icon라고 합니다. 이미지 아이콘과 동일하게 AndroidManifest.xml에 있는 <application> 태그의 icon 속성에 적용하고 사용합니다.

 

strings 다루기

안드로이드는 strings.xml을 Translations Editor를 통해서 관리할 수 있습니다.

 

다국어 처리하기

Translations Editor는 사실 다국어를 처리하는 데 목적이 있습니다.

(Translations Editor에서 지구본을 클릭 한 후에 korean이라고 치면 자동으로 검색됩니다. 이후 strings 목록의 칼럼에 Korean(ko)가 추가됩니다.)

(프로젝트 트리를 보면 string.xml (ko-rKR)이 추가된 것을 볼 수 있습니다. 다국어 처리, mipmap, drawable 등 이미지, xml파일 관리 시에는 project 모드로 놓고 사용하는 것이 좋습니다. :))

(Translations Editor로 돌아와서 [Korean(ko)] 항목을 채우면 strings.xml(ko) 파일에 Key와 같은 Key가 적용된 <string> 태그에 한글 Value가 입력되어 있습니다.)

이렇게 Translations Editor를 이용해서 각 국가별 strings.xml이 구성되어 있는 앱을 스마트폰에 설치하면, 해당 스마트폰의 언어 설정에 맞는 strings.xml의 값을 사용해서 화면에 문자를 세팅합니다. 만약 구성에 없는 언어 설정일 경우는 기본으로 제공되는 strings.xml의 값이 사용됩니다.
예를 들어 strings.xml에 영어로 값을 입력하고, strings.xml(ko)를 생성해서 한국어 값을 입력한 후 앲을 배포했는데, 사용자 스마트폰의 언어 설정이 일본어라면 영어로 된 값을 사용합니다. 그래서 출시국을 기준으로 기본 strings.xml의 언어를 정하는 것이 좋습니다.
(실제 앱 개발 시에는 Translations Editor를 사용하는 경우는 드뭅니다. 대부분 다국어가 들어가 있는 DB나 액셀 형태의 Sheet를 사용해서 한꺼번에 입력한 후, 코드로 각각의 strings.xml을 생성해서 사용합니다.)

 

- 미니 퀴즈 4-2 -

1. 라디오버튼의 코드를 응용해서 라디오버튼을 클릭하면 화면의 텍스트뷰에 값이 출력되는 코드를 작성해보시오.

 

 

2. 체크박스 코드를 실행하면 매번 하나의 아이템 상태만 출력되는데, 한 번 체크할 때 현재 체크되어 있는 모든 아이템을 출력하는 코드를 작성해보세요. 예를 들어 바나나가 체크되어 있는 상태에서 추가로 오렌지가 체크되면 "바나나와 오렌지가 체크되었습니다." 라는 메시지를 출력하면 됩니다.

 

[activity_main.xml]

 

[MainActivity.kt]

 

[Logcat]

 

3. 프로그래스바 아래에 텍스트뷰를 배치하고 1초에 한 번, 1씩 증가하는 숫자를 출력하는 코드를 작성해보세요.

 

이상 마치겠습니다. :)