포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 에외(Exception), Try, Catch, Finally, Toast


▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기

2018/03/15 - [Android_Kotlin] - [Android Kotlin] 문자열 템플릿(String Template) 사용법

2018/06/05 - [Android_Kotlin] - [Android Kotlin] 클래스, 프로퍼티, 커스텀 접근자 - 라디오 버튼(Radio Button)



코틀린의 예외처리는 자바와 비슷합니다. 


다른 점이 있다면 코틀린에서는 체크 예외 처리를 강제하지 않습니다. 개발자들이 예외를 잡지만 처리는 하지 않고 무시하는 코드로 작성을 흔하게 하기에 이를 고려하여 자바와는 다르게 강제하지 않은 것이 아닐까 생각합니다. 


이 역시 예제로 한번 살펴 보겠습니다.




1. 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.kotlin_test.Main1Activity.ExceptionActivity">

<EditText
android:hint="문자 입력시 예외처리"
android:id="@+id/edt_ex_str"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:gravity="center"
android:background="@null"/>

<Button
android:id="@+id/btn_ex1"
android:text="Try Catch Finally 사용 예외처리"
android:layout_width="match_parent"
android:layout_height="50dp" />
<Button
android:id="@+id/btn_ex2"
android:text="Try를 식으로 예외처리"
android:layout_width="match_parent"
android:layout_height="50dp" />
<Button
android:id="@+id/btn_ex3"
android:text="Catch에서 값 반환"
android:layout_width="match_parent"
android:layout_height="50dp" />

<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_ex_log"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="20sp"
android:gravity="center"/>
</ScrollView>
</LinearLayout>





2. 소스

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Toast
import com.kotlin_test.R
import kotlinx.android.synthetic.main.activity_exception.*
import java.io.BufferedReader
import java.io.StringReader

class ExceptionActivity : AppCompatActivity() {

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

btn_ex1.setOnClickListener {
txt_ex_log.setText("")

val reader = BufferedReader(StringReader(edt_ex_str.text.toString()))

txt_ex_log.setText(readNumber1(reader).toString())
}

btn_ex2.setOnClickListener {
txt_ex_log.setText("")

val reader = BufferedReader(StringReader(edt_ex_str.text.toString()))

readNumber2(reader)
}

btn_ex3.setOnClickListener {
txt_ex_log.setText("")

val reader = BufferedReader(StringReader(edt_ex_str.text.toString()))

readNumber3(reader)
}
}

// 자바와 동일한 방식의 Try
fun readNumber1(reader: BufferedReader): Int? {
try {
val line = reader.readLine() // 자바로 이 부분을 처리하기 위해선 체크 예외를 명시적으로 처리해야 한다.
// 하지만 코틀린에서는 함수가 던질 수 있는 예외를 명시할 필요가 없다. (throws IOException을 붙이지 않아도 된다.)
return Integer.parseInt(line)
}
catch (e: NumberFormatException) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
return null
}
finally {
reader.close()
}
}

// Try를 식으로 사용하기
fun readNumber2(reader: BufferedReader) {
val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
return // 예외가 발생하면 return
}

txt_ex_log.setText(number)
}

// Catch에서 값 반환하기
fun readNumber3(reader: BufferedReader) {
val number = try {
Integer.parseInt(reader.readLine())
} catch (e: NumberFormatException) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
null // 예외가 발생하면 null 값을 사용
}

txt_ex_log.setText(number.toString())
}
}



3. 실행 결과


포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 이터레이션(Iteration), For, In, Map, Collection


▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기

2018/03/15 - [Android_Kotlin] - [Android Kotlin] 문자열 템플릿(String Template) 사용법

2018/06/05 - [Android_Kotlin] - [Android Kotlin] 클래스, 프로퍼티, 커스텀 접근자 - 라디오 버튼(Radio Button)


코틀린에서의 이터레이션(Iteration)에 대해 알아보겠습니다.

코틀린에서의 이터레이션은 자바와 흡사합니다. 그중 while 루프는 자바와 동일하므로 이번 포스팅에 다루지 않습니다. while을 제외한 for, in, map, collection에 대해 예제로 사용법을 알아보겠습니다.


1. 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.kotlin_test.Main1Activity.ForInActivity">

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="피즈버그 게임"
android:textSize="20dp"
android:textStyle="bold"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="10"
android:orientation="horizontal">
<RadioGroup
android:id="@+id/rdo_direction"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="7"
android:orientation="horizontal"
android:gravity="center">
<RadioButton
android:id="@+id/rbtn_increase"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="증가"
android:paddingRight="20dp"/>
<RadioButton
android:id="@+id/rbtn_decrease"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="감소" />
</RadioGroup>
<EditText
android:id="@+id/edt_step"
android:hint="스텝 입력"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="3"
android:background="@null"
android:gravity="center"/>
</LinearLayout>

<Button
android:id="@+id/btn_for1"
android:text="실행"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="맵 이터레이션"
android:textSize="20dp"
android:textStyle="bold"/>
<Button
android:id="@+id/btn_for2"
android:text="실행"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="컬렉션 이터레이션"
android:textSize="20dp"
android:textStyle="bold"/>
<Button
android:id="@+id/btn_for3"
android:text="실행"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="컬렉션 이터레이션"
android:textSize="20dp"
android:textStyle="bold"/>
<EditText
android:id="@+id/edt_ins1"
android:hint="숫자 혹은 문자 입력"
android:layout_width="match_parent"
android:layout_height="50dp"
android:maxLength="1"
android:background="@null"
android:gravity="center"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="6"
android:orientation="horizontal">

<Button
android:id="@+id/btn_for4"
android:text="문자검사"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:textSize="20sp"/>

<Button
android:id="@+id/btn_for5"
android:text="숫자검사"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:textSize="20sp"/>

<Button
android:id="@+id/btn_for6"
android:text="When 사용"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:textSize="20sp"/>

</LinearLayout>

<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/txt_for_log"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="20sp"
android:gravity="center"/>
</ScrollView>
</LinearLayout>





2. 소스

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.kotlin_test.R
import kotlinx.android.synthetic.main.activity_for_in.*
import java.util.*

