저희 팀은 원래 1-2주차에 Kotlin Koans(혹은 Example)를 끝내려고 했으나 생각보다 양이 방대하고(ㅠㅠ) 중간중간 지엽적인 내용이 많다고 판단해 각자 원하는 부분만 공부하고 실습하면서 Kotlin 문법을 더 자세히 익히기로 했습니다!
Kotlin Koans를 모두 보는 것은 아주 아주 비추입니다
Build Your First App
팀원 모두가 이전에 Android Studio를 사용한 경험이 있어서 첫 번째 장은 퀴즈만 함께 풀고 넘어갔습니다.
이전에 안드로이드 스튜디오로를 사용해 본 사람들에게는 문제가 쉬운 편이지만 5번 문제는 조금 어려웠습니다.
[Gradle]은 앱의 프로젝트 구조, 구성 및 종속성을 설명하기 위해 도메인 별 언어를 사용하는 빌드 자동화 시스템이다.
[앱을 컴파일하고 실행하면 Gradle 빌드가 실행 중이라는 정보를 볼 수 있다.]
그렇다고 합니다.
Build an interactive app
1. 기본적인 안드로이드 프로젝트의 구조
이 코드랩에서는 DiceRoller 앱을 만들면서 안드로이드 앱의 주요 컴포넌트들을 배우고 앱에 버튼을 통한 간단한 상호작용을 추가합니다.
우선 activity_main.xml 파일을 열고 주사위의 결과를 띄울 TextView인 result_text와 주사위를 굴리는 Button인 roll_button을 만듭니다.
버튼 태그를 다음과 같이 작성했을 때
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Roll" />
Roll에 커서를 놓은 후 Alt + Enter를 누르고 Extract string resource를 선택합니다.
Resource name 란에 roll_label이라고 입력하고 OK 버튼을 누르면 string.xml 파일에 다음과 같이 추가됩니다.
<resources>
<string name="app_name">DiceRoller</string>
<string name="roll_label">Roll</string>
</resources>
이렇게 문자열을 분리된 파일에 두면 관리하기 쉬워집니다.
activity_main.xml을 다음과 같이 작성했다면
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center_vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/result_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="30sp"
android:text="Dice Rolled!" />
<Button
android:id="@+id/roll_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/roll_label" />
</LinearLayout>
MainActivity.kt을 작성합니다.
package com.example.android.diceroller
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rollButton: Button = findViewById(R.id.roll_button)
rollButton.setOnClickListener { rollDice() }
}
private fun rollDice() {
// Toast.makeText(this, "button clicked",
// Toast.LENGTH_SHORT).show()
val randomInt = (1..6).random() // 1에서 6의 값을 랜덤으로 리턴함
val resultText: TextView = findViewById(R.id.result_text)
resultText.text = randomInt.toString()
}
}
(resultText를 rollDice 함수에서 초기화하는 게 이상해 보이는데 다음 코드랩에서 수정합니다!)
기존에 Java로 작성하는 것과 크게 다르지 않습니다.
Java에 비해 버튼에 OnClickListener를 사용하는 부분이 간결해졌네요.^_^
여기까지 작성하고 실행해보면 Roll 버튼을 누를 때마다 DiceRolled!라고 되어 있던 부분이 랜덤한 숫자로 바뀝니다.
하지만 이걸 주사위 굴리기 앱이라고 볼 수는 없습니다.
2. 이미지 리소스와 호환성
이 코드랩에서는 DiceRoller 앱을 이미지 리소스를 사용해 제대로 된 주사위 굴리기 앱으로 만듭니다. 그리고 다른 안드로이드 버전 간 앱 호환성과 어떻게 안드로이드 Jetpack이 도움이 되는지에 대해서도 배웁니다.
우선 DiceImages.zip을 다운로드 받습니다.
프로젝트 상단에 있는 드랍다운 메뉴를 클릭해 Android로 돼 있던 것을 Project로 바꿉니다.
그리고 다운로드한 xml 파일들을 [app -> src -> main -> res -> drawable] 폴더에 추가합니다. (drawalbe-v24 폴더에 추가하거나 다운로드 받은 폴더를 통채로 추가하지 않도록 주의!)
Android 뷰로 돌아가서 drawable 폴더에 xml 파일이 추가된 것을 볼 수 있습니다.
dice_1.xml 파일을 더블클릭하면 xml 코드를 볼 수 있고 그 다음에 우측에 Preview 버튼을 누르면 이 벡터 drawable이 실제로 어떻게 보이는 지 미리보기를 할 수 있습니다.
activity_main.xml을 열고 Text 탭에서 TextView를 지우고 다음 코드로 바꿔줍니다.
<ImageView
android:id="@+id/dice_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/dice_1" />
이제 MainActivity.kt를 수정해봅시다.
onCreate 함수의 마지막 두 줄을 지우고 다음 코드를 작성합니다.
val diceImage: ImageView = findViewById(R.id.dice_image)
randomInt 값에 따라 특정한 주사위 이미지를 선택하도록 when 블럭을 추가합니다.
val drawableResource = when (randomInt) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
setImageResource를 사용해 소스를 업데이트합니다.
diceImage.setImageResource(drawableResource)
앱을 컴파일하고 실행해보면 Roll 버튼을 눌렀을 때 주사위가 적절한 이미지로 업데이트됩니다.
하지만 지금 코드는 사용자가 주사위를 굴릴 때마다 findViewById가 호출되므로 효율적이지 않습니다..
앱이 효율적으로 실행되도록 코드를 수정해봅시다!
onCreate 함수 전에 다음 코드를 작성합니다.
var diceImage : ImageView? = null
바로 초기화해주면 좋겠지만 onCreate에서 setContentView가 호출되기 전에는 레이아웃의 뷰에 접근할 수 없기 때문에 null로 초기화해야 합니다.
이렇게 하면 diceImage를 사용할 때마다 null 값 체크를 해야해서 코드를 복잡하게 만듭니다. 더 나은 방법으로 바꿔봅시다.
null 값을 주지 말고 lateinit 키워드를 추가합니다.
lateinit var diceImage : ImageView
lateinit 키워드는 코틀린 컴파일러에게 해당 변수가 사용되기 전에 초기화될 것이라고 약속하는 것입니다.
onCreate에서 setContentView가 호출된 후에 다음 코드를 추가합니다.
val diceImage: ImageView = findViewById(R.id.dice_image)
rollDice 함수에서 diceImage를 정의하는 코드를 지워줍니다.
그리고 앱을 컴파일하고 실행하면 원하는 대로 작동이 되지만 맨 처음에 주사위를 굴리지 않았는데 주사위 이미지가 뜨는 게 마음에 들지 않습니다.
activity_main.xml 파일을 Text 탭으로 열고 ImageView의 android:src를 다음과 같이 변경합니다.
android:src="@drawable/empty_dice"
ImageView에서 android:src 속성을 복사한 후 android를 tools로, empty_dice를 dice_1으로 변경합니다.
android:src="@drawable/empty_dice"
tools:src="@drawable/dice_1"
tools 네임스페이스는 안드로이드 스튜디오에서 미리보기나 디자인 에디터에서만 사용되는 placeholder를 정의할 때 사용됩니다. 따라서 tools 네임스페이스를 사용하는 속성은 컴파일 할 때는 제거됩니다.
네임스페이스는 이름이 같은 속성을 참조할 때 모호성을 해결합니다(C에서의 namespace와 같은 역할인 듯).
이제 앱을 실행해 보면 roll 버튼을 누르기 전까지는 아무 것도 보이지 않다가 버튼을 누르면 알맞은 주사위 이미지가 나타나는 것을 볼 수 있습니다!
이제 API 레벨과 호환성, 구버전의 기기들을 지원하는 Android Jetpack 라이브러리를 사용하는 방법에 대해 알아봅시다.
Gradle Scripts -> build.gradle (Module: app) 파일을 열어보면 compileSdkVersion, minSdkVersion, targetSdkVersion 등을 볼 수 있습니다.
targetSdkVersion은 대부분 compileSdkVersion과 같습니다.
MainActivity.kt를 보면 AppCompatActivity를 상속합니다.
AppCompatActivity는 다른 플랫폼 OS 레벨 간에 액티비티가 똑같이 보이는 것을 보장하는 호환성 클래스입니다. 이는 androidx.appcompat.app.App.CompatActivity 패키지에서 임포트된 클래스입니다. Android Jetpack 라이브러리의 네임스페이스가 androidx입니다.
다시 build.gradle (module: app)으로 돌아가서 dependencies를 보면
androidx의 AppCompatActivity를 포함하고 있는 appcompat 라이브러리에 대한 dependency가 정의되어 있습니다.
비트맵 이미지(ex. PNG)에 비해 벡터 이미지는 사이즈가 변경되어도 이미지의 퀄리티가 변하지 않고 용량도 적게 차지합니다. 벡터 drawable은 API 21 이상에서 지원하는데 API 레벨이 19인 디바이스에서 실행하더라도 정상적으로 실행이 됩니다. 이는 앱을 빌드할 때 Gradle 빌드 프로세스가 벡터 이미지로부터 PNG 이미지를 생성하기 때문입니다. 하지만 이 PNG 파일들은 앱의 크기를 키우기 때문에 이는 좋은 방법이 아닙니다.
이를 위해 API 레벨 7로 돌아가는 벡터 drawable을 위한 Android X 라이브러리가 있는 것입니다.
build.gradle (Module: app)에서 defaultConfig 부분에 다음 코드를 추가해줍니다.
vectorDrawables.useSupportLibrary = true
Gradle 파일이 변경될 때마다 빌드 파일과 프로젝트를 동기화해야 한니다.
빌드 파일을 변경하면 상단에 동기화하라는 메시지가 뜨는데 여기에서 Sync Now를 클릭하거나 우측 상단의 Sync Project with Gradle Files 버튼을 클릭하면 됩니다.
activity_main.xml 파일을 열고 루트 태그에 다음 코드가 없다면 추가해줍니다. (아마 자동으로 생성돼 있을 것)
xmlns:app="http://schemas.android.com/apk/res-auto"
ImageView 태그에서 android:src 속성을 app:srcCompat으로 변경합니다.
app:srcCompat="@drawable/empty_dice"
app:srcCompat 속성은 안드로이드 구버전에서 벡터 drawable을 지원하는 Android X 라이브러리를 사용합니다.
앱을 실행해 보면 겉으로는 바뀐 것이 없지만 낮은 API 레벨의 기기에서 실행했을 때 더이상 PNG 파일을 생성하지 않을 것입니다.
여담으로 이번 코드랩은 특히 저한테 도움이 많이 됐습니다.
작년 한 해 동안 안드로이드 앱을 주제로 한 프로젝트만 4개 정도 했었는데 항상 주먹구구식으로 개발을 하다 보니 build.gradle 파일을 다루는 부분, srcCompat을 사용하는 부분은 구글링해서 예시만 찾아보고 구체적으로 어떤 역할을 하는지는 모르는 채로 코드를 작성했었는데 이제 궁금증이 조금 해결되었습니다.
작년에 특히 androidx 때문에 정말 애 먹었습니다. 그 때만 생각하면 눈물이 날 지경입니다.^_ㅠ
당시 세 명이서 협업을 하다보니 호환 관련 에러가 자주 발생했는데 그걸 해결하려고 migrate to androidx를 하면 정말 과장이 아니라 코드의 거의 모든 줄에 빨간 줄이 생기고 그랬습니다. 정말 눈물 나죠?
androidx가 2018년에 생겼다는데 2019년은 과도기였는지 migrate to androidx에 대한 정보를 찾기가 너무 어렵더라구요.
지금 돌이켜 생각해보면 공식문서를 찾아볼 걸 싶다가도 그때 시간적으로 너무 여유가 없어서 뭔가 이해하고 넘어갈 겨를이 없었지 않나 싶기도 합니다.
그때 너무 힘들었어서 다시는 안드로이드 개발은 안해야겠다고 다짐했는데 어쩌다보니 그 이후에도 안드로이드 앱 프로젝트를 한 번 더 하고 지금도 안드로이드 스터디를 하고 있네요.
다른 분들은 부디 어떤 프로젝트를 하든지 완성하는 데 급급해하지 말고 프로젝트를 통해서 뭐라도 얻어가시길 바랍니다. 그러려면 시간 여유가 있어야되는데 시간 여유가 있으려면 일정 관리를 잘 해야겠죠? 다들 일정 관리 잘 하십쇼..!
3. Learn to help yourself
이 코드랩에서는 템플릿, 문서, 동영상 그리고 샘플 앱 같이 코틀린 안드로이드 개발자에게 유용한 리소스에 대해서 배웁니다.
Create New Project 창에서 Basic Activity 템플릿을 선택한 후 Next 버튼을 누릅니다.
Language를 Kotlin으로 선택하고 Use androidx.* artifacts에 체크되어 있는지 확인한 후 Finish를 누릅니다.
앱을 실행시키면 다음과 같은 화면이 뜹니다.
① status bar: MainActivity.kt에서 status bar를 숨길 수 있음
② app bar(또는 action bar): activity_main.xml에서 AppBarLayout 태그 안에 Toolbar가 있음
③ app name: 초기값은 패키지 이름에서 따옴. AndroidManifest.xml에서 android:label 속성값. string.xml에 app_name 문자열이 정의돼 있음.
④ options-menu overflow button: MainActivity.kt에서 onOptionsItemSelected()를 사용해 사용자가 메뉴를 선택했을 때의 동작을 구현함. res/menu/menu_main.xml 파일에서 options-menu 아이템을 볼 수 있음.
⑤ CoordinatorLayout 뷰그룹: activity_main.xml에서 include 태그에 layout 속성의 속성값이 content_main. content_main.xml은 이 뷰그룹에 포함됨. (무슨 말인지 잘 모르겠음)
⑥ TextView: content_main.xml에 있음. app의 모든 UI 요소가 content_main.xml에서 정의되어야 함.
⑦ floating action button(FAB): activity_main.xml에서 FloatingActionButton 태그. MainActivity.kt에서 OnClick() 리스너 사용 가능.
strings.xml을 통해 앱 이름을 바꿔봅시다.
<string name="app_name">New Application</string>
activity_main.xml에서 app bar(Toolbar)의 색깔을 바꿔봅시다.
android:background="?attr/colorPrimaryDark"
앱 이름과 app bar의 색깔이 바뀌었습니다.
FAB를 누르면 하단에 snackbar가 나타납니다.
snackbar 텍스트를 바꿔봅시다.
MainActivity.kt의 onCreate 안에 setOnClickListener 안의 코드를 변경하면 됩니다.
fab.setOnClickListener { view ->
Snackbar.make(view, "This FAB needs an action!", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
colors.xml에서 colorAccent를 변경해봅시다.
<color name="colorAccent">#1DE9B6</color>
프로젝트가 이미 생성되고 나서도 액티비티 템플릿을 사용할 수 있습니다.
Android pane에서 java 폴더를 우클릭 하고 [New -> Activity -> Gallery ...]를 선택합니다.
원하는 액티비티를 선택해 추가하면 됩니다.
구글에서 제공하는 샘플 코드를 통해 공부해봅시다.
여기서는 Android Jetpack 컴포넌트의 예시인 android-sunflower app을 다운로드 받고 안드로이드 스튜디오에서 열어줍니다. 이때 build 하는 데 다음과 같은 에러가 발생했습니다.
New Gradle Sync is not supported due to containing Kotlin modules
File -> Settings를 열어서 Kotlin Compiler에서 Target JVM version을 1.6에서 1.8로 바꿔주었습니다.
또 다른 에러도 발생해서 안드로이드 스튜디오를 최신 버전으로 업데이트했습니다.
안드로이드 스튜디오 상단에 Help -> Check for updates...를 선택하고 Update and Restart를 누르면 됩니다.
이제 빌드가 정상적으로 됩니다!
실행해보면 귀엽고 멋진 앱을 볼 수 있습니다.
이제 소스파일들을 열어보면서 익숙하지 않은 클래스, 타입 또는 프로시저를 찾고 Android Developer 문서를 찾아보며 공부하면 됩니다.
런처 아이콘을 바꿔봅시다. 우선 현재 아이콘은 이렇습니다.
프로젝트의 Android pane에서 res 폴더를 우클릭하고 New -> Image Asset을 선택합니다.
Asset Type을 Clip Art로 선택합니다.
그리고 위 사진에서 Clip Art의 로봇 아이콘을 누르면 Select Icon 창이 뜹니다.
여기서 mood 아이콘을 선택하고 OK를 누릅니다.
Background Layer 탭에서 Asset Type을 Color로 선택한 후 Color를 변경해줍니다.
Options 탭에서 설정할 게 있으면 설정해주고 Next를 누릅니다.
아이콘 파일이 추가되는 위치와 덮어쓰기 되는 파일을 확인한 후 Finish를 누릅니다.
앱을 재설치하고 디바이스를 켜 보면 아이콘이 적용되어 있습니다.
마지막으로 안드로이드 공식 문서 사이트들입니다!
구글이 관리하는 공식 안드로이드 개발자 문서
developer.android.com/index.html
고퀄리티 안드로이드 앱의 디자인과 기능 구현의 가이드라인을 제공
머테리얼 디자인
API 정보, 참고 문서, 튜토리얼, 툴 가이드, 코드 샘플 제공
Google Play 앱 출시 정보
'안드로이드' 카테고리의 다른 글
[android - 1team] 안드로이드 experince 1 (0) | 2021.01.13 |
---|---|
[안드로이드 2팀] Layouts - LinearLayout, ConstraintLayout, Data binding (0) | 2020.12.27 |
[안드로이드 1팀] Beginner Track Unit 2 : DIsplay a scrollable list (0) | 2020.12.09 |
[안드로이드 1팀] layouts_get user input in an app (0) | 2020.11.27 |
[안드로이드 1팀] kotlin 간략 문법 (0) | 2020.11.16 |