Activity에 hello world 출력하기


 코틀린으로 안드로이드 개발을 시작해보려고 합니다. 첫걸음으로 Hello World와 Hello Kotlin을 출력하고 버튼을 하나 추가하여 두 문장을 교대로 변경하도록 해보았습니다.


 기억이 가물가물하여 대충 생각하는 코틀린 문법들을 이용해서 작성해봤습니다. 다시 코틀린 문법을 되새기면서 안드로이드에 어떻게 적용되는지 살펴보도록 하겠습니다.


 1차 목표는 예전에 Android Programming: The Big Nerd Ranch Guide(2nd ed.) 를 공부하면서 접한 예제를 응용하여 학습용으로 만들어두었던 국가 수도 맞추기 앱을 코틀린으로 포팅하는 것입니다.



activity_main.xml

 <?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="kotlinapp.circus.com.kotlinapplication.MainActivity">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change Sentence"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>



MainActivity.java

 package kotlinapp.circus.com.kotlinapplication


import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var tv = findViewById(R.id.tv) as TextView
tv.setText("Hello Kotlin")

tv2.setText("Hello World")

var button:Button = findViewById(R.id.button)
button.setOnClickListener(View.OnClickListener {
var temp:String? = null
temp = tv.text.toString()
tv.setText(tv2.text)
tv2.setText(temp)
})
}
}





국가 수도 맞추기 앱 간단 소개

이전, 다음으로 이동할 때마다 선택지 위치 및 오답 종류가 변경됩니다.

정답을 선택하면 정답! 틀리면 오답! 토스트를 띄웁니다.





List & Map


 이번에는 대표적인 콜렉션(Collection)인 list와 키와 값을 가지는 저장 클래스인 map를 살펴보겠습니다. 코틀린에서는 Java를 포함한 다른 언어들과 다르게 list와 map을 읽기 전용(read only) 객체와 수정 가능한(mutable) 객체 두 가지로 형태로 나누어 제공합니다.


List


 리스트를 만들 때 사용하는 함수는 listOf 입니다. 이렇게 생성시 읽기 전용 모드가 됩니다.

읽기 모드로 생성시 어차피 변경이 불가능하기 때문에 val로 선언해주는 것이 좋을 것 같습니다.


