[kotlin 입문] Functions 알아보기
fun코틀린에서는 함수 선언을 위해 fun 키워드를 사용함.함수 매개변수는 괄호 안에 작성 ().각 매개변수에는 유형이 있어야 하며, 여러 매개변수는 쉼표로 구분반환 유형은 함수의 괄호 뒤에 콜론':'으로 구분함수의 본문은 중괄호 안에 작성 {}키워드 return는 함수에서 무언가를 종료하거나 반환하는 데 사용fun sum(x: Int, y: Int): Int { return x + y}fun main() { println(sum(1, 2)) // 3} * void 함수와 같이 return 값을 반환하지 않아도 된다.Named arguments간결한 코드의 경우 함수를 호출할 때 매개변수 이름을 포함할 필요가 없음. 그러나 매개변수 이름을 포함하면 코드를 읽기가 더 쉬워짐. 이를 Name..
2024.08.08
[kotlin 입문] range 사용 방법
range란?range는 수학적 의미에서 닫힌 간격을 정의함. 범위에 포함된 두 끝점(endpoint) 값으로 정의된다.range는 비교 가능한 유형에 대해서 정의된다.in, !in 연산자의 형태로 사용된다.클래스의 range를 만들려면 범위 시작 값에서 rangeTo()를 호출하고 마지막 값을 인수로 제공한다. rangeTo()는 종종 ".." 연산자 형식으로 호출된다.".." 연산자를 사용한 범위fun main() { // 0~10까지 시작, 끝을 포함하는 범위 정의 val myRange : IntRange = 0..10 // 커스텀 범위를 반복하는 for문 // for (i in myRange) println("i : $i") // for문 안에서 범위 정의 가능 for ..
2024.08.07
[kotlin 입문] Control Flow(조건문 if, when)
코틀린의 Control Flow에는 조건문과 반복문으로 나누어 진다.*조건문에는 if와 when이 존재한다. 이 중에서 코틀린은 when을 사용하는 것을 추천한다. if다른 언어들과 동일한 사용 방식.val d: Intval check = trueif (check) { d = 1} else { d = 2}println(d)// 1* (check) 안에 들어갈 조건문의 결과가 항상 참 또는 거짓 이여야 함. 코틀린은 다른 언어와 다르게 삼항연산자가 없다대신 val a = 1val b = 2println(if (a > b) a else b) // Returns a value: 2이런 식으로 표현이 가능하다. When- if와 달리 조건문에 식이 아닌 조건을 검사할 값이 들어간다.- 여러 개의 분기..
2024.08.07
[kotlin 입문] collection 알아보기(Sets, Maps)
SetsSet은 고유한 원소를 저장함.순서를 저장하지는 않는다(정렬 방식은 일반적으로 정해져있지 않음.)1. Immutable Setscontains(element: E)주어진 요소가 집합에 포함되어 있는지 확인containsAll(elements: Collection)주어진 모든 요소가 집합에 포함되어 있는지 여부를 반환isEmpty()집합이 비어 있는지 여부를 확인size집합의 크기를 반환 //immutable set 선언val set = setOf(1, 2, 3)// sizeval size = set.size//집합이 비어있는지 확인val empty = set.isEmpty()//주어진 요소가 집합에 포함되어 있는지 확인val containsElement = set.contains(1)//주어진 모..
2024.07.31
no image
[kotlin 입문] collection 알아보기
프로그래밍할 때, 나중에 처리할 수 있도록 데이터를 구조로 그룹화하는게 더 유용함.->코틀린에서는 collection 기능을 제공 Collection코틀린은 기본 collection 타입으로 sets, lists, maps를 제공.*각 collection 타입은 read-only / mutable 인터페이스가 구현된 한 쌍으로 존재.값을 수정하기 위해 mutableList를 var로 선언하지 않아도 된다.List원소들을 정렬하거나 인덱스를 활용하여 저장 가능.(순서가 있다.)array와 비슷하지만 크기가 가변 가능하다는 차이점이 있다.(array는 자바와 마찬가지로 크기 고정)사용할 수 있는 함수(위에서 말했듯이 가변, 불변에 대한 인터페이스가 각각 존재) 1. immutable list(불변 list)..
2024.07.30
[kotlin 입문] 변수 알아보기(Int, String, 타입 추론)
Int / Int?코틀린의 Int는 null을 허용하지 않는다.null을 허용하기 위해서는 타입에 ? 붙여서 선언하여야 한다.var i: Int = 10var j: Int? = 10//i = null //Int는 null을 허용하지 않음j = null //Int? 는 null을 허용 String / String?Int와 마찬가지로 String은 null을 허용하지 않음.null을 허용하기 위해서는 타입에 ? 붙여서 선언하여야 한다.var i: String = "ABC"var j: String? = "ABC"//i = null //String는 null을 허용하지 않음j = null //String? 는 null을 허용 타입 추론코틀린은 타입 추론으로 변수에 들어오는 값을 보고 타입을 알아서 지정해준다.(..
2024.07.30
[kotlin 입문] 변수 알아보기(var, val, const val)
코틀린에서 변수는 2가지로 나뉘어 진다. //가변 타입 변수var 변수명: 타입//읽기 전용 변수(불변 타입)val 변수명: 타입 val값을 초기화만 가능하고, 변경은 불가능(변경 시, compile 에러 발생)*java에서 final과 같음.컴파일러가 코드 문맥을 확인시 val 변수가 한번만 초기화 되는것이 맞다면 개발자가 상황에 따라 val를 여러 값으로 초기화(할당) 할 수 있음.변수의 참조가 가리키는 객체의 내부 값은 변경이 가능. //코드 문맥에 따라 val 변수가 1번만 초기화 되는것이 맞다면 코드 상으로는 여러 값을 초기화 가능(예시 처럼 조건문 사용시,) val name: String if (isSuccess()){ name = "Anroid" }else{ nam..
2024.07.30
no image
연습: Compose 기본사항
알게된 내용 정리 Task Manager 연습 문제.  package com.example.composetestimport android.annotation.SuppressLintimport android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport androidx.compose.foundation.Imageimport androidx.compose.foundation.layout.Arrangementimport androidx.compose.foundation.layout.Columnimport ..
2024.06.05

fun

  • 코틀린에서는 함수 선언을 위해 fun 키워드를 사용함.
  • 함수 매개변수는 괄호 안에 작성 ().
  • 각 매개변수에는 유형이 있어야 하며, 여러 매개변수는 쉼표로 구분
  • 반환 유형은 함수의 괄호 뒤에 콜론':'으로 구분
  • 함수의 본문은 중괄호 안에 작성 {}
  • 키워드 return는 함수에서 무언가를 종료하거나 반환하는 데 사용
fun sum(x: Int, y: Int): Int {
    return x + y
}

fun main() {
    println(sum(1, 2))
    // 3
}

 

* void 함수와 같이 return 값을 반환하지 않아도 된다.

Named arguments

간결한 코드의 경우 함수를 호출할 때 매개변수 이름을 포함할 필요가 없음. 그러나 매개변수 이름을 포함하면 코드를 읽기가 더 쉬워짐. 이를 Named arguments 사용이라고 함 .

fun printMessageWithPrefix(message: String, prefix: String) {
    println("[$prefix] $message")
}

fun main() {
    // Uses named arguments with swapped parameter order
    printMessageWithPrefix(prefix = "Log", message = "Hello")
    // [Log] Hello
}

 

Default parameter values

  • 함수 매개변수에 대한 기본값을 정의할 수 있다.
  • 기본값이 있는 매개변수는 함수를 호출할 때 생략할 수 있다.
  • 기본값을 선언하려면 =유형 뒤에 할당 연산자를 사용
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
    println("[$prefix] $message")
}

fun main() {
    // Function called with both parameters
    printMessageWithPrefix("Hello", "Log") 
    // [Log] Hello
    
    // Function called only with message parameter
    printMessageWithPrefix("Hello")        
    // [Info] Hello
    
    printMessageWithPrefix(prefix = "Log", message = "Hello")
    // [Log] Hello
}

 

Functions without return

  • 함수가 유용한 값을 반환하지 않으면 반환 유형은 Unit 이다. 
  • Unit는 하나의 값만 있는 유형이다
  • Unit 는 함수 본문에서 명시적으로 반환되는 것을 선언할 필요가 없다
    *즉, 키워드를 사용하거나 반환 유형을 선언할 필요가 없다 (return.)
fun printMessage(message: String) {
    println(message)
    // `return Unit` or `return` is optional
}

fun main() {
    printMessage("Hello")
    // Hello
}

 

 

range란?

  • range는 수학적 의미에서 닫힌 간격을 정의함. 범위에 포함된 두 끝점(endpoint) 값으로 정의된다.
  • range는 비교 가능한 유형에 대해서 정의된다.
  • in, !in 연산자의 형태로 사용된다.
  • 클래스의 range를 만들려면 범위 시작 값에서 rangeTo()를 호출하고 마지막 값을 인수로 제공한다. rangeTo()는 종종 ".." 연산자 형식으로 호출된다.

".." 연산자를 사용한 범위

fun main() {
    // 0~10까지 시작, 끝을 포함하는 범위 정의
    val myRange : IntRange = 0..10 // 커스텀 범위를 반복하는 for문 //    for (i in myRange) println("i : $i")

    // for문 안에서 범위 정의 가능
    for (i in 0..10) println("for문으로 range 반복 : $i")
}

 

downTo 를 사용한 역순 범위

val range = 10 downTo 1 // 10부터 1까지의 역순 범위
for (i in range) {
    println(i)
}

 

until 을 사용한 범위(끝 값 포함 안)

val range = 1 until 10 // 1부터 9까지의 범위
for (i in range) {
    println(i)
}

 

Step을 사용한 범위

val range = 1..10 step 2 // 1부터 10까지 2씩 증가하는 범위
for (i in range) {
    println(i)
}

 

문자 범위

val charRange = 'a'..'f' // 'a'부터 'f'까지의 범위
for (c in charRange) {
    println(c)
}

 

 

범위에 값이 포함되는지 확인 가능

val range = 1..10
println(5 in range) // true
println(15 in range) // false

 

 

코틀린의 Control Flow에는 조건문반복문으로 나누어 진다.

*조건문에는 if와 when이 존재한다. 이 중에서 코틀린은 when을 사용하는 것을 추천한다.

 

if

다른 언어들과 동일한 사용 방식.

val d: Int
val check = true

if (check) {
    d = 1
} else {
    d = 2
}

println(d)
// 1

* (check) 안에 들어갈 조건문의 결과가 항상 또는 거짓 이여야 함.

 

코틀린은 다른 언어와 다르게 삼항연산자가 없다

대신 

val a = 1
val b = 2

println(if (a > b) a else b) // Returns a value: 2

이런 식으로 표현이 가능하다.

 

When

- if와 달리 조건문에 식이 아닌 조건을 검사할 값이 들어간다.

- 여러 개의 분기 조건문을 지원(switch case문과 비슷)한다.

val obj = "Hello"

when (obj) {
    // Checks whether obj equals to "1"
    "1" -> println("One")
    // Checks whether obj equals to "Hello"
    "Hello" -> println("Greeting")
    // Default statement
    else -> println("Unknown")     
}
// Greeting

 

When을 추천하는 이유

  • 가독성이 좋다.
  • 여러 가지 분기 처리를 통해 명확한 표현이 가능하다.
  • 표현식으로 사용이 가능하다.
val result = when (value) {
    1 -> "One"
    2 -> "Two"
    else -> "Other"
}
  • 다양한 조건 처리가 가능하다.(값의 범위, 타입 검사 등)
val result = when {
    value in 1..10 -> "Between 1 and 10"
    value is String -> "It's a String"
    else -> "Other"
}
  • 타입 검사와 스마트 캐스팅 -> 안전성과 간결성을 높임
fun describe(obj: Any): String =
    when (obj) {
        is Int -> "An integer: $obj"
        is String -> "A string of length ${obj.length}"
        is List<*> -> "A list of length ${obj.size}"
        else -> "Unknown"
    }
  • 기본값 설정이 가능하다.(switch case의 default 처럼)

Sets

  • Set은 고유한 원소를 저장함.
  • 순서를 저장하지는 않는다(정렬 방식은 일반적으로 정해져있지 않음.)

1. Immutable Sets

contains(element: E) 주어진 요소가 집합에 포함되어 있는지 확인
containsAll(elements: Collection<E>) 주어진 모든 요소가 집합에 포함되어 있는지 여부를 반환
isEmpty() 집합이 비어 있는지 여부를 확인
size 집합의 크기를 반환

 

//immutable set 선언
val set = setOf(1, 2, 3)

// size
val size = set.size

//집합이 비어있는지 확인
val empty = set.isEmpty()

//주어진 요소가 집합에 포함되어 있는지 확인
val containsElement = set.contains(1)

//주어진 모든 요소가 집합에 포함되어 있는지
val containsAllElements = set.containsAll(listOf(1, 2))

 

2.Mutable Sets

add(element: E) 집합에 요소를 추가
remove(element: E) 집합에서 주어진 요소를 제거
clear() 집합의 모든 요소를 제거
addAll(elements: Collection<E>) 주어진 모든 요소를 집합에 추가
removeAll(elements: Collection<E>) 주어진 모든 요소를 집합에서 제거
retainAll(elements: Collection<E>) 주어진 요소만을 유지하고 나머지는 집합에서 제거

 

//mutable set 선언
val mutableSet = mutableSetOf(1, 2, 3)

//추가
mutableSet.add(4)

//제거
mutableSet.remove(2)

//모두 제거
mutableSet.clear()

//모두 추가
mutableSet.addAll(listOf(5, 6))

//주어진 모든 요소 제거
mutableSet.removeAll(listOf(1, 3))

//주어진 요소만을 유지하고 나머지는 집합에서 제거
mutableSet.retainAll(listOf(4, 5))

 

Maps

  • key, value 쌍으로 저장하는 방식.
  • key 값은 고유하지만, 서로 다른 key가 같은 value 값을 가질 수는 있음

Immutable Map

size 맵의 크기를 반환
isEmpty() 맵이 비어있는지 여부를 반환
containsKey(key: K) 주어진 키가 맵에 포함되어 있는지 여부를 반환
containsValue(value: V) 주어진 값이 맵에 포함되어 있는지 여부를 반환
get(key: K) 주어진 키에 해당하는 값을 반환
*키 값이 없으면 null을 반환
keys 맵의 모든 키를 반환
values 맵의 모든 값을 반환
entries 맵의 모든 키-값 쌍을 반환

 

//immutable map 생성
val map = mapOf(1 to "one", 2 to "two", 3 to "three")

//size
val size = map.size

//비어있는지 확인
val empty = map.isEmpty()

//주어진 키가 맵에 포함되어 있는지
val containsKey = map.containsKey(1)

//주어진 값이 맵에 포함되어 있는지
val containsValue = map.containsValue("one")

//주어진 키에 해당하는 값
val value = map.get(1)

// 맵의 모든 키, 값
val values = map.values
val keys = map.keys

for (entry in map.entries) {
    println("${entry.key} -> ${entry.value}")
}

 

Mutable map

 

put(key: K, value: V) 맵에 키-값 쌍을 추가
remove(key: K) 주어진 키에 해당하는 키-값 쌍을 제거
clear() 맵의 모든 요소를 제거
putAll(from: Map<out K, V>) 주어진 맵의 모든 요소를 현재 맵에 추가

 

//Mutable map 생성
val mutableMap = mutableMapOf(1 to "one", 2 to "two", 3 to "three")

//맵에 키-값 쌍을 추가
mutableMap.put(4, "four")
// or
mutableMap[4] = "four"

//주어진 키에 해당하는 키-값 쌍을 제거
mutableMap.remove(2)

//맵에 모든 요소 제거
mutableMap.clear()

//주어진 맵의 모든 요소를 현재 맵에 추가
mutableMap.putAll(mapOf(5 to "five", 6 to "six"))

프로그래밍할 때, 나중에 처리할 수 있도록 데이터를 구조로 그룹화하는게 더 유용함.

->코틀린에서는 collection 기능을 제공

 

Collection

  • 코틀린은 기본 collection 타입으로 sets, lists, maps를 제공.
    *각 collection 타입은 read-only / mutable 인터페이스가 구현된 한 쌍으로 존재.
  • 값을 수정하기 위해 mutableList를 var로 선언하지 않아도 된다.
    코틀린 collection 인터페이스 다이어그램

List

  • 원소들을 정렬하거나 인덱스를 활용하여 저장 가능.(순서가 있다.)
  • array와 비슷하지만 크기가 가변 가능하다는 차이점이 있다.(array는 자바와 마찬가지로 크기 고정)

사용할 수 있는 함수(위에서 말했듯이 가변, 불변에 대한 인터페이스가 각각 존재)

 

1. immutable list(불변 list)

size list의 크기 반환
isEmpty() list가 비어있는지 여부를 반환
contains(element: E) 주어진 요소가 리스트에 포함되어 있는지 여부 반환
get(index: Int) 주어진 인덱스의 요소를 반환
indexOf(찾을 요ㄹ) 주어진 요소의 인덱스를 반환. 요소가 없으면 -1을 반환
lastIndexOf(찾을 요소) 주어진 요소의 마지막 인덱스를 반환. 요소가 없으면 -1을 반환
subList(fromIndex: Int, toIndxe: Int) 지정된 범위에 해당하는 하위 리스트를 반환.
forEach(action: (E) -> Unit) 리스트의 각 요소에 대해 주어진 동작을 수행합니다.
map(transform: (E) -> R) 리스트의 각 요소에 변환 함수를 적용한 결과로 새로운 리스트를 반환
filter(predicate: (E) -> Boolean) 주어진 조건을 만족하는 요소들로 이루어진 새로운 리스트를 반환

 

val immutableList: List<Int> = listOf(1, 2, 3, 4, 5)
println(immutableList.size) // 5
println(immutableList.contains(3)) // true
println(immutableList.get(2)) // 3
println(immutableList.indexOf(4)) // 3

 

2. Mutable List(가변 list)

add(element: E) 리스트에 요소를 추가
add(index: Int, element: E) 지정된 인덱스에 요소를 추가
addAll(elements: Collection<E>) 컬렉션의 모든 요소를 리스트에 추가
addAll(index: Int, elements: Collection<E>) 지정된 인덱스에 컬렉션의 모든 요소를 추가
remove(element: E) 주어진 요소를 리스트에서 제거
removeAt(Index: Int) 주어진 인덱스의 요소를 제거
removeAll(elements: Collection<E>) 주어진 컬렉션에 포함된 모든 요소를 리스트에서 제거
clear() 리스트의 모든 요소를 제거
set(index: Int, element: E) 지정된 인덱스의 요소를 주어진 요소로 교체
val mutableList: MutableList<Int> = mutableListOf(1, 2, 3, 4, 5)
mutableList.add(6)
println(mutableList) // [1, 2, 3, 4, 5, 6]

mutableList.add(1, 9)
println(mutableList) // [1, 9, 2, 3, 4, 5, 6]

mutableList.remove(3)
println(mutableList) // [1, 9, 2, 4, 5, 6]

mutableList.set(0, 7)
println(mutableList) // [7, 9, 2, 4, 5, 6]

Int / Int?

  • 코틀린의 Int는 null을 허용하지 않는다.
  • null을 허용하기 위해서는 타입에 ? 붙여서 선언하여야 한다.
var i: Int = 10
var j: Int? = 10

//i = null //Int는 null을 허용하지 않음
j = null //Int? 는 null을 허용

 

String / String?

  • Int와 마찬가지로 String은 null을 허용하지 않음.
  • null을 허용하기 위해서는 타입에 ? 붙여서 선언하여야 한다.
var i: String = "ABC"
var j: String? = "ABC"

//i = null //String는 null을 허용하지 않음
j = null //String? 는 null을 허용

 

타입 추론

  • 코틀린은 타입 추론으로 변수에 들어오는 값을 보고 타입을 알아서 지정해준다.(코틀린 자체 기능)
    *가독성 증가, 코드가 더 간결해짐.
val x = 10           // Int 타입으로 추론
val y = 3.14         // Double 타입으로 추론
val name = "Kotlin"  // String 타입으로 추론
val numbers = listOf(1, 2, 3)  // List<Int> 타입으로 추론

코틀린에서 변수는 2가지로 나뉘어 진다.

 

//가변 타입 변수
var 변수명: 타입

//읽기 전용 변수(불변 타입)
val 변수명: 타입

 

val

  • 값을 초기화만 가능하고, 변경은 불가능(변경 시, compile 에러 발생)
    *java에서 final과 같음.
  • 컴파일러가 코드 문맥을 확인시 val 변수가 한번만 초기화 되는것이 맞다면 개발자가 상황에 따라 val를 여러 값으로 초기화(할당) 할 수 있음.
  • 변수의 참조가 가리키는 객체의 내부 값은 변경이 가능.
 //코드 문맥에 따라 val 변수가 1번만 초기화 되는것이 맞다면 코드 상으로는 여러 값을 초기화 가능(예시 처럼 조건문 사용시,)
  val name: String 
  
  if (isSuccess()){ 
    name = "Anroid" 
  }else{ 
    name = "iOS"
  }
  println("name : $name")
  
  //변수의 참조가 가리키는 객체의 내부 값은 변경이 가능
  //ArrayList 는 MutableList 인터페이스를 상속받은 구현체임
  val nameArray = arrayListOf("Kotlin")
  nameArray.add("Java")
  println("nameArray : $nameArray")
  
  //더 직관적인 예시
  //mutableListOf는 가변 객체
  val numbers = mutableListOf(1, 2, 3, 4)
  numbers.add(5)  // 리스트에 5를 추가할 수 있음
  numbers[0] = 0  // 첫 번째 요소를 0으로 변경할 수 있음
  println(numbers)  // 출력: [0, 2, 3, 4, 5]
  
  //ListOf는 불변 객체
  val numbers = listOf(1, 2, 3, 4)
  // numbers.add(5)  // 오류: Unresolved reference: add
  // numbers[0] = 0  // 오류: Val cannot be reassigned
  println(numbers)  // 출력: [1, 2, 3, 4]

 

var

  • 값을 초기화한 후, 변경 가능(가변 변수)
    *다른 타입의 값을 넣을 수는 없음.(형변환은 가능)
//초기화 후 값 변경 가능
  var name1 = "kotlin" 
  name1 = "java"
  println("name1 : $name1")
  
  //초기화 시 타입을 지정 했다면 다른 타입의 값을 넣는 것은 불가능
  var name2 = "kotlin" 
  name2 = 77 //Type mismatch 에러 발생

 

const

  • val와 같이 불변 타입 선언을 위함.

val 와 const의 차이점

  • const val는 컴파일 시점에 값을 할당하지만, val은 런타임 시점에 값을 할당함. 
    *즉 val은 불완전한 불변성을 가지고 있음.
  • val은 java에서 final. const val은 java에서 static final을 의미.
  • val은 불완전한 불변성을 가지고 있기에, const val 사용을 추천.
// val 키워드를 통해 상수를 선언
object Constants {
    const val NAME = "Rin"
}

fun testValWithoutConst() {
    val name = Constants.NAME
}

 

컴파일러는 컴파일 시점에 이미 NAME 상수가 어떤 값을 가지고 있는지 알 수 있다. 때문에 값으로 대체가 가능하다.

즉 NAME이 "Rin"으로 인라인 되면서, 값을 참조할 때마다 상수에 접근하면서 발생하는 오버헤드를 줄일 수 있다.

 

알게된 내용 정리

 

Task Manager 연습 문제.

 

 

package com.example.composetest

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.composetest.ui.theme.ComposeTestTheme

class TaskManager : ComponentActivity() {
    @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            ComposeTestTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) {
                    GreetingTaskImage(
                        first = "test",
                        second = "test",
                        modifier = Modifier.fillMaxSize()
                    )
                }
            }
        }
    }
}

