当前位置: 首页 > 知识库问答 >
问题:

在kotlin中启动定时器序列

司空坚
2023-03-14

我正试图创建一个tabata计时器。我设法从editText中获取用户输入,并启动一个计时器,它表示准备时间。

当准备时间结束后,我想开始工作时间,然后是Rest时间。稍后,当用户输入时,我需要将Worktime和Resttime重复x次。但我想不通。

MainActivity.kt:

        btn_Start_Timer.setOnClickListener() {
            val prepTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
            val workTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
            val restTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
            val numberOfRepetitions = Integer.parseInt(eT_Number_Repetitions.text.toString().trim());

            val Timer = object : CountDownTimer(prepTimeMillis, 1000) {

                override fun onTick(millisUntilFinished: Long) {
                    tV_Total_Duration.setText("Preparation 00:00: " + millisUntilFinished / 1000)
                }

                override fun onFinish() {
                    tV_Total_Duration.setText("Preparation done!")
                }
            }

            timer.start()
        }
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <ScrollView
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:fillViewport="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/tV_Workout_Name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Name of the Workout"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tV_Total_Repetitions" />

            <EditText
                android:id="@+id/eT_WorkoutName"
                android:layout_width="290dp"
                android:layout_height="40dp"
                android:layout_marginTop="16dp"
                android:ems="10"
                android:inputType="textPersonName"
                android:text="Name"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.495"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tV_Workout_Name" />

            <TextView
                android:id="@+id/tV_Prepare"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Preparation"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/eT_WorkoutName" />

            <EditText
                android:id="@+id/eT_PrepTime"
                android:layout_width="290dp"
                android:layout_height="40dp"
                android:layout_marginTop="16dp"
                android:ems="10"
                android:inputType="number"
                android:text="0"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tV_Prepare" />

            <Button
                android:id="@+id/btn_Decrement_PrepTime"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="-"
                app:layout_constraintEnd_toStartOf="@+id/eT_PrepTime"
                app:layout_constraintTop_toBottomOf="@+id/eT_WorkoutName" />

            <Button
                android:id="@+id/btn_Increment_PrepTime"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="+"
                app:layout_constraintStart_toEndOf="@+id/eT_PrepTime"
                app:layout_constraintTop_toBottomOf="@+id/eT_WorkoutName" />

            <TextView
                android:id="@+id/tV_WorkTime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Working"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/eT_PrepTime" />

            <EditText
                android:id="@+id/eT_Work_Time"
                android:layout_width="290dp"
                android:layout_height="40dp"
                android:layout_marginTop="16dp"
                android:ems="10"
                android:text="0"
                android:inputType="number"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tV_WorkTime" />

            <Button
                android:id="@+id/btn_Decrement_WorkTime"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="-"
                app:layout_constraintEnd_toStartOf="@+id/eT_Work_Time"
                app:layout_constraintTop_toBottomOf="@+id/btn_Decrement_PrepTime" />

            <Button
                android:id="@+id/btn_Increment_WorkTime"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="+"
                app:layout_constraintStart_toEndOf="@+id/eT_Work_Time"
                app:layout_constraintTop_toBottomOf="@+id/btn_Increment_PrepTime" />

            <TextView
                android:id="@+id/tv_Repetitions"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Number of Repetitions"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/eT_Rest_Time" />

            <Button
                android:id="@+id/btn_Decrement_Repetitions"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="-"
                app:layout_constraintEnd_toStartOf="@+id/eT_Number_Repetitions"
                app:layout_constraintTop_toBottomOf="@+id/btn_Decrement_Rest" />

            <Button
                android:id="@+id/btn_Increment_Repetitions"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="+"
                app:layout_constraintStart_toEndOf="@+id/eT_Number_Repetitions"
                app:layout_constraintTop_toBottomOf="@+id/btn_Increment_Rest" />

            <EditText
                android:id="@+id/eT_Number_Repetitions"
                android:layout_width="290dp"
                android:layout_height="40dp"
                android:layout_marginTop="16dp"
                android:ems="10"
                android:inputType="number"
                android:text="1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.495"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_Repetitions" />

            <TextView
                android:id="@+id/tV_Rest_Time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Resting"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/eT_Work_Time" />

            <Button
                android:id="@+id/btn_Decrement_Rest"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="-"
                app:layout_constraintEnd_toStartOf="@+id/eT_Rest_Time"
                app:layout_constraintTop_toBottomOf="@+id/btn_Decrement_WorkTime" />

            <EditText
                android:id="@+id/eT_Rest_Time"
                android:layout_width="290dp"
                android:layout_height="40dp"
                android:layout_marginTop="16dp"
                android:ems="10"
                android:text="0"
                android:inputType="number"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.495"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tV_Rest_Time" />

            <Button
                android:id="@+id/btn_Increment_Rest"
                android:layout_width="50dp"
                android:layout_height="40dp"
                android:layout_marginTop="56dp"
                android:text="+"
                app:layout_constraintStart_toEndOf="@+id/eT_Rest_Time"
                app:layout_constraintTop_toBottomOf="@+id/btn_Increment_WorkTime" />

            <TextView
                android:id="@+id/tV_Total_Duration"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Duration 00:00:00"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tV_Total_Repetitions"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:editable="false"
                android:text="Repeated for x times"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tV_Total_Duration" />

            <Button
                android:id="@+id/btn_Start_Timer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="52dp"
                android:layout_marginEnd="110dp"
                android:layout_marginBottom="52dp"
                android:text="Start now"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/eT_Number_Repetitions" />

            <Button
                android:id="@+id/btn_Stop_Timer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="110dp"
                android:layout_marginTop="52dp"
                android:layout_marginBottom="52dp"
                android:text="STOP"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/eT_Number_Repetitions" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

