전 컴포즈 게시물에서는 간단하게 비만도 계산기를 만들어 봤다.
이번 게시물에서는 다음 그림과 같이 스톱워치를 만들어보면서 콜백함수와 컴포즈로 뷰 작업 하는 것에 익숙해져야겠다.
시작을 하면 초와 밀리초가 나오면서, 랩타임을 클릭했을 때 위와 같이 시간이 기록되고, 정지를 눌렀을 때 모든 기록된 시간이 초기화된다.
먼저 ViewModel로 기능먼저 구현해보자.
private var time = 0
private var timerTask : Timer? = null
//현재 진행되고 있는지
private val _isRunning = mutableStateOf(false)
val isRunning : State<Boolean> = _isRunning
private val _sec = mutableStateOf(0)
val sec : State<Int> = _sec
private val _milli = mutableStateOf(0)
val milli : State<Int> = _milli
private val _lapTimes = mutableStateOf(mutableListOf<String>())
val lapTimes : State<List<String>> = _lapTimes
private var lap = 1
먼저, 시간을 계산하기 위한 time과 실행시키는 timer변수를 생성하고,
현재 정지된 상태인지, 실행되고 있는 상태인지를 알기 위한 isRunning 변수를 생성한다.
또한, 기록할 수 있는 lapTimes는 계속 기록될 수 있으니 리스트 형태로 생성하였다.
fun start(){
_isRunning.value = true
timerTask = timer(period = 10) {
time++
_sec.value = time / 100
_milli.value = time % 100
}
}
이제 start 버튼을 누르면 isRunning상태를 바꾸어주고 타이머를 시작하여 초와 밀리초를 계산한다.
fun pause(){
_isRunning.value = false
timerTask?.cancel()
}
fun reset(){
//모두 초기화
timerTask?.cancel()
time = 0
_isRunning.value = false
_sec.value = 0
_milli.value = 0
_lapTimes.value.clear()
lap = 1
}
fun recordLapTime(){
_lapTimes.value.add(0, "$lap Lap : ${sec.value}.${milli.value}")
lap++
}
일시 정지 버튼을 클릭했을 때 isRunning상태를 다시 false로 바꾸어주고, 타이머를 정지한다.
초기화 버튼을 클릭했을 때에는 모든 값을 초기값으로 초기화한다.
랩타임을 클릭했을 때에는 리스트 lapTimes에 기록하는데, 이 형태를 lap을 증가시켜주면서 위의 그림과 같이 표현했다.
이제 뷰를 만들어 ViewModel과 연결시켜보자.
@Composable
fun MainScreen(
sec : Int,
milli : Int,
isRunning : Boolean,
lapTimes : List<String>,
onReset : () -> Unit,
onToggle : (Boolean) -> Unit,
onLapTime : () -> Unit
) {
먼저 메인화면에서 받을 정보들을 받아주고, 각 버튼을 클릭했을 땐 콜백함수를 통해 viewModel에 있는 함수를 실행시켜보자.
Scaffold(
topBar = {
TopAppBar(title = { Text("StopWatch")})
}
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
){
Spacer(modifier = Modifier.height(40.dp))
Row(
verticalAlignment = Alignment.Bottom,
){
Text("$sec", fontSize = 100.sp)
Text("$milli")
}
Spacer(modifier = Modifier.height(16.dp))
Column(
modifier = Modifier
.weight(1f) // 나머지 영역을 모두 차지
.verticalScroll(rememberScrollState()),
){
lapTimes.forEach { lapTime ->
Text(lapTime)
}
}
topBar를 통해 앱 바를 설정하고,
Column 안에 Row를 만들어 초 바로 오른쪽 밑에 밀리초가 나오게끔 하였다.
또한, lapTimes는 리스트 형태로 나타내기 때문에 스크롤 할 수 있게 verticalScroll을 통해 나타냈고,
forEach를 통해 리스트를 출력하게끔 하였다.
Row(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
){
//reset 버튼
FloatingActionButton(
onClick = {
onReset()
},
backgroundColor = Color.Red,
) {
Image(
painter = painterResource(id = R.drawable.ic_baseline_refresh_24),
contentDescription = "reset"
)
}
FloatingActionButton(
onClick = {
onToggle(isRunning)
},
backgroundColor = Color.Green,
) {
Image(
painter = painterResource(id = if(isRunning) R.drawable.ic_baseline_pause_24 else R.drawable.ic_baseline_play_arrow_24),
contentDescription = "toggle"
)
}
Button(
onClick = {
onLapTime()
}
){
Text("랩타임")
}
}
버튼은 한 줄에 모두 나타내므로 Row를 통해 구현하였고, 서로 일정하게 떨어져있어야 하므로 SpaceBetween을 사용하였다.
가운데 버튼인 일시정지,시작 버튼은 일시정지와 시작버튼일 때 각각의 기능이 다르므로, onToggle이라는 콜백함수를 만들어,
isRunning의 상태에 따라 각기 다른 viewmodel 함수를 불러오는 형태로 구현하였다.
val viewModel = viewModel<MainViewModel>()
val sec = viewModel.sec.value
val milli = viewModel.milli.value
val isRunning = viewModel.isRunning.value
val lapTimes = viewModel.lapTimes.value
MainScreen(
sec = sec,
milli = milli,
isRunning = isRunning,
lapTimes = lapTimes,
onReset = { viewModel.reset() },
onToggle = { running ->
if(running) {
viewModel.pause()
}else{
viewModel.start()
}
},
onLapTime = { viewModel.recordLapTime() }
)
이렇게 onToggle함수가 불렸을 때 running상태에 따라 달라지고, 리셋버튼, 랩타임버튼을 클릭했을 때에도 마찬가지로
콜백함수로 viewModel에 있는 reset함수, recordLapTime 함수가 불려지게끔 구현하였다!
GitHub - SsongSik/Jetpack_Compose: Creating apps using Jetpack Compose
Creating apps using Jetpack Compose. Contribute to SsongSik/Jetpack_Compose development by creating an account on GitHub.
github.com
'Android > Compose' 카테고리의 다른 글
[Compose]비만도 계산기 (0) | 2022.09.24 |
---|---|
[Compose] ViewModel, State (1) | 2022.09.23 |
[Compose] Navigation (0) | 2022.09.22 |
[Compose] TextField, Scaffold, SnackBar (0) | 2022.09.21 |
[Compose] Image, Card, State (0) | 2022.09.15 |