@Composable
fun GreetingTaskText(first: String, second: String, modifier: Modifier = Modifier){
    Column (
        modifier = modifier,
        //horizontalAlignment = Alignment.CenterHorizontally
    ){
        Text(
            text = first,
            modifier = Modifier.padding(top = 24.dp, bottom = 8.dp),
            fontWeight = FontWeight.Bold
        )
        Text(
            text = second,
            //fontSize = ,
            fontSize = 16.sp,
        )
    }
}

@Composable
fun GreetingTaskImage(first: String, second: String, modifier: Modifier = Modifier){
    val image = painterResource(id = R.drawable.ic_task_completed)
    Column ( modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally){
        Image(
            painter = image,
            contentDescription = null,
        )
        GreetingTaskText(
            first = first,
            second = second,
            modifier = Modifier
        )
    }
}

@Preview(showBackground = true)
@Composable
fun GreetingTaskPreview() {
    ComposeTestTheme {
        GreetingTaskImage(first = "All tasks completed test",
            second = "Nice work!"
        )
    }
}

 

문제점 : 

 

이 사진과 같이 text 역시 가운데 정렬이 되어야 함.

 

하지만 위에서 작성한 소스로 테스트 시,

 

 

column 블럭으로 설정한 GreetingTaskText 컴포저블 블록만 가운데 정렬이 되고, 내용 text는 가운데 정렬이 되지 않는 문제가 발생.

 