class ForInActivity : AppCompatActivity() {

var log = ""

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

var increase = true

// Radio Button 리스너
rdo_direction.setOnCheckedChangeListener { radioGroup, i -> if(i == R.id.rbtn_increase) increase = true else increase = false }
// Radio Button 기본값 기정
rdo_direction.check(R.id.rbtn_increase)


// for ---------------------------------------------------------------------------------------------------------------------------
// 피즈버그 게임 실행 버튼
btn_for1.setOnClickListener {
log = ""
txt_for_log.setText("")

if(increase) {
// for 증가 (..)
if(edt_step.text.toString().equals("")) {
for (i in 1..100) {
log += fizzBuzz(i) + "\n"
}
} else {
val num = Integer.parseInt(edt_step.text.toString())

// step : 증가값 지
for (i in 1..100 step num) {
log += fizzBuzz(i) + "\n"
}
}

txt_for_log.setText(log)
} else {
// for 감소 (downTo)
if(edt_step.text.toString().equals("")) {
for (i in 100 downTo 1) {
log += fizzBuzz(i) + "\n"
}
} else {
val num = Integer.parseInt(edt_step.text.toString())

// for 감소 : downTo
for (i in 100 downTo 1 step num) {
log += fizzBuzz(i) + "\n"
}
}

txt_for_log.setText(log)
}
}

// 맵 이터레이션 실행 버튼
btn_for2.setOnClickListener {
log = ""
txt_for_log.setText("")

// 키에 대해 정렬하기 위해 TreeMap 사용
val binaryReps = TreeMap<Char, String>()

// 입력 - 맵에 값 셋팅
for (c in 'A'..'F') {
val binary = Integer.toBinaryString(c.toInt()) // 아스키(ASCII) 코드를 2진 표현으로 바꾼다.
binaryReps[c] = binary // java로 표현하면 binaryReps.put(c, binary)와 같다.
}

// 출력 - 맵 키(letter)와 값(binary)을 두 변수에 각각 대입한다.
for ((letter, binary) in binaryReps) {
log += ("$letter = $binary\n")
}

txt_for_log.setText(log)
}

// 컬렉션 이터레이션 실행 버튼
btn_for3.setOnClickListener {
log = ""
txt_for_log.setText("")

// 입력 - 컬렉션 인덱스(index)와 값(element)을 두 변수에 각각 대입한다.
var list = arrayListOf("10", "11", "1001")
for((index, element) in list.withIndex()) {
log += ("$index = $element\n")
}

txt_for_log.setText(log)
}



// in ---------------------------------------------------------------------------------------------------------------------------
// 문자검사 버튼
btn_for4.setOnClickListener {
log = ""
txt_for_log.setText("")

var str = edt_ins1.text.toString()

if(!str.equals("")) {
var c_arr: CharArray = str.toCharArray()

if (isLetter(c_arr[0])) {
txt_for_log.setText("문자 검사 : true")
} else {
txt_for_log.setText("문자 검사 : false")
}
}
}

// 숫자검사 버튼
btn_for5.setOnClickListener {
log = ""
txt_for_log.setText("")

var str = edt_ins1.text.toString()

if(!str.equals("")) {
var c_arr: CharArray = edt_ins1.text.toString().toCharArray()

if (isDigit(c_arr[0])) {
txt_for_log.setText("숫자 검사 : true")
} else {
txt_for_log.setText("숫자 검사 : false")
}
}
}

// WHEN사용 버튼
btn_for6.setOnClickListener {
log = ""
txt_for_log.setText("")

var str = edt_ins1.text.toString()

if(!str.equals("")) {
var c_arr: CharArray = edt_ins1.text.toString().toCharArray()

txt_for_log.setText(recognize(c_arr[0]))
}
}
}

// 피즈버즈 게임
fun fizzBuzz(i: Int) =
when {
i % 15 == 0 -> "FizzBuzz " // 15로 나눠서 떨어지면 FizzBuzz
i % 3 == 0 -> "Fizz " // 3으로 나눠서 떨어지면 Fizz
i % 5 == 0 -> "Buzz " // 5으로 나눠서 떨어지면 Buzz
else -> "$i "
}

// in을 사용해 값이 범위에 속하는지 검사하기 : 문자
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'

// in을 사용해 값이 범위에 속하는지 검사하기 : 숫자
fun isDigit(c: Char) = c in '0'..'9'

// !in을 사용해 값이 범위에 속하지 않는지 검사하기
fun isNotLetter(c: Char) = c !in 'a'..'z' || c !in 'A'..'Z'

// when에서 in 사용하기
// 문자열에 국한되지 않고 비교가 가능한 클래스(java.lang.Comparable 인터페이스가 구현된 클래스)라면 그 클래스의 인스턴스 객체를 사용해 범위를 만들 수 있다.
fun recognize(c: Char) = when (c) {
in '0'..'9' -> "숫자"
in 'a'..'z', in 'A'..'Z' -> "문자"
else -> "I don't know…​"
}
}


3. 실행 결과




포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 스마트 캐스트(Smart Cast)


▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기

2018/03/15 - [Android_Kotlin] - [Android Kotlin] 문자열 템플릿(String Template) 사용법

2018/06/05 - [Android_Kotlin] - [Android Kotlin] 클래스, 프로퍼티, 커스텀 접근자 - 라디오 버튼(Radio Button)


이번엔 코틀린의 스마트 캐스트에 포스팅하겠습니다.


코틀린에서는 is를 사용해 타입검사를 합니다. 자바의 instanceof와 비슷합니다. 하지만 자바에서는 어떤 변수의 타입을 instanceof로 확인한 다음에는 그 타입에 속한 멤버에 접근하기 위해서는 명시적으로 변수 타입을 캐스팅해야 합니다. 하지만 코틀린에서는 프로그래머 대신 컴파일러가 캐스팅을 해줍니다.


즉, is로 검사하고 나면 굳이 변수를 원하는 타입으로 캐스팅하지 않아도 마치 처음부터 그 변수가 원하는 타입으로 선언된 것 처럼 사용할 수 있습니다. 이런 기능을 스마트 캐스트(Smart Cast)라고 부릅니다. 이제 아래 작성한 소스를 보시면 이해가 더 쉽게 되실 것입니다.




1. 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.kotlin_test.Activity.SmartCastActivity">

<TextView
android:text="산술식 : ( a + b ) + c"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="6">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="첫 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_int_01"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="두 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_int_02"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="세 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_int_03"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
</LinearLayout>
<Button
android:id="@+id/btn_cal1"
android:text="If 연쇄 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>

<Button
android:id="@+id/btn_cal2"
android:text="값을 만들어내는 If 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_cal3"
android:text="When 사용 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_cal4"
android:text="When 블록 사용 계산하기"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"/>
<TextView
android:id="@+id/txt_cal_result"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:gravity="center"/>
<TextView
android:id="@+id/txt_cal_log"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="21sp"
android:gravity="center"/>
</LinearLayout>





2. 소스

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.kotlin_test.R
import kotlinx.android.synthetic.main.activity_smart_cast.*

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

