쏭식
Ssongcode;
쏭식
전체 방문자
오늘
어제
  • 분류 전체보기 (106)
    • JAVA (21)
      • Basic (21)
    • Kotlin (14)
      • Basic (14)
    • Android (64)
      • Basic (24)
      • JETPACK (30)
      • Compose (8)
      • 파고들기 (2)
    • Project (4)
    • etc (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • ViewModel
  • 상속
  • 리사이클러뷰
  • 프로젝트회고
  • workmanager
  • mvvm
  • Adapter
  • Jetpack
  • 컴포즈
  • Android
  • 배열
  • 코테
  • 알고리즘
  • AAC
  • 메소드
  • livedata
  • 코틀린코테
  • 자료구조
  • DataBinding
  • 기초100제
  • 코틀린
  • 객체지향
  • 백준
  • Room
  • Kotlin
  • compose
  • 코드업100제
  • 코딩테스트
  • 자바
  • 변수

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
쏭식

Ssongcode;

[Compose]스톱워치
Android/Compose

[Compose]스톱워치

2022. 9. 29. 23:59

전 컴포즈 게시물에서는 간단하게 비만도 계산기를 만들어 봤다.

이번 게시물에서는 다음 그림과 같이 스톱워치를 만들어보면서 콜백함수와 컴포즈로 뷰 작업 하는 것에 익숙해져야겠다.

결과화면

시작을 하면 초와 밀리초가 나오면서, 랩타임을 클릭했을 때 위와 같이 시간이 기록되고, 정지를 눌렀을 때 모든 기록된 시간이 초기화된다.

먼저 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
    'Android/Compose' 카테고리의 다른 글
    • [Compose]비만도 계산기
    • [Compose] ViewModel, State
    • [Compose] Navigation
    • [Compose] TextField, Scaffold, SnackBar
    쏭식
    쏭식

    티스토리툴바