물론 이 경우에는, 주석 달아둔 부분을 풀면 text 역시 가운데 정렬이 되지만, 

 

package com.example.composetest

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.composetest.ui.theme.ComposeTestTheme

class TaskManager : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTestTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    TaskCompletedScreen()
                }
            }
        }
    }
}

@Composable
fun TaskCompletedScreen() {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val image = painterResource(R.drawable.ic_task_completed)
        Image(painter = image, contentDescription = null)
        Text(
            text = "All tasks completed",
            modifier = Modifier.padding(top = 24.dp, bottom = 8.dp),
            fontWeight = FontWeight.Bold
        )
        Text(
            text = "Nice work!",
            fontSize = 16.sp
        )
    }
}

@Preview(showBackground = true)
@Composable
fun TaskCompletedPreview() {
    ComposeTestTheme {
        TaskCompletedScreen()
    }
}

 

위 소스처럼 하나의 컴포저블로 관리해서 전체 내용을 가운데 정렬하는 것이 깔끔한 것 같다.(물론 위의 예제의 경우에서만.)

예제 내용이 전체 가운데 정렬이기에 밑에 2번 째 소스가 깔끔해 보이지만, 각 컴포저블 별로 정렬 방법이 다르다면, 첫 번째 소스처럼 내용별로 나누는 것이 더 효율적일 것 같다.

'안드로이드' 카테고리의 다른 글

Android 앱에 이미지 추가  (0) 2024.06.05
안드로이드 스튜디오 ch1. Android 앱 만들기  (0) 2024.06.04