class SmartCastActivity : AppCompatActivity() {
var log: String = ""

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

btn_cal1.setOnClickListener {
txt_cal_log.setText("")

val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = eval1(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("If 연쇄 : " + result.toString())
}

btn_cal2.setOnClickListener {
txt_cal_log.setText("")

val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = eval2(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("If 식 : " + result.toString())
}

btn_cal3.setOnClickListener {
txt_cal_log.setText("")

val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = eval3(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("When 사용 : " + result.toString())
}

btn_cal4.setOnClickListener {
log = ""
val num1: Num = Num(Integer.parseInt(edt_int_01.text.toString()))
val num2: Num = Num(Integer.parseInt(edt_int_02.text.toString()))
val num3: Num = Num(Integer.parseInt(edt_int_03.text.toString()))

val result: Int = evalWithLogging(Sum(Sum(num1, num2), num3))
txt_cal_result.setText("When 블록 사용 : " + result.toString())
txt_cal_log.setText(log)
}
}

// if 연쇄를 사용해 계산
fun eval1(e: Expr): Int {
if (e is Num) {
val n = e as Num // 위에서 이미 is Num으로 인해 스마트 캐스트 되어 as Num하여 타입을 변경할 필요 없다
// 자바에서는 instanceof로 타입을 검사한 후 명시적으로 타입을 변경해줘야 하지만 코틀린에선 is로 타입을 검사 하면 스마트 캐스팅되어 해당 타입으로 사용이 가능하다
return n.value
}
if (e is Sum) {
return eval1(e.right) + eval1(e.left) // 변수 e에 대해 스마트 캐스트를 사용한다
}
throw IllegalArgumentException("Unknown expression")
}

// 리팩토링 1 - 값을 만들어내는 if 식
fun eval2(e: Expr): Int =
if (e is Num) {
e.value
} else if (e is Sum) {
eval2(e.right) + eval2(e.left)
} else {
throw IllegalArgumentException("Unknown expression")
}

// 리팩토링 2 - if 중첩 대신 when 사용하기
fun eval3(e: Expr): Int =
when (e) {
is Num -> e.value
is Sum -> eval3(e.right) + eval3(e.left)
else -> throw IllegalArgumentException("Unknown expression")
}

// 리팩토링 3 - 분기에 복잡한 동작이 들어가 있는 when 사용하기
fun evalWithLogging(e: Expr): Int =
when (e) {
is Num -> {
log += "num: ${e.value}\n"
e.value
}
is Sum -> {
val left = evalWithLogging(e.left)
val right = evalWithLogging(e.right)
log += "sum: $left + $right\n"
left + right
}
else -> throw IllegalArgumentException("Unknown expression")
}
}



3. 실행 결과


포스팅 OS : Mac

검색어 : 자바(Java), 배열(Array), 원(Circle), 피타고라스 정리(Pythagorean Theorem)




배열을 사용해 입력받은 정수에 따라 점점 커지는 원 그리는 방법에 대해 포스팅합니다.

여러가지 방법이 있지만 여기서는 피타고라스 정리를 사용하여 그려보겠습니다.


피타고라스 정리가 무엇인지 모르시는 분들을 위해 링크와 이미지를 올립니다.


피타고라스 정리 <-- 클릭!




자 그럼 바로 소스로 들어갑니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import java.util.Scanner;
 
public class Circle {
    public static void main(String[] args) {
        /* 원 그리기  */
 
        System.out.println("숫자를 입력해주세요.");
        Scanner input = new Scanner(System.in);
        
        int answer;                    // 입력받은 정수
        int heigt;                     // 총 세로 길이
        int width;                     // 총 가로 길이
        int top;                       // 상단
        int radius;                    // 반지름
        
        // 입력받은 정수 저장
        answer = input.nextInt();
        
        // 총 세로 길이 생성
        heigt = answer * 18;
        
        // 총 가로 길이 생성
        width = answer * 40;
        
        // 상단, 하단 종료 위치 지정
        top = heigt / 2;
        
        // 반지름
        radius = heigt / 2 + 1;
        
        
//        System.out.println("입력한 정수 : " + answer);     // 입력받은 정수
//        System.out.println("총 세로 길이 : " + heigt);     // 총 세로 길이
//        System.out.println("총 가로 길이 : " + width);     // 총 가로 길이
//        System.out.println("상단 : " + top);             // 상단
//        System.out.println("반지름 : " + radius);        // 반지름
        
        char[][] star = new char[heigt][width]; // 가공한 정수를 배열 생성에 사용
        
        int start = 0;            // 시작 지점 
        int end = 0;              // 종료 지점
        
        // 원 상단 생성
        for(int a = top; a > 0 ; a--){
            int bLine =(int) Math.round(Math.sqrt(Math.pow(radius - 12- Math.pow(a, 2)));  // 피타고라스 공식
            bLine = (bLine + 1* 2;                                                           // 가로 행의 중앙에서 양쪽으로 점을 찍어야 되는 거리
            
//            System.out.println(a+ "행의 길이는 " + bLine +" 입니다.");
            
            int realRow = Math.abs(a - top);       // 세로 행의 위치
            start = (width / 2- bLine;           // 가로 행의 시작 지점
            end = ((width / 2- 1+ bLine;       // 가로 행의 종료 지점
 
//            System.out.println("시작 : " + start +" / 종료 : " + end);
            
            // 배열에 원 상단 입력
            for(int b = 0 ; b < width ; b++){
                if(start <= b && b < end) {
                    star[realRow][b] = '*';
                }else {
                    star[realRow][b] = ' ';
                }
            }
        }        
        
        // 원 상단 출력
        for(int a = 0 ; a < top ; a++){
            for(int b=0 ; b < width ; b++){
                System.out.print(star[a][b]);
            }
            System.out.println();
        }
        // 원 하단 출력 (상단을 반전해서 출력)
        for(int a = top - 1 ; a >= 0 ; a--){ 
            for(int b=0 ; b < width ; b++){
                System.out.print(star[a][b]);
            }
            System.out.println();
        }
    }
}
cs



주석으로 상세하게 설명을 써놓았으니 한번 쭉 보시고 이해가 안되면 로그 찍어보시면 이해하시는데 도움이 될 것입니다.


위 소스를 실행하면 아래와 같이 원이 그려집니다.





포스팅 OS : Mac

검색어 : 코틀린(Kotlin), enum, when, 스피너(Spinner), 임포트(import)



▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기

2018/03/15 - [Android_Kotlin] - [Android Kotlin] 문자열 템플릿(String Template) 사용법

2018/06/05 - [Android_Kotlin] - [Android Kotlin] 클래스, 프로퍼티, 커스텀 접근자 - 라디오 버튼(Radio Button)





이번에는 enum과 when을 사용하는 방법에 대하여 포스팅하겠습니다. 


enum과 when을 충분히 사용해 볼수 있는 예제로 색을 혼합해 혼합된 색상에 대해 출력해주는 액티비티를 만들겠습니다.


참고로 제가 안드로이드 예제를 통한 학습방법을 고집하는 이유는 단순히 코틀린만을 공부하는게 아니라 코틀린을 안드로이드에 적절히 사용하는 방법도 같이 익히기 위해서 입니다. 제가 작성하는 포스팅을 처음부터 차근차근 보다보면 코틀린을 공부함과 동시에 안드로이드 앱을 코틀린으로 제작하는 방법도 저절로 배우게 되실 거라고 봅니다.



1. Color 클래스

enum class Color (
// 상수의 프로퍼티를 정의
val r: Int, val g: Int, val b: Int
) {
// 각 상수를 생성할 때 그에 대한 프로퍼티 값을 지정
RED(255, 0, 0),
ORANGE(255, 165, 0),
YELLOW(255, 255, 0),
GREEN(0, 255, 0),
BLUE(0, 0, 255),
INDIGO(75, 0, 130),
VIOLET(238, 130, 238); // 마지막엔 반드시 세미콜론(;)을 사용해야 한다.

// enum 클래스 안에서 메소드를 정의
fun rgb() = "R : " + r + " G : " + g + " B : " + b
}



2. 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.kotlin_test.Activity.EnumWhenActivity">

<TextView
android:text="1. 색 정보 보기"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp" />
<TextView
android:id="@+id/txt_color_result"
android:layout_width="match_parent"
android:layout_height="100dp"
android:textSize="20dp"
android:textStyle="bold"
android:gravity="center" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="3">
<Button
android:id="@+id/btn_red"
android:text="Red"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>

<Button
android:id="@+id/btn_orange"
android:text="Orange"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>

<Button
android:id="@+id/btn_yellow"
android:text="Yellow"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="4">
<Button
android:id="@+id/btn_green"
android:text="Green"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>

<Button
android:id="@+id/btn_blue"
android:text="Blue"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>

<Button
android:id="@+id/btn_indigo"
android:text="Indogo"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>

<Button
android:id="@+id/btn_violet"
android:text="Violet"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"/>
</LinearLayout>


<TextView
android:text="2. 혼합할 색상을 골라주세요"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="20dp"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="8"
android:gravity="center">
<TextView
android:text="1번 색"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"/>
<Spinner
android:id="@+id/spn_first"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="3"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="8"
android:gravity="center">
<TextView
android:text="2번 색"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"/>
<Spinner
android:id="@+id/spn_second"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="3"/>
</LinearLayout>
<Button
android:id="@+id/btn_mix"
android:text="Mix"
android:layout_width="match_parent"
android:layout_height="50dp"/>

<TextView
android:id="@+id/txt_mix_result"
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"/>

</LinearLayout>



3. res > values > string.xml String Array 삽입

<string-array name="colorList">
<item>Red</item>
<item>Orange</item>
<item>Yellow</item>
<item>Green</item>
<item>Blue</item>
<item>Indogo</item>
<item>Violet</item>
</string-array>



4. 소스

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Spinner
import com.kotlin_test.R
import com.kotlin_test.model.Color
import com.kotlin_test.model.Color.*
import kotlinx.android.synthetic.main.activity_enum_when.*

class EnumWhenActivity : AppCompatActivity() {

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

// 색 정보 보기 구간 ---------------------------------------------------------------------------
btn_red.setOnClickListener {
txt_color_result.setText(getMsg(Color.RED))
}

btn_orange.setOnClickListener {
txt_color_result.setText(getMsg(Color.ORANGE))
}

btn_yellow.setOnClickListener {
txt_color_result.setText(getMsg(Color.YELLOW))
}

btn_green.setOnClickListener {
txt_color_result.setText(getMsg(Color.GREEN))
}

btn_blue.setOnClickListener {
txt_color_result.setText(getMsg(BLUE))
}

btn_indigo.setOnClickListener {
txt_color_result.setText(getMsg(Color.INDIGO))
}

btn_violet.setOnClickListener {
txt_color_result.setText(getMsg(Color.VIOLET))
}


// 색 혼합 구간 -------------------------------------------------------------------------------
var firstColor : Color? = RED
var secondColor : Color? = RED

// 스피너 셋팅
val spnFirst = findViewById(R.id.spn_first) as Spinner
val spnSecond = findViewById(R.id.spn_second) as Spinner
val adapter = ArrayAdapter.createFromResource(this, R.array.colorList, android.R.layout.simple_spinner_item)

// 첫 번째 스피너
spnFirst.adapter = adapter
spnFirst.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, position: Int, l: Long) {
firstColor = setColor(position)
}

override fun onNothingSelected(adapterView: AdapterView<*>) {

}
}

// 두 번째 스피너
spnSecond.adapter = adapter
spnSecond.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, position: Int, l: Long) {
secondColor = setColor(position)
}

override fun onNothingSelected(adapterView: AdapterView<*>) {

}
}

// Mix 버튼
btn_mix.setOnClickListener {
txt_mix_result.setText("mix : " + mix(firstColor, secondColor) + "\n" +
"mixOptimized : " + mixOptimized(firstColor, secondColor))
}
}

fun getMsg(color : Color) = "색상 코드 : ${color.rgb()}\n" +
"연관 단어1 : ${getMnemonic(color)}\n" +
"연관 단어2 : ${getWarmth(color)}"

// import com.kotlin_test.model.Color.*
// 짧은 이름으로 사용하기 위해 enum 상수를 모두 임포트 하면 아래와 같이 RED, ORANGE 등으로 사용 가능합니다
fun setColor(positon: Int) =
when (positon) {
0 -> RED
1 -> ORANGE
2 -> YELLOW
3 -> GREEN
4 -> BLUE
5 -> INDIGO
6 -> VIOLET
else -> null
}

// when을 사용해 올바른 enum 값 찾기
fun getMnemonic(color: Color) =
when (color) {
RED -> "Richard"
ORANGE -> "Of"
YELLOW -> "York"
GREEN -> "Gave"
BLUE -> "Battle"
INDIGO -> "In"
VIOLET -> "Vain"
}

// 한 when 분기 안에 여러 값 사용하기
// 콤마로 구분한다.
fun getWarmth(color: Color) =
when(color) {
RED, ORANGE, YELLOW -> "warm"
GREEN -> "neutral"
BLUE, INDIGO, VIOLET -> "cold"
}

// when의 분기 조건에 여러 다른 객체 사용하기
// 비효율 적인 코드 - 여러 set 인스턴스를 생성하기 떄문에
fun mix(c1: Color?, c2: Color?) =
when (setOf(c1, c2)) {
setOf(RED, YELLOW) -> "ORANGE"
setOf(YELLOW, BLUE) -> "GREEN"
setOf(BLUE, VIOLET) -> "INDIGO"
else -> "Dirty color"
}

// 인자가 없는 when
// when에 인자가 없으면 각 분기의 조건이 Boolean 결과를 계산하는 식이어야 한다.
// 위 mix 함수보다 불필요한 객체 생성을 막을 수 있어서 효율적이다. 비록 코드는 약간 읽기 어려워지지만 성능 향상이 가능하다
fun mixOptimized(c1: Color?, c2: Color?) =
when {
(c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> "ORANGE"
(c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) -> "GREEN"
(c1 == BLUE && c2 == VIOLET) || (c1 == VIOLET && c2 == BLUE) -> "INDIGO"
else -> "Dirty color"
}
}



5. 실행 결과



포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 클래스(Class), 프로퍼티(Property), 커스텀 접근자, 라디오 버튼(Radio Button)



▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기

2018/03/15 - [Android_Kotlin] - [Android Kotlin] 문자열 템플릿(String Template) 사용법




2달만에 다시 글을 쓰게 되네요. 제가 투입된 프로젝트가 바빠져서 한동안 블로그엔 신경을 못썼습니다. 이제 좀 상황이 정리되어 다시 열심히 달려보려 합니다! 





오늘 포스팅할 주제는 코틀린의 클래스와 프로퍼티 입니다. 역시 제 포스팅 스타일대로 안드로이드 예제로 설명하려 합니다. 



우선 액티비티 하나를 만들어서 두 개의 예제로 진행하려 합니다. 

예제에 대해 간단히 설명드리면,

첫 번째 예제는 Person 이라는 객체를 생성하여 이름과 결혼 여부를 저장하고 저장된 값을 텍스트 뷰에 출력해줍니다.

두 번째 예제는 Rectangle 이라는 객체를 생성하여 가로 세로의 길이를 입력하여 저장하고 커스텀 접근자를 사용하여 정사각형 여부를 텍스트 뷰에 출력해줍니다.


늘 그렇듯 아래 소스에 주석으로 설명을 달아 놓았으니 소스 보시고 직접 실행해보시면서 이해하시면 간단합니다.

이전 포스팅에서 다뤘던 내용은 주석으로 달지 않고 최대한 소스에 적용해서 작성했습니다. 이해가 안가시는 부분은 이전 포스팅에서 찾아서 보시길 바랍니다.



1. Person 클래스

class Person(
/*
프로퍼티 생성 - 필드(변수), 접근자(Getter, Setter)를 한데 묶어 프로퍼티라 합니다.
자바와는 다르게 코틀린에서 프로퍼티 생성 시 Getter, Setter등의 접근자를 따로 생성해줄 필요가 없습니다.

ex) 코틀린으로 생성된 Person을 자바로 생성했을 경우 소스 예제입니다.
public class Person {
private final String name;
private boolean isMarried;

public Person(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setMarried(boolean married) {
isMarried = married;
}

public boolean isMarried() {
return isMarried;
}
}

위 처럼 긴 소스를 코틀린은 아래와 현재 이 클래스와 같이 간단하게 생성이 가능합니다.
*/

// val - 읽기 전용 프로퍼티
// var - 읽기, 쓰기 전용 프로퍼티
val name: String,
var isMarried : Boolean = false
)




2. Rectangle 클래스

class Rectangle (val height : Int, val width : Int) {
// 커스텀 접근자 생성
val isSquare : Boolean
/* // 블록을 본문으로 사용하는 방법
get() {
return height == width
}
*/
// 식을 본문으로 사용하는 방법
get() = height == width
}



3. 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context="com.kotlin_test.Activity.PropertyActivity">

<TextView
android:text="1. Person"
android:layout_width="match_parent"
android:layout_height="40dp"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#0054FF"
android:gravity="center"/>
<EditText
android:id="@+id/edt_person_nm"
android:hint="이름을 입력하세요"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:gravity="center"
android:background="@null"/>

<RadioGroup
android:id="@+id/rdo_married"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<RadioButton
android:id="@+id/rbtn_married_false"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="미혼"
android:paddingRight="20dp"/>
<RadioButton
android:id="@+id/rbtn_married_true"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="기혼" />
</RadioGroup>

<Button
android:id="@+id/btn_person_save"
android:text="저장"
android:layout_width="match_parent"
android:layout_height="50dp" />

<Button
android:id="@+id/btn_person_print"
android:text="출력"
android:layout_width="match_parent"
android:layout_height="50dp" />

<TextView
android:id="@+id/txt_person_result"
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:gravity="center"/>


<TextView
android:text="2. Rectangle"
android:layout_width="match_parent"
android:layout_height="40dp"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#0054FF"
android:gravity="center"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="4">
<EditText
android:id="@+id/edt_width"
android:hint="가로 길이"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:background="@null"
android:inputType="number"
android:gravity="center"/>
<EditText
android:id="@+id/edt_height"
android:hint="세로 길이"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="2"
android:background="@null"
android:inputType="number"
android:gravity="center"/>
</LinearLayout>

<Button
android:id="@+id/btn_rect_save"
android:text="저장"
android:layout_width="match_parent"
android:layout_height="50dp" />

<Button
android:id="@+id/btn_rect_print"
android:text="정사각형 여부 판독"
android:layout_width="match_parent"
android:layout_height="50dp" />

<TextView
android:id="@+id/txt_rect_result"
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="20sp"
android:gravity="center"/>

</LinearLayout>



4. 액티비티 소스

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.kotlin_test.R
import com.kotlintest.model.Person
import com.kotlintest.model.Rectangle
import kotlinx.android.synthetic.main.activity_property.*

class PropertyActivity : AppCompatActivity() {

private var person : Person? = null
private var isMarried : Boolean = false

private var rect : Rectangle? = null

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

// ---------------
// 1. Person
// ---------------
// 결혼 유무 선택
rdo_married.setOnCheckedChangeListener { radioGroup, i -> if(i == R.id.rbtn_married_true) isMarried = true else isMarried = false}

// 저장
btn_person_save.setOnClickListener {
person = Person(edt_person_nm.text.toString(), isMarried)
txt_person_result.setText("저장 완료!")
}

// 출력
btn_person_print.setOnClickListener {
val stringMarried : String = if(person != null) person?.isMarried.toString() else "false"
val marriedYn : Boolean

if ( stringMarried.equals("true"))
marriedYn = true
else
marriedYn = false

txt_person_result.setText("이름 : ${person?.name}\n결혼유무 : ${if(marriedYn) "기혼" else "미혼" }")
}

// ---------------
// 2. Rectangle
// ---------------
// 저장
btn_rect_save.setOnClickListener {
rect = Rectangle(Integer.parseInt(edt_height.text.toString()), Integer.parseInt(edt_width.text.toString()))
txt_rect_result.setText("저장 완료!")
}

// 정사각형 여부 판독
btn_rect_print.setOnClickListener {
val stringSquare : String = if(rect != null) rect?.isSquare.toString() else "false"
val isSquare : Boolean
if(stringSquare.equals("true")) isSquare = true else isSquare = false

if(isSquare) txt_rect_result.setText("정사각형") else txt_rect_result.setText("직사각형")
}
}
}



5. 실행 결과





이번 포스팅을 직접 테스트해보시면 코틀린에서 자바대비 얼마나 소스의 양을 줄일 수 있을지 가늠하실 수 있을 것 같네요. 



포스팅 OS : Mac

검색어 : 안드로이드(Android), 자바(Java), 라이브러리(Library), 모듈(Module), 생성(Create), 변환(Convert), 참조(reference)

 

 

오늘 포스팅 할 내용은 빌드 가능한 라이브러리 모듈 생성 방법 입니다. 여기서 '빌드 가능한' 이라는 뜻은 안드로이드 스튜디오(Android Studio)로 오픈(Open) 했을 때 빌드가 가능하다는 것을 의미합니다. 이 말이 무엇을 뜻하는 것인지는 안드로이드 스튜디오로 라이브러리를 제작해보신 분들이라면 쉽게 이해하셨겠지만 모르는 분들을 위해 간략하게 설명을 하도록 하겠습니다.

 

안드로이드 스튜디오에서 라이브러리를 생성할 때 기본적으로 앱 프로젝트를 생성 후 그 앱에 라이브러리를 추가하는 방식으로 밖에 생성하지 못합니다. 물론 제가 아는 한도내에서는 말이죠. 바로 라이브러리를 생성할 수 있는 선택 메뉴가 없습니다. 

 

물론 전자의 방법으로 라이브러리를 추가해서 개발을 진행해도 되지만 Git과 같은 저장소에 업로드 할 때 라이브러리 폴더만 덩그라니 올리게 되면 그 라이브러리가 제대로 작동하는지 확인하기 위해서는 앱 프로젝트를 생성하여 라이브러리를 삽입하거나 애초에 Git에 업로드 할 때 앱 프로젝트까지 같이 올리는 수 밖에 없게됩니다. 

 

이러한 점이 불편해서 문득 라이브러리만 빌드가 가능하다면 좋겠다라는 생각이 들어서 이 포스팅을 계획하게 되었습니다. 제 의도가 글로 충분히 전해졌는지 모르겠네요. 그럼 시작하겠습니다.

 

 

1. 라이브러리 모듈 생성을 위한 앱 프로젝트 생성

 

위에서 그렇게 앱 프로젝트에 의존적이지 않은 라이브러리 모듈을 생성하겠다고 떠들었는데 앱 프로젝트를 생성한다고 하니 의아하신 분들도 있을 겁니다. 엄밀히 말하면 이 포스팅은 앱 프로젝트를 라이브러리 모듈로 변환(Convert)하는 포스팅이라 보시면 됩니다. 애초에 안드로이드 스튜디오에서 라이브러리 모듈을 바로 생성하는 것은 지원하지 않으니 약간의 변칙을 사용하는 것입니다.

 

아래 그림의 순서를 따라 액티비티가 없는 앱 프로젝트를 생성하세요.

 

1) New Project

 

 

 

2) Minimum SDK 지정

 

 

 