我设法导入了图书馆。

但我现在得到以下错误:

只能从coroutine或其他挂起函数调用挂起函数“count down”

未传递参数“handler”的值

代码:

        btn_Start_Timer.setOnClickListener() {
            val prepTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
            val workTimeMillis = Integer.parseInt(eT_Work_Time.text.toString().trim()) * 1000L;
            val restTimeMillis = Integer.parseInt(eT_Rest_Time.text.toString().trim()) * 1000L;
            val numberOfRepetitions = Integer.parseInt(eT_Number_Repetitions.text.toString().trim());

            countdownJob = lifecycleScope.launch {
                repeat(numberOfRepetitions) {
                    countDown(prepTimeMillis, 1000L) { millisUntilFinished ->
                        tV_Total_Duration.setText("Preparation 00:00: " + millisUntilFinished / 1000)
                    }
                    countDown(workTimeMillis, 1000L) { millisUntilFinished ->
                        tV_Total_Duration.setText("Work 00:00: " + millisUntilFinished / 1000)
                    }
                    countDown(restTimeMillis, 1000L) { millisUntilFinished ->
                        tV_Total_Duration.setText("Rest 00:00: " + millisUntilFinished / 1000)
                    }
                }
            }

        }

        btn_Stop_Timer.setOnClickListener(){

        }
    }

    suspend inline fun countDown(millisInFuture: Long, countDownInterval: Long, crossinline onTick: (Long) -> Unit) = withContext(Dispatchers.Main)
    {
        suspendCancellableCoroutine<Unit>
        { continuation ->
            val timer = object: CountDownTimer(millisInFuture, countDownInterval)
            {
                override fun onTick(millisUntilFinished: Long) = onTick(millisUntilFinished)
                override fun onFinish() = continuation.resume(Unit)
            }.start()

            continuation.invokeOnCancellation()
            {
                timer.cancel()
            }
        }
    }

