Kotlin:CountDownTimer onTick被onClick中断

时间:2020-09-28 20:48:10

标签: android kotlin countdowntimer

在我的多项选择测验应用程序中,必须在CountDownTimer达到0之前选择并提交答案。如果计时器达到0或提交的答案不正确,则会显示一个对话框。

这是我的问题:如果用户在未选择答案的情况下按下“提交”按钮,计时器将重置,并且文本视图会在重置时间和正确时间之间快速交替。例如,如果计时器设置为20秒,并且当计时器显示17秒时单击“提交”,则计时器显示以下内容:20、19、18、17、19 / 16、18 / 15、17 / 14和以此类推。 每次单击“提交”按钮时,这个问题就变得更加复杂。

如果在未选择答案的情况下单击“提交”,如何允许计时器继续运行?

计时器声明

type Config<T> = {
    context: T;
};

type Hooks<T> = {
    hooks: T;
};

type FunctionWithThis<T> = (this: T, ...args: any[]) => any;

type RemoveThis<T extends Record<string, FunctionWithThis<any>>> = {
    [P in keyof T]: T[P] extends (...a: infer A) => infer R ?  (...a:A) => R: never
}

 const configure = <TContext extends Object, 
                    THooks extends Record<string, FunctionWithThis<TContext>>>
                   (config: Config<TContext> & Hooks<THooks>) => {
    const result = {
        get data() { return config.context; }
    };

    Object.entries(config.hooks).forEach((action) => {
        (result as any)[action[0]] = (...args: any[]) => action[1].call(config.context, ...args);
    });

    return result as { data: TContext; } & RemoveThis<THooks>;
};

const engine = configure({
    context: {
        foo: 12
    },
    hooks: {
        log() {
            console.log(this.foo); // this.foo is typed correctly here
        }
    }
});

const data = engine.data;
engine.log(); // ok

设置问题

 private lateinit var timer: CountDownTimer
 var isRunning: Boolean = true

计时器

private fun setQuestion() {
        val question = mQuestionsList!![mCurrentPosition - 1]
        defaultOptionsView()
        if(mCurrentPosition == mQuestionsList!!.size + 1){
            timer.cancel()
            btn_submit.text = "FINISH"
        }else{
            btn_submit.text = "SUBMIT"
        }

OnClick

timer = object: CountDownTimer(20000, 1000){
            override fun onTick(millisUntilFinished: Long) {
                val timeResult =
                    "${(millisUntilFinished/1000 / 60).toString().padStart(1, '0')}:" +
                    "${(millisUntilFinished/1000 % 60).toString().padStart(2, '0')}"
                tv_timer.text = "$timeResult"
            }

            override fun onFinish() {
                isRunning = false
                btn_submit.callOnClick()
                timer.cancel()
    //cancel the timer and simulate button click to treat answer as incorrect and show dialogBox
            }
        }.start()

2 个答案:

答案 0 :(得分:0)

我看不到为什么您使用已发布的代码会得到特定的行为-它的行为就像您再次运行计时器设置代码,因此一次运行了两个,这就是为什么让他们为应该显示什么时间而战。猜测一下,您没有在做timer = blabla之前取消了旧版本,因此它只是在后台运行,因此您不再有对其的引用。

老实说,如果您是我,我会将该逻辑分解为一些单独的功能,因此您拥有应用程序需要执行的步骤,从显示问题到验证用户的答案,再到下一步。因此,您的onClick最终会遇到这样的情况:

fun reset() {
    clearTimer()
    clearRadioButtons()
    answerSelected = -1
}

override fun onClick(v: View?) {
    when(v?.id){
        R.id.radio_button1 -> answerSelected = 1
        R.id.radio_button2 -> answerSelected = 2
        R.id.radio_button3 -> answerSelected = 3
        R.id.radio_button4 -> answerSelected = 4
        R.id.btn_submit -> if (answerSelected == -1) showWarning() else submitAnswer(answerSelected)
    }
}

fun showWarning() {
    // show a warning toast, no need to do anything else
    // (including messing with the timer)
}

fun submitAnswer(answerNumber: Int) {
    // validate the input (e.g. answer between 1 and 4)
    // you might want to call showWarning() here instead, in response to validation failure
    recordAnswer()
    reset()
    // if you make setQuestion return false when there are no questions
    // left, you can check that and take action
    if (!setNextQuestion()) showResults()
}

有很多方法可以做到,但希望您能想到。通过将其分解为较小的任务(使用好名字,使其读起来像简单的指令),可以更轻松地推断正在发生的事情,并一步一步地采取必要的行动。

通过将诸如clearTimer()之类的重要动作放入自己的函数中,您可以在任何地方看到该动作(通过调用该函数),并且更容易判断基本流程是否正确

答案 1 :(得分:0)

此问题与下面的setQuestion()语句中的when中调用计时器有关。

when {
   mCurrentPosition <= mQuestionsList!!.size -> {
      setQuestion()
   }

通过将计时器放在onCreate中并在timer.start()语句中调用when来复制该问题。删除timer.start()可解决此问题。新的代码可以在下面找到。

onCreate

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_questions_assessment)
        mQuestionsList = ConstantsAssessment.getQuestions2()
        setQuestion()
        btn_submit.setOnClickListener(this)
        btn_back.setOnClickListener() {
            startActivity(Intent(this, MainActivity::class.java))
        }
        timer = object: CountDownTimer(20000, 1000) {
            override fun onTick(millisUntilFinished: Long) {
                val timeResult =
                    "${(millisUntilFinished/1000 / 60).toString().padStart(1, '0')}:" +
                            "${(millisUntilFinished/1000 % 60).toString().padStart(2, '0')}"
                tv_timer.text = "$timeResult"
            }
            override fun onFinish() {
                isRunning = false
                btn_submit.callOnClick()
                timer.cancel()
            }
        }.start()
    }

提交按钮

R.id.btn_submit -> {
                if (mSelectedOptionPosition == 0 && isRunning) {
                    if (radio_group.checkedRadioButtonId == -1 && btn_submit.text == "SUBMIT") {
                        Toast.makeText(applicationContext, "Please select an answer", Toast.LENGTH_SHORT).show();
                    } else {
                        radio_group.clearCheck()
                        mCurrentPosition++
                        timer.start()
                    }
                    when {
                        mCurrentPosition <= mQuestionsList!!.size -> {
                            setQuestion()
                        }
                        else -> {
                            val intent = Intent(this, ResultsActivity::class.java)
                            intent.putExtra(ConstantsAssessment.TOTAL_CORRECT, mCorrectAnswers)
                            intent.putExtra(ConstantsAssessment.TOTAL_OPP, mQuestionsList!!.size)
                            startActivity(intent)
                        }
                    }
                }