3) No Activity 옵션 선택 후 Finish 버튼을 눌러 프로젝트 생성

 

 

 

4) 프로젝트를 생성한 후에는 파인더로 해당 프로젝트의 폴더로 이동합니다. 그리고 아래 그림처럼 app 폴더 앞에서 libs, src, build.gradle 파일을 선택하여 상위 폴더(프로젝트 폴더)로 옮겨주세요. 그리고 app 폴더는 삭제합니다. 

 

- 이 과정을 하는 이유는 나중에 지금 만든 라이브러리를 다른 앱에 넣을 경우 module 명을 app이 아닌 라이브러리 이름(이 포스팅에선 module 이겠죠?)으로 적용하게 하기 위해서 입니다.

 

 

 

 

 

 

 

 

5) 방금 app 폴더에서 상위 폴더로 꺼낸 src 폴더에서 레이아웃이 들어 있는 res폴더를 마찬가지로 프로젝트 폴더로 꺼내주세요. 

 

- 이 부분은 개인 취향입니다. 폴더 위치를 자기 맘대로 정하는 것이 해도 그만 안해도 그만 입니다만 나중에 gradle에서 sourceSet으로 path를 정해줄때 제대로 적용만 하면 됩니다.

 

 

 

 

 

 

6) 윗 단계까지 폴더정리가 끝났으면 이제 gradle에서 변경된 폴더의 위치를 잡아줄 차례입니다. 아래 그림처럼 일단 settings.gradle에서 app으로 지정된 부분을 삭제합니다. 

 