编辑:build.gradle(module.app):

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.example.instafollow"
        minSdkVersion 24
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-livedata:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-runtime:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:2.4.0-rc01")
    annotationProcessor("androidx.lifecycle:lifecycle-compiler:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-common-java8:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-service:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-process:2.4.0-rc01")
    implementation("androidx.lifecycle:lifecycle-reactivestreams:2.4.0-rc01")
    testImplementation("androidx.arch.core:core-testing:2.1.0")

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.72'
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.6.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

共有1个答案

云俊名
2023-03-14

使用现有类的方法是在第一个类的onfinish()中嵌套另一个倒计时器,并使用嵌套的倒计时器来倒数工作时间。然后在剩下的时间里,您将在该计时器的onfinish()中嵌套另一个计时器。但是,因为你想重复整个过程,你必须把它移到一个单独的函数中,这样你就可以重复调用它。所以启动三个计时器的函数也需要一个countdown参数。非常复杂,它看起来像这样:

btn_Start_Timer.setOnClickListener() {
    val prepTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
    val workTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
    val restTimeMillis = Integer.parseInt(eT_PrepTime.text.toString().trim()) * 1000L;
    val numberOfRepetitions = Integer.parseInt(eT_Number_Repetitions.text.toString().trim());

    doRep(prepTimeMillis, workTimeMillis, restTimeMillis, numberOfRepetitions)
}

private fun doRep(prepTimeMillis: Long, workTimeMillis: Long, restTimeMillis: Long, times: Int) {
    object : CountDownTimer(prepTimeMillis, 1000) {
        override fun onTick(millisUntilFinished: Long) {
            tV_Total_Duration.setText("Preparation 00:00: " + millisUntilFinished / 1000)
        }

        override fun onFinish() {
            object : CountDownTimer(workTimeMillis, 1000) {
                override fun onTick(millisUntilFinished: Long) {
                    tV_Total_Duration.setText("Work 00:00: " + millisUntilFinished / 1000)
                }

                override fun onFinish() {
                    object : CountDownTimer(restTimeMillis, 1000) {
                        override fun onTick(millisUntilFinished: Long) {
                            tV_Total_Duration.setText("Rest 00:00: " + millisUntilFinished / 1000)
                        }

                        override fun onFinish() {
                            if (times == 1) {
                                tV_Total_Duration.setText("All done!")
                            } else {
                                doRep(prepTimeMillis, workTimeMillis, restTimeMillis, times - 1)
                            }
                        }
                    }.start()
                }
            }.start()
        }
    }.start()

}

这就是所谓的“回调地狱”,在这里您必须深入html" target="_blank">嵌套代码,很难遵循,所以它是使用coroutines进行简化的首选。下面是CountdownTimer的一个挂起函数版本,它允许您按顺序而不是通过嵌套代码来使用它。当您在coroutine中调用这个函数时,您将通常在ontick函数中执行的操作传递给它。它自动启动计时器,然后coroutine挂起,直到onfinish()发生,因此可以顺序编写coroutine代码。如果调用它的coroutine被取消,它将取消计时器,因此ontick()停止被调用。

suspend inline fun countDown(
    millisInFuture: Long,
    countDownInterval: Long,
    crossinline onTick: (Long) -> Unit
) = withContext(Dispatchers.Main) {
    suspendCancellableCoroutine<Unit>{ continuation ->
        val timer = object: CountDownTimer(millisInFuture, countDownInterval) {
            override fun onTick(millisUntilFinished: Long) = onTick(millisUntilFinished)
            override fun onFinish() = continuation.resume(Unit)
        }.start()
        continuation.invokeOnCancellation { timer.cancel() }
    }
}

我还将创建一个简单的helper函数来从编辑文本中获取用户输入,并避免代码重复,如下所示:

fun TextView.inputToInt(): Long = text.toString().trim().toIntOrNull() ?: 0

通过这两个函数,您可以使用启动的coroutine在按钮监听器中顺序编写代码:

