들어가며
기존 안드로이드의 UI를 제작하기 위해서는 Xml 파일을 직접 작성하거나, Design 화면에서 열심히 요소를 배치해야했는데요. 현재도 그러한 방법을 많이 사용하고 있지만, 점점 Jetpack Compose로 전환하는 추세를 보이고 있는 것 같습니다.
Kotlin이 정착한 이후 안드로이드 개발이 매우 현대적으로 변화하고 있는 것 같습니다.
프로젝트 생성 화면에서도 많은 변화를 느낄 수 있었습니다. 예전에는 Empty Activity를 생성하면 기본적인 구성에 activty_main.xml 파일이 포함되어 생성되었는데, Jetpack Compose의 사용률이 증가하면서 더 이상 기본으로 생성해주지 않았습니다. Empty Views Activity를 클릭하면 activity_main.xml 파일이 포함되어 생성되긴 합니다.
Jetpack Compose
Kotlin 기반의 선언적 UI 프레임워크 입니다. 사용하면서 느낀 바로는 React와 같이 웹 개발 프레임워크에서 UI를 제작하는 것과 매우 유사한 느낌을 받았습니다. ChatGPT가 생성한 간단한 스톱워치 프로그램을 보면서 어떤 형태를 가지고 있는지 살펴봅시다.
GPT가 생성한 스톱워치 앱의 UI는 다음과 같이 생겼습니다. 이제 단편적이고 일반적인 작업에 한해서는 GPT가 되게 잘 수행하는 것 같습니다.
뷰를 생성하는 방법
Jetpack Compose에서는 함수를 통해 뷰를 생성하는데요, 뷰 역할을 하는 함수를 컴포저블(Composable)이라고 합니다.
@Composable
을 통해 컴포저블 함수로 만들 수 있습니다.
컴포저블 내에서 다른 컴포저블을 사용할 수 있습니다.
소괄호를 통해 속성을 정의하고 중괄호를 통해 다른 컴포저블의 컨테이너 역할을 할 수 있습니다.
@Composable
fun StopwatchApp() {
// 상태 변수 정의
var timeInMillis by remember { mutableStateOf(0L) }
var isRunning by remember { mutableStateOf(false) }
// 코루틴 스코프 생성
val coroutineScope = rememberCoroutineScope()
// UI 구성
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Stopwatch",
fontSize = 32.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = formatTime(timeInMillis),
fontSize = 48.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(onClick = {
if (isRunning) {
isRunning = false
} else {
isRunning = true
coroutineScope.launch {
val startTime = System.currentTimeMillis() - timeInMillis
while (isRunning) {
timeInMillis = System.currentTimeMillis() - startTime
delay(10L) // 10ms 단위로 업데이트
}
}
}
}) {
Text(text = if (isRunning) "Pause" else "Start")
}
Button(onClick = {
isRunning = false
timeInMillis = 0L
}) {
Text(text = "Reset")
}
}
}
}
MainActivty에 뷰를 등록하는 방법
setContent 함수에 Composable 함수를 넘김으로써 메인 화면을 등록할 수 있습니다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
StopwatchApp()
}
}
}
미리보기 기능 사용하기
@Preview
가 선언된 컴포저블 함수는 Split 화면에서 미리 볼 수 있습니다. 여러개도 가능합니다.
https://developer.android.com/develop/ui/compose/tooling/previews
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
StopwatchApp()
}
MVVM 패턴 적용하기
Jetpack Compose를 이용했을 때 MVVM 패턴 또한 쉽게 적용할 수 있었는데요. 다음 시간에 알아보도록 합시다.
GPT가 생성한 StopWatch App 소스 코드
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
StopwatchApp()
}
}
}
@Composable
fun StopwatchApp() {
// 상태 변수 정의
var timeInMillis by remember { mutableStateOf(0L) }
var isRunning by remember { mutableStateOf(false) }
// 코루틴 스코프 생성
val coroutineScope = rememberCoroutineScope()
// UI 구성
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Stopwatch",
fontSize = 32.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = formatTime(timeInMillis),
fontSize = 48.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(onClick = {
if (isRunning) {
isRunning = false
} else {
isRunning = true
coroutineScope.launch {
val startTime = System.currentTimeMillis() - timeInMillis
while (isRunning) {
timeInMillis = System.currentTimeMillis() - startTime
delay(10L) // 10ms 단위로 업데이트
}
}
}
}) {
Text(text = if (isRunning) "Pause" else "Start")
}
Button(onClick = {
isRunning = false
timeInMillis = 0L
}) {
Text(text = "Reset")
}
}
}
}
fun formatTime(timeInMillis: Long): String {
val milliseconds = timeInMillis % 1000 / 10
val seconds = (timeInMillis / 1000) % 60
val minutes = (timeInMillis / 1000) / 60
return "%02d:%02d:%02d".format(minutes, seconds, milliseconds)
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
StopwatchApp()
}
'프로그래밍 > 안드로이드' 카테고리의 다른 글
[안드로이드] DataBinding을 위한 DataHolder 클래스들 (0) | 2024.07.27 |
---|---|
[안드로이드] Jetpack Compose에 MVVM 적용하기 (1) | 2024.07.24 |