- 변경 전

 

 

 

 

 

 

 

 

 

- 변경 후

 

 

 

 

 

 

7) 이번에는 build.gradle에서 아래 그림과 같이 변경해주시고 sync 해주시면 빌드가 됩니다. 

- 필수 변경 사항

-> apply plugin : 'com.adroid.application' -> 'com.adroid.library' 변경

-> applicationId 삭제

 

(글씨가 작아서 안보이시면 사진 클릭하시면 크게 보입니다.)

- 변경 전 

 

 

- 변경 후

 

 

 

- Java 버전

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}

apply plugin: 'com.android.library'

android {
    compileSdkVersion 26
    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            res.srcDirs = ['res']
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    repositories {
        google()
        jcenter()
    }
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

- Kotlin 버전

buildscript {
    ext.kotlin_version = '1.3.20'
    repositories {
        google()
        jcenter()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'com.android.library'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            res.srcDirs = ['res']
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

 

 

 

8) 빌드가 된 것을 확인 했으면 라이브러리 테스트용 클래스를 하나 만들어 주세요. 아래 그림과 똑같이 만드셔도 되고 본인이 직접 알아서 만드셔도 됩니다.

 

 

 

 

 

 

-----------------------------------------------------------------

 

자 여기까지 완료하셨으면 빌드 가능한 라이브러리 모듈을 만드는데 성공하셨습니다. 

 

여기까지 오셨는데도 왜 굳이 이렇게 힘들게 빌드 가능한 라이브러리 모듈을 만들어야 하는가에 대해 궁금증이 있으신 분이 있으신가요? 

생각해보시면 간단합니다. 라이브러리 모듈만 따로 만들고 싶고 그 라이브러리 모듈이 에러가 있는지 없는지 자체적으로 확인을 하고 싶을때 이 방법을 사용하시면 됩니다. 

 

그리고 가장 중요한 이유로 라이브러리 모듈 프로젝트를 따로 관리하면서 다른 앱 프로젝트에서 라이브러리를 복사해서 넣는게 아닌 참조하여 관리하고 싶을때 사용하시면 굉장히 편리합니다. 

 

 

 

 

 

 

2. 앱 프로젝트에 생성한 라이브러리 모듈 참조하기 

 

그럼 이제 만든 라이브러리를 앱에 넣어서 사용해 볼까요?

참고로 라이브러리를 넣는 방법으로 import module 하시면 해당 라이브러리 폴더를 그대로 복사하여 앱 프로젝트에 넣습니다. 그럴 경우 이렇게 힘들게 라이브러리를 만들 필요가 없습니다. 그냥 new module로 앱 프로젝트에 의존적인 라이브리를 만드시면 됩니다. 그래서 이번엔 참조하여 라이브러를 넣는 방법을 사용하겠습니다. 

 

참조한다는 것은 sourceSet의 개념과 비슷합니다. 라이브러리를 넣은 앱 프로젝트에서 라이브러리를 수정하면 실제 따로 관리되는 라이브러리 모듈에서도 똑같이 적용됩니다. 바로 이것이 이렇게 힘들게 라이브러리 모듈을 만든 궁극적인 이유입니다. 

 

라이브러리를 제작하게 되면 바로 테스트 가능한 앱도 필요하기 때문에 앱에 의존적으로 라이브러리를 생성하도록 해놓은 것 같지만 Git같은 저장소에 업로드 할때는 테스트앱을 제외하고 라이브러리만 올려야 관리가 간편하기 때문에 이런 방법을 생각해 내었습니다. 올리되 빌드가 가능하면 더욱 관리가 편해질 것 같아서요.

 

 

1) 앱 프로젝트 생성

 

 

 

 

 

 

2) settings.gradle에 라이브러리 모듈 추가

 

 

 

 

 

 

 

 