fun main(args: Array<String>) {

    val name = listOf("Kim", "Hong", "Park")

    for (i in 0..name.size-1) {

        println(name.get(i))

    }

//    name.add("Choi") 읽기 전용이라 추가 불가

Kim

Hong

Park


 수정 가능하게 만드려면 mutableListOf를 사용하면 됩니다.


fun main(args: Array<String>) {

    var name = mutableListOf<String>("Kim", "Hong", "Park")

    name.add("Choi")

    name.add("Seo")

    for (i in 0..name.size-1) {

        println(name.get(i))

    }

Kim

Hong

Park

Choi

Seo


 (특이점) val name = mutableListOf<String>("Kim", "Hong", "Park")로 선언시에도 name.add는 가능합니다. 다만 name이 reassign 될 수 없을 뿐입니다.


Java에서도 final 변수이지만 arrayList의 데이터는 조작이 가능합니다.


import java.util.ArrayList;


public class TestJava {

public static void main(String[] args) {

final ArrayList<String> arr = new ArrayList<>();

arr.add("TEST1");

arr.add("TEST2");

arr.add("TEST3");

for (String a : arr) {

System.out.println(a);

}

}

TEST1

TEST2

TEST3


 콜렉션에는 null을 필터할 수 있는 함수인 filterNotNull()이 존재합니다. 다음과 같이 null이 존재하면 null을 제외한 엘리먼트만 출력되는 것을 볼 수 있습니다.


fun main(args: Array<String>) {

    val name = listOf("Kim", "Hong", null, "Park")

    for (i in name.filterNotNull()) {

        println(i)

    }

Kim

Hong

Park


 또한 콜렉션에서는 +, - 를 사용하여 엘리먼트를 추가 및 삭제할 수 있습니다.


fun main(args: Array<String>) {

    val name = listOf("Kim", "Hong", null, "Park")

    for (i in name.filterNotNull()) {

        println(i)

    }

    println("-----")

    var newName = name - "Hong"

    for (i in newName.filterNotNull()) {

        println(i)

    }

    println("-----")

    newName = name + "Choi"

    for (i in newName.filterNotNull()) {

        println(i)

    }

Kim

Hong

Park

-----

Kim

Park

-----

Kim

Hong

Park

Choi



Map

List와 유사하게 mapOf로 읽기 전용, mutableMapOf로 수정 가능한 Map을 만들 수 있습니다.


fun main(args: Array<String>) {

    val name = mapOf(1 to "Kim", 3 to "Park")

    for (i in name.keys) {

        println(name.get(i))

    }


    for (i in name) {

        println(name)

    }

Kim

Park

{1=Kim, 3=Park}

{1=Kim, 3=Park}



fun main(args: Array<String>) {

    val name = mutableMapOf(1 to "Kim", 3 to "Park")

    name.put(2, "Hong")

    

    for (i in name.keys) {

        println(name.get(i))

    }


    for (i in name) {

        println(name)

    }

Kim

Park

Hong

{1=Kim, 3=Park, 2=Hong}

{1=Kim, 3=Park, 2=Hong}

{1=Kim, 3=Park, 2=Hong}


또한 +, - 를 사용할 수 있는데 value으로는 안되며 key로 가능합니다.


fun main(args: Array<String>) {

    val name = mapOf(1 to "Kim", 3 to "Park")

    for (i in name.keys) {

        println(name.get(i))

    }

    println("-----")

    var newName = name - 3

    for (i in newName.keys) {

        println(newName.get(i))

    }

Kim

Park

-----

Kim


배열 (Array)


 코틀린에서 배열은 Array 클래스로 표현됩니다. 해당 클래스는 (연산자 오버로딩 규칙에 의해 []로 바뀌는) get과 set 함수를 가지고 있고 size 프로퍼티도 가지고 있습니다. 그 외 다른 유용한 멤버 함수들도 있습니다.

(Ref. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-array/index.html)


class Array<T> private constructor() {

    val size: Int

    operator fun get(index: Int): T

    operator fun set(index: Int, value: T): Unit


    operator fun iterator(): Iterator<T>

    // ...

}


 배열 인스턴스는 arrayOf, arrayOfNulls 및 emptyArray 표준 라이브러리 함수를 사용하여 만들 수 있습니다.


 예를 들어 배열을 만들기 위해서 arrayOf() 라이브러리 함수를 사용해서 값을 전달하면 됩니다. 즉, arrayOf(1, 2, 3)가 array[1, 2, 3]을 만듭니다. 다음의 예제를 보면 이해하기 쉽습니다.


fun main(args: Array<String>) {

    var a = arrayOf(1, 2, 3)


    println(a.get(0))

    println(a.get(1))

    println(a.get(2))


    println(a[0])

    println(a[1])

    println(a[2])


    a[0] = 100

    a.set(1, 200)


    println(a[0])

    println(a[1])

    println(a[2])

}

1

2

3

1

2

3

100

200

3


 코드를 보면 앞서 말했듯이 get과 set 함수는 []로 바뀔 수 있습니다. 해당 함수의 형태와 역할은 다음과 같습니다.


 get : 특정 인덱스의 엘리먼트를 리턴하며 인덱스 연산자 []로 호출할 수 있음

 operator fun get(index: Int): T


set : 특정 인덱스의 특정 값을 set 하며 인덱스 연산자 []로 호출할 수 있음

 operator fun set(index: Int, value: T)


 또한 코틀린은 Boxing overhead 없이 기본 유형(primitive type)의 배열을 표현하기 위한 전문 클래스를 가지고 있습니다. ByteArray, ShortArray, IntArray 등이 있습니다. 이 클래스들은 Array 클래스와 상속 관계가 없지만 동일한 메서드 및 프로퍼티 집합을 가집니다. 그리고 각 클래스는 그에 맞는 팩토리 함수를 가지고 있습니다.


 fun main(args: Array<String>) {

    var a: IntArray = intArrayOf(1, 2, 3)

    for (i in a) {

        println(i)

    }


    var b: ShortArray = shortArrayOf(10, 20, 30)

    for (i in b) {

        println(i)

    }

}

1

2

3

10

20

30


 생성자를 이용해서 다음과 같이 배열을 만들 수도 있습니다.


(Ref. https://github.com/JetBrains/kotlin/blob/1.2.0/core/builtins/native/kotlin/Array.kt#L31)


 fun main(args: Array<String>) {

    val a = Array(5, { i -> (i * i) })

    for (i in a) {

        println(i)

    }

}

0

1

4

9

16


 결과 값이 왜 저렇게 될까요?

 레퍼런스를 보시면 'each element is calculated by calling the specified [init] function.'라고 합니다.

 init함수가 square numbers(제곱)를 생성하고 있는 것으로 보입니다.

+ Recent posts