btn_Start_Timer.setOnClickListener() {
    val prepTimeMillis = eT_PrepTime.inputToInt() * 1000L
    val workTimeMillis = eT_PrepTime.inputToInt() * 1000L // TODO pick correct ET
    val restTimeMillis = eT_PrepTime.inputToInt() * 1000L // TODO pick correct ET
    val numberOfRepetitions = eT_Number_Repetitions.inputToInt()
    
    lifecycleScope.launch {
        repeat(numberOfRepetitions) {
            countDown(prepTimeMillis, 1000L) { millisUntilFinished ->
                tV_Total_Duration.setText("Preparation 00:00: " + millisUntilFinished / 1000)
            }    
            countDown(workTimeMillis, 1000L) { millisUntilFinished ->
                tV_Total_Duration.setText("Work 00:00: " + millisUntilFinished / 1000)
            }    
            countDown(restTimeMillis, 1000L) { millisUntilFinished ->
                tV_Total_Duration.setText("Rest 00:00: " + millisUntilFinished / 1000)
            }    
        }
    }
}

请注意,您的设计很容易受到每次屏幕旋转时计时器被重置的影响,因为活动将被破坏并重新创建。我建议将计时器放在ViewModel中,该ViewModel使用可以在活动中观察到的值更新LiveData ,以应用于TextView。但这是一个巨大的话题。您可以阅读有关如何使用ViewModel和LiveData的文档。

编辑:为了支持取消,您需要一个属性来保存coroutine作业。

private var countdownJob: Job? = null
countdownJob?.cancel()
countdownJob = lifecycleScope.launch { 
   // ... code from above example
}
 类似资料:
  • 我的目标操作系统将是Windows7。 如有任何帮助,不胜感激,谢谢。

  • 我有一份Java申请。 应用程序有一个决定应用程序是否在启动时启动的设置。 目前,我通过在StartUp items文件夹中放置/删除快捷方式实现了这一点。 然而,我想知道是否有更好的方法来处理这种行为。 编辑 是的,是视窗。抱歉之前没有清除。 应用程序有一个UI,用户可以在其中触发操作,并且应用程序在运行时定期在后台运行一些任务。 @Peter,如何使用应用程序中的代码更改注册表?这种方法是否与

  • 问题内容: 我已经搜索过SO,但没有找到类似的问题,因为我不确定如何在句子中进行逐步设置。我正在使用带有徽标的ActionBarSherlock,而不是带有活动顶部左上角文本的启动器图标(即72x72图标)。 当活动首次加载时,只需不到一秒的时间。我看到清单中定义的启动器图标和标签(如下所示),然后带徽标的操作栏出现。这个家庭活动非常简单,因此它不会做任何可能导致延迟的额外加载。 我可以通过将其样

  • 我正在尝试使用简单的spring启动应用程序。我在ApplicationContext上启动应用程序时遇到问题。 2017-04-26 11:17:31.101警告14528---[main]s.c.a.AnnotationConfigApplicationContext:上下文初始化期间遇到异常-取消刷新尝试:org。springframework。豆。工厂未满足的PendencyExcepti

  • 我发现Android应用程序非常奇怪的错误,而在Android设备中安装应用程序时具有给定的流程。 > 已从Play商店(旧版)安装该应用程序。 启动应用程序(现在我在主屏幕上,即< code>HomeActivity)。 现在,我通过安装最新版本(正在生产版本,尚未发布到playstore)升级了应用程序。 单击启动器图标,等待主屏幕显示,然后按菜单按钮将应用程序置于后台。 现在,单击启动器图标

  • 目前,我正在收听使用spring boot和spring amqp通过API调用创建的新队列。这是一段成功运行的代码 但如果应用程序再次启动,我需要在容器(DirectMessageListenerContainer)中注册队列,并在应用程序启动时将队列添加到侦听器容器id“queueContainer”中。 我无法侦听队列列表并在启动时将其添加到listner容器。在这方面你能帮忙吗