3) 라이브러리가 목록에 추가된 것 확인

 

 

 

 

 

4) build.gradle에서 라이브러리 compile 

 

- 라이브러리 참조 후 compile 과정을 거쳐야 실제로 사용이 가능합니다.

 

 

 

 

 

5) 테스트 액티비티 제작

 

 

 

 

 

6) 실행 확인

 

 

 

 

 

 

 

 

 

 

 

 

 

 

늘 이야기 하지만 제 포스팅은 어느정도 안드로이드 개발이 가능한 분들을 대상으로 하기에 기초적인 설명은 최대한 배제하고 있습니다. 혹시라도 이해가 안되거나 설명이 부족하다고 느끼신다면 공부를 좀 더 하고 보실 것을 권장합니다.

 

누군가 가르쳐 주는 것은 자신의 실력을 업그레이드 하는데 큰 도움이 되지 않습니다. 스스로 해결하려고 노력하는 것이 더 중요하죠. 어려움이 닥치면 물어보기 보단 먼저 스스로 해결해보려고 노력하는 습관을 갖는 것이 중요합니다.

 

 

 

 

 

 

 

 

 

 

 

포스팅 OS : Mac

검색어 : 자바(Java), 배열(Array), 별(Star)




오늘은 자바 기초 공부를 위해 배열을 사용해 입력받은 정수에 따라 점점 커지는 별 그리는 방법에 대해 포스팅합니다.


