Kotlin Flow - 안드로이드 타이머 구현
20217 단어 jetpackcomposeflowandroidkotlin
ViewModel의 Kotlin 흐름으로 논리를 구축하고 Jetpack Compose 및 Composable로 타이머를 표시합니다.주 정부는 StateFlow를 대표로 할 것이다.
타이머에 뭐가 있어요?
사용자 인터페이스
타이머의 UI 부분은 CircularProgressIndicator와 카운트다운 수치를 표시하는 텍스트로 표시됩니다.타이머는 클릭할 때만 작동하고, 다른 클릭은 타이머를 리셋합니다.
다음은 사용자 인터페이스 코드입니다.
@Composable
fun TimerDisplay(timerState: TimerState, toggleStartStop: () -> Unit) {
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator(
timerState.progressPercentage,
Modifier.clickable { toggleStartStop() })
Text(timerState.displaySeconds)
}
}
TimerState는 지원 클래스입니다.60초 타이머가 30초 경과할 때CircularProgressIndicator의 진도는 0.5%, 나머지 몇 초 안에 표시되는 텍스트만 포함됩니다.TimerState를 사용하면 남은 초와 총 초를 제공하고 조합할 수 있는 나머지 정보를 계산할 수 있습니다.
TimerState 코드입니다.
data class TimerState(
val secondsRemaining: Int? = null,
val totalSeconds: Int = 60,
val textWhenStopped: String = "-"
) {
val displaySeconds: String =
(secondsRemaining ?: textWhenStopped).toString()
// Show 100% if seconds remaining is null
val progressPercentage: Float =
(secondsRemaining ?: totalSeconds) / totalSeconds.toFloat()
// Always implement toString from Effective Java Item 9
override fun toString(): String = "Seconds Remaining $secondsRemaining, totalSeconds: $totalSeconds, progress: $progressPercentage"
}
너는 그 중의 약간의 미세한 차이를 읽거나 뛰어넘을 수 있다.미세한 차이는 타이머가 멈췄을 때 당신은 무엇을 나타낼 수 있습니까?이 데이터 클래스에 대해 stopped는 int에서 표시하고 나머지 초는null입니다.초수 Maining과 total Seconds만 제공하면 TimerDisplay에 필요한 나머지 정보를 계산할 수 있습니다.논리적 흐름
타이머의 논리를 TimerUseCase라는 클래스에 봉인할 것입니다.
다음은 그것의 작업 원리다.
(totalSeconds downTo 0).asFlow()
총 초에서 0까지 숫자 목록을 효과적으로 만들고 흐르는 형식으로 하나씩 보냅니다.토탈섹스가 5라면 5, 4, 3, 2, 1, 0의 발사를 받을 수 있다.
마지막 코드에서 우리는 그것을 1로 줄일 것이지만, 잠시 후에 우리는 원인을 볼 것이다.
(totalSeconds downTo 0).asFlow()
.onEach { delay(1000) }
즉, 항목이 이 흐름에서 나올 때마다 먼저 1초를 기다린 다음 체인을 따라 계속하도록 한다.이것이 바로 타이머가 똑딱거리는 실현 방식이다.
.transform { remainingSeconds: Int ->
emit(TimeState(remainingSeconds))
}
이것은 아마도 다음일 것이다. 그러나 우리가 이렇게 쓰면 문제가 있을 것이다.여기서 우리가 유일하게 해야 할 일은 시간 상태를 만드는 것이다. 그러나 더 복잡한 작업을 수행하려면 몇 밀리초가 걸릴 수도 있다. 현재 우리는 체인에서 시간을 강제로 이동시킨다.
여기에 예가 하나 있다.다음 발사에 1초가 걸리지만 Time State와 같은 대상을 만드는 데 200ms가 걸리면 다음 항목을 발사하기 전에 1200ms가 지났습니다.만약 이 주기가 타이머에서 여러 번 반복된다면 더 이상 정확하지 않을 것이다.
그래서 우리는 둘 사이에 끼어 있는 것이 필요하다.다음은 'conflate' 를 사용하는 실제 코드입니다. 하나의 단독 라인에서 (동시에) 변환 함수를 실행하는 데 사용되며, 하나의 시간 라인에서 실행되지 않습니다.
또한 코드가 그대로 유지되면, 타이머가 1초 동안 클릭한 후에 똑딱거리는 소리만 볼 수 있다.우리는 그것이 즉시 완전한 시간을 보여 주고 선택을 시작하기를 바란다. 그래서 우리는 두 가지 수정을 했다.
.onStart { emit(totalSeconds) }
그리고 흐름은 사실상 첫 번째 지연치를 낸다.다음 초 그거.이것이 바로 왜 절차가(totalSeconds - 1 downTo 0).asFlow()
다음은 코드입니다. /**
* The timer emits the total seconds immediately.
* Each second after that, it will emit the next value.
*/
fun initTimer(totalSeconds: Int): Flow<TimeState> =
(totalSeconds - 1 downTo 0).asFlow() // Emit total - 1 because the first was emitted onStart
.onEach { delay(1000) } // Each second later emit a number
.onStart { emit(totalSeconds) } // Emit total seconds immediately
.conflate() // In case the creating of State takes some time, conflate keeps the time ticking separately
.transform { remainingSeconds: Int ->
emit(TimeState(remainingSeconds))
}
부가 조건
우리 아직 안 끝났어!
실제로 계수가 완료되면 타이머는 기본값으로 취소되거나 재설정된 문제를 처리할 수 없습니다.이를 위해서는 시작할 때 onCompletion을 설정해야 합니다.
이것은 다음과 같습니다.
private var _timerStateFlow = MutableStateFlow(TimerState())
val timerStateFlow: StateFlow<TimerState> = _timerStateFlow
Timer State를 보낼 수 있는 개인 상태 흐름을 만들고, 조합 가능한 상태로 연결할 수 있는 공공 상태 흐름을 만들어야 합니다. private var job: Job? = null
fun toggleTime(totalSeconds: Int) {
if (job == null) {
job = timerScope.launch {...}
} else {
job?.cancel()
job = null
}
}
ToggleTime 함수를 호출하면 협동 프로그램 작업이 실행되지 않으면 새로운 작업이 시작됩니다.다시 클릭하면 현재 실행 중인 작업이 취소됩니다.
job = timerScope.launch {
initTimer(totalSeconds)
.onCompletion { _timerStateFlow.emit(TimerState()) }
.collect { _timerStateFlow.emit(it) }
}
onCompletion 블록은 "finally"와 같습니다.흐름이 정상적으로 완성되었든지 오류가 있든지 간에 onCompletion 블록을 호출하고 시간 상태를 리셋하여 UI를 리셋할 수 있도록 합니다.마찬가지로collect에서 우리는 받은 Time State를 Timer State Flow에 넣고 UI에서 관찰할 수 있도록 합니다.
모두 여기 있습니다.
class TimerUseCase(private val timerScope: CoroutineScope) {
private var _timerStateFlow = MutableStateFlow(TimerState())
val timerStateFlow: StateFlow<TimerState> = _timerStateFlow
private var job: Job? = null
fun toggleTime(totalSeconds: Int) {
if (job == null) {
job = timerScope.launch {
initTimer(totalSeconds)
.onCompletion { _timerStateFlow.emit(TimerState()) }
.collect { _timerStateFlow.emit(it) }
}
} else {
job?.cancel()
job = null
}
}
/**
* The timer emits the total seconds immediately.
* Each second after that, it will emit the next value.
*/
private fun initTimer(totalSeconds: Int): Flow<TimerState> =
// generateSequence(totalSeconds - 1 ) { it - 1 }.asFlow()
(totalSeconds - 1 downTo 0).asFlow() // Emit total - 1 because the first was emitted onStart
.onEach { delay(1000) } // Each second later emit a number
.onStart { emit(totalSeconds) } // Emit total seconds immediately
.conflate() // In case the operation onTick takes some time, conflate keeps the time ticking separately
.transform { remainingSeconds: Int ->
emit(TimerState(remainingSeconds))
}
}
}
ViewModel
예를 들어 이 모든 작업을 완성하면 ViewModel을 매우 깨끗하게 할 수 있습니다.
class TimerVm : ViewModel() {
private val timerIntent = TimerUseCase(viewModelScope)
val timerStateFlow: StateFlow<TimerState> = timerIntent.timerStateFlow
fun toggleStart() = timerIntent.toggleTime(60)
}
마지막으로 주요 활동에 사용됩니다. 예를 들어 다음과 같습니다.
val vm = viewModel<TimerVm>()
val timerState = vm.timerStateFlow.collectAsState()
TimerDisplay(timerState.value, vm::toggleStart)
너는 지금 아주 좋은 타이머가 하나 생겼다. 타이머는 줄곧 규칙을 지킬 것이다.이 과정에서 당신은 포장, 협동 프로그램과 Jetpack에 대한 지식을 배웠을 것입니다!
저는 안드로이드의 고급 직위를 찾고 있습니다. 이 직위는 고객에게 큰 영향을 미치고 좋은 팀을 가지고 임금도 높습니다.만약 당신이 모집하고 있다면 저에게 알려주세요!컨설팅 계약도 가능하다.
class TimerVm : ViewModel() {
private val timerIntent = TimerUseCase(viewModelScope)
val timerStateFlow: StateFlow<TimerState> = timerIntent.timerStateFlow
fun toggleStart() = timerIntent.toggleTime(60)
}
val vm = viewModel<TimerVm>()
val timerState = vm.timerStateFlow.collectAsState()
TimerDisplay(timerState.value, vm::toggleStart)
Reference
이 문제에 관하여(Kotlin Flow - 안드로이드 타이머 구현), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/aniketsmk/kotlin-flow-implementing-an-android-timer-ieo텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)