요즘 일이 바빠서 블로그에 관심을 가지지 못했습니다 ㅠ

그래도 짬짬히 올려보도록 하겠습니다.


그럼 소스를 보도록 하죠.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import java.util.Scanner;
 
public class Star {
 
    public static void main(String[] args) {
        /* 별 그리기 - 다리 5개 */
        
        System.out.println("숫자를 입력해주세요.");
        Scanner input = new Scanner(System.in);
        
        int answer;                    // 입력받은 정수
        int heigt;                     // 별 세로 길이
        int width;                     // 별 가로 길이
        int center;                    // 가로 중간 지점
        int top;                       // 머리 종료 지점
        int middle;                    // 몸통 종료 지점
        int bottom;                    // 다리 종료 지점
        
        // 입력받은 정수 저장
        answer = input.nextInt();
        
        // 별 세로 길이 생성
        heigt = answer * 9;
        
        // 별 가로 길이 생성
        width = answer * 15;
        
        // 가로 길이가 짝수일 경우 + 1
        if(width % 2 == 0) {
            width += 1;
        }
        
        // 가로 중간 지점 생성
        center = width / 2;
        
        // 머리,몸통,다리 종료 위치 지정
        top = heigt / 3;
        middle = top * 2;
        bottom = top * 3;
        
        /*
        System.out.println("입력한 정수 = " + answer);    // 입력받은 정수
        System.out.println("총 세로 길이 = " + heigt);    // 별 세로 길이
        System.out.println("총 가로 길이 = " + width);    // 별 가로 길이
        System.out.println("가로 중간 지점 = " + center);  // 가로 중간 지점
        System.out.println("상단 = " + top);             // 상단
        System.out.println("중간 = " + middle);          // 중간
        System.out.println("하단 = " + bottom);          // 하단
        */
        
        char[][] star = new char[heigt][width]; // 별의 세로, 가로 길이로 배열 생성
        
        int start;       // 시작 지점 
        int end;         // 종료 지점
        int startBottom; // 하단 시작 지점
        int endBottom;   // 하단 종료 지점
        
        // 별 머리 생성
        start = center;
        end = center;
        for(int a = 0 ; a < top ; a++){
            for(int b = 0 ; b < width ; b++){
                if(start <= b && b <= end) {
                    star[a][b] = '*';
                }else {
                    star[a][b] = ' ';
                }
            }
            
            start--;
            end++;
        }
        
        // 별 몸통 생성
        start = 0;
        end = width - 1;
        for(int a = top ; a < middle ; a++){
            for(int b = 0 ; b < width ; b++){
                if(start <= b && b <= end) {
                    star[a][b] = '*';
                }else {
                    star[a][b] = ' ';
                }
            }
            
            start++;
            end--;
        }        
 
        // 별 다리 생성
        startBottom = center;
        endBottom = center;
        for(int a = middle ; a < bottom ; a++){
            for(int b = 0 ; b < width ; b++){
                if(startBottom <= b && b <= endBottom) {
                    star[a][b] = ' ';
                }else {
                    star[a][b] = '*';
                }
                
                if(start -1 > b || end +1 < b) {
                    star[a][b] = ' ';
                }
            }        
            
            start--;
            end++;
            startBottom -= 3;
            endBottom += 3;    
        }
        
        // 별 배열 출력
        for(int a=0 ; a < heigt ; a++){
            for(int b=0 ; b < width ; b++){
                System.out.print(star[a][b]);
            }
            System.out.println();
        }
    }
}
cs



오늘도 역시 설명은 주석으로 대체합니다.

저는 별을 머리, 몸통, 다리로 구분하여 그리는 방법을 사용했습니다. 제가 만든 별이 완벽한 별의 모양일까? 라는 의문이 들긴 하는데 좀 더 정확한 별을 그리고 싶으시다면 좀더 세세하게 셋팅하면 될 것 같네요.


위 소스를 실행하면 아래와 같이 별이 그려집니다.





다음엔 원 그리기 포스팅을 하도록 하겠습니다.


포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 문자열 템플릿(String Template)


▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 함수와 변수를 이용하여 에디트텍스트(EditText)에 입력한 값 중 큰 값 구하기




이번에는 문자열 템플릿에 대해 포스팅하겠습니다. 간단한 기능이므로 포스팅도 매우 간략하게 쓰겠습니다.



문자열 템플릿은 사실 이전 포스팅에서 사용한 적이 있습니다.

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린

바로 위 포스팅에서 몰래(?) 사용했었죠. 




사용한 소스 코드를 한번 보시죠.

btn_print_02.setOnClickListener {
// 변경 불가능한 변수 - val
// 자바로 말하면 final
val nameStr : String = "Kotlin_02"
txtHellow.setText("Hellow $nameStr")
}

위 소스는 버튼 클릭시 'Hellow Kotlin_02' 이라는 메시지를 출력하는 소스입니다. 




txtHellow.setText("Hellow $nameStr")

바로 여기가 문자열 템플릿을 사용한 부분이죠. 이렇게 '$' 문자를 사용해서 nameStr 변수를 따옴표(" ")안에서 바로 사용할 수 있습니다. 자바에서는 없던 기능이죠. 

자바에서는 "Hellow" + nameStr 이렇게 사용했던 것이 '$' 문자 하나로 간단하게 사용이 가능해졌습니다. 




문자열 템플릿에서 사용할 수 있는 대상은 변수 이름만으로 한정되지 않습니다. 복잡한 식도 중괄호({ })로 둘러싸서 문자열 템플릿 안에 넣을 수 있습니다.

btn_print_02.setOnClickListener {
// 변경 불가능한 변수 - val
// 자바로 말하면 final
val nameStr : String = "Kotlin_02"
txtHellow.setText("Hellow ${if(nameStr.equals("")) "Kotlin" else nameStr}")
}

이런식으로 ${ } 중괄호 안에 식을 넣어 사용이 가능합니다. 




대체로 $를 그냥 사용하는 것 보다는 ${ } 처럼 중괄호를 써서 사용하는 습관을 들이는 것을 추천합니다. 그 이유는 문자와 변수명의 구분이 확실하고 코드를 읽을 때도 문자열 템플릿 안에서 변수가 쓰인 부분을 더 쉽게 식별이 가능하기 때문입니다.


문자와 변수명 구분이 확실하다는 의미는 아래 소스를 보시면 이해가 쉽습니다.

"$nameStr님 안녕하세요."

위와 같이 사용하면 에러가 납니다. 이유는 간단합니다. 코틀린에서는 자바와 마찬가지로 한글(한글 뿐 아니라 '글자(letter)'로 분류할 수 있는 모든 유니코드 문자)을 식별자에 사용할 수 있어서 변수 이름에 한글이 들어갈 수 있습니다. 위 소스에서 실제로 변수명은 'nameStr'이지만 컴파일러는 'nameStr님'으로 인식해서 변수가 없다고 에러가 나는 겁니다.

"${nameStr}님 안녕하세요."

이렇게 사용하면 간단하게 해결 됩니다. 보기에도 변수라는게 인식하기 쉽지 않나요?



문자열 템플릿은 자바에서 없던 기능인 만큼 유용하게 활용이 가능할 것으로 보입니다. 자바에서 " " + " "  남발해서 보기에도 안좋고 보기도 힘든 상황을 많이 겪어보신 분들이라면 쌍수를 들고 환영할만한 기능임에는 분명합니다.

포스팅 OS : Mac

검색어 : 코틀린(Kotlin), 함수(Function), 변수(Variable), 에디트 텍스트(Edit Text)


▶︎ 이전 포스팅 목록

2018/02/10 - [Android_Kotlin] - [Android Kotlin] 안드로이드 스튜디오(Andorid Studio)에 코틀린(Kotlin) 개발환경 셋팅

2018/03/14 - [Android_Kotlin] - [Android Kotlin] 텍스트뷰(TextView)와 버튼(Button)을 사용한 헬로 코틀린



이번에는 코틀린의 함수와 변수에 대해 간단하게 알아보도록 할텐데 에디트텍스트에 입력한 두 개의 정수 중 큰 값을 출력하는 액티비티를 만들어 보겠습니다.


이번에도 역시 설명은 주석으로 보시면 되겠습니다. 사실 주석으로 설명을 달아 놓는 이유가 지금 포스팅 하는 소스들을 하나의 공부용 앱으로 만들어서 Git에 업로드 할 예정이라 소스만 보고도 이해가 가능해야 해서 이런 방식을 채택하게 되었습니다.



1. 레이아웃


<LinearLayout 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"
android:orientation="vertical"
tools:context="com.kotlin_test.Activity.FunctionActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:weightSum="4">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="첫 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_01"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="두 번째 :"
android:gravity="center"/>
<EditText
android:id="@+id/edt_02"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:inputType="number"/>

</LinearLayout>
<Button
android:id="@+id/btn_result1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Max 1"/>
<Button
android:id="@+id/btn_result2"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Max 2"/>
<Button
android:id="@+id/btn_result3"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="Max 3"/>

<TextView
android:id="@+id/txt_result"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center"
android:textSize="20sp"/>

</LinearLayout>





2. 소스


import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import com.kotlin_test.R
import kotlinx.android.synthetic.main.activity_function.*

class FunctionActivity : AppCompatActivity() {

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

// var : 변경 가능한 변수 선언
var maxNum : String

// MAX 1 버튼
btn_result1.setOnClickListener {

// val : 변경 불가능한 변수 선언
val num01 : Int = Integer.parseInt(edt_01.text.toString())

/*
타입 추론 기능에 의해 num01 변수와는 다르게 타입 지정을 생략할 수 있다. (: Int 생략 가능)
단, 변수 생성과 동시에 값을 초기화 해줄 경우에만 생략이 가능하다.
변수 초기화를 나중에 진행할 경우 아래와 같이 반드시 타입을 지정해 주어야 한다
val num02 : Int
*/
val num02 = Integer.parseInt(edt_02.text.toString())

maxNum = max1(num01, num02).toString()

txt_result.text = maxNum
}

// MAX 2 버튼
btn_result2.setOnClickListener {

val num01 : Int = Integer.parseInt(edt_01.text.toString())
val num02 = Integer.parseInt(edt_02.text.toString())

maxNum = max2(num01, num02).toString()

txt_result.text = maxNum
}

// MAX 3 버튼
btn_result3.setOnClickListener {

val num01 : Int = Integer.parseInt(edt_01.text.toString())
val num02 = Integer.parseInt(edt_02.text.toString())

maxNum = max3(num01, num02).toString()

txt_result.text = maxNum
}
}

// 블록이 본문인 함수
fun max1(a: Int, b : Int) : Int {
return if (a > b) a else b
}

// 식이 본문인 함수
fun max2(a : Int, b : Int) : Int = if (a > b) a else b

/*
식이 본문인 함수 간략화 (: Int 반환 타입 지정 생략)
식이 본문일 경우 굳이 사용자가 반환 타입을 적지 않아도 컴파일러가 함수 본문 식을 분석해서
식의 결과 타입을 함수 반환 타입으로 정해준다
이렇게 컴파일러가 프로그래머 대신 타입을 정해주는 기능을 <타입 추론>이라 부른다.
*/
fun max3(a: Int, b : Int) = if (a > b) a else b
}



3. 실행 결과





+ Recent posts