[英]Countdown timer seems to be running in background even after I pause it after orientation change (portrait to landscape or vice versa)
我做了一個倒數計時器,我希望它即使在方向改變后也能繼續運行。 但是說當我啟動計時器然后改變方向然后暫停它時,計時器似乎在后台運行。
我通過在onFinish()
function 中顯示吐司消息來檢查它是否在后台運行,它似乎在后台運行。
import android.os.CountDownTimer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
private static final long START_TIME_IN_MILLIS = 10000;
private TextView mTextViewCountDown;
private Button mButtonStartPause;
private Button mButtonReset;
private CountDownTimer mCountDownTimer;
private boolean mTimerRunning;
private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
private long mEndTime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextViewCountDown = findViewById(R.id.text_view_countdown);
mButtonStartPause = findViewById(R.id.button_start_pause);
mButtonReset = findViewById(R.id.button_reset);
mButtonStartPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mTimerRunning) {
pauseTimer();
} else {
startTimer();
}
}
});
mButtonReset.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resetTimer();
}
});
updateCountDownText();
}
private void startTimer() {
mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;
mCountDownTimer = new CountDownTimer(mTimeLeftInMillis, 1000) {
@Override
public void onTick(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
updateCountDownText();
}
@Override
public void onFinish() {
mTimerRunning = false;
Toast.makeText(MainActivity.this, "TIMER RUNNING IN BACKGROUND..!!! ", Toast.LENGTH_SHORT).show();
updateButtons();
}
}.start();
mTimerRunning = true;
updateButtons();
}
private void pauseTimer() {
mCountDownTimer.cancel();
mTimerRunning = false;
updateButtons();
}
private void resetTimer() {
mTimeLeftInMillis = START_TIME_IN_MILLIS;
updateCountDownText();
updateButtons();
}
private void updateCountDownText() {
int minutes = (int) (mTimeLeftInMillis / 1000) / 60;
int seconds = (int) (mTimeLeftInMillis / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
}
private void updateButtons() {
if (mTimerRunning) {
mButtonReset.setVisibility(View.INVISIBLE);
mButtonStartPause.setText("Pause");
} else {
mButtonStartPause.setText("Start");
if (mTimeLeftInMillis < 1000) {
mButtonStartPause.setVisibility(View.INVISIBLE);
} else {
mButtonStartPause.setVisibility(View.VISIBLE);
}
if (mTimeLeftInMillis < START_TIME_IN_MILLIS) {
mButtonReset.setVisibility(View.VISIBLE);
} else {
mButtonReset.setVisibility(View.INVISIBLE);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong("millisLeft", mTimeLeftInMillis);
outState.putBoolean("timerRunning", mTimerRunning);
outState.putLong("endTime", mEndTime);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mTimeLeftInMillis = savedInstanceState.getLong("millisLeft");
mTimerRunning = savedInstanceState.getBoolean("timerRunning");
updateCountDownText();
updateButtons();
if (mTimerRunning) {
mEndTime = savedInstanceState.getLong(enter code here"endTime");
mTimeLeftInMillis = mEndTime - System.currentTimeMillis();
startTimer();
}
}
}
有沒有辦法解決這個問題?
您可以將倒計時邏輯和數據移動到ViewModel
ViewModel class 旨在以生命周期意識的方式存儲和管理與 UI 相關的數據。 ViewModel class 允許數據在屏幕旋轉等配置更改中保留下來。
它是推薦應用架構中使用的架構組件。
MainViewModel 代碼可以是:
package com.example.countdown;
import androidx.lifecycle.ViewModel;
public class MainViewModel extends ViewModel {
private static final long START_TIME_IN_MILLIS = 10000;
private boolean mTimerRunning;
private long mTimeLeftInMillis = START_TIME_IN_MILLIS;
private long mEndTime;
boolean getTimerRunning() {
return mTimerRunning;
}
long getTimeLeftInMillis() {
return mTimeLeftInMillis;
}
long getEndTime() {
return mEndTime;
}
void startTimer() {
mEndTime = System.currentTimeMillis() + mTimeLeftInMillis;
mTimerRunning = true;
}
void restoreTimer(boolean timerRunning, long timeLeftInMillis, long endTime) {
this.mTimerRunning = timerRunning;
this.mTimeLeftInMillis = timeLeftInMillis;
this.mEndTime = endTime;
}
void finish() {
mTimerRunning = false;
}
void restart() {
mTimeLeftInMillis = START_TIME_IN_MILLIS;
}
void updateCounter(long millisUntilFinished) {
mTimeLeftInMillis = millisUntilFinished;
}
boolean remainingTime() {
return mTimeLeftInMillis < START_TIME_IN_MILLIS;
}
}
和 MainActivity:
package com.example.countdown;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
protected MainViewModel viewModel;
private TextView mTextViewCountDown;
private Button mButtonStartPause;
private Button mButtonReset;
private CountDownTimer mCountDownTimer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
mTextViewCountDown = findViewById(R.id.text_view_countdown);
mButtonStartPause = findViewById(R.id.button_start_pause);
mButtonReset = findViewById(R.id.button_reset);
mButtonStartPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (viewModel.getTimerRunning()) {
pauseTimer();
} else {
startTimer();
}
}
});
mButtonReset.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resetTimer();
}
});
updateCountDownText();
}
@Override
protected void onPause() {
super.onPause();
if (mCountDownTimer != null) {
mCountDownTimer.cancel();
}
}
@Override
protected void onResume() {
super.onResume();
if (viewModel.getTimerRunning()) {
startTimer();
}
}
@Override
protected void onDestroy() {
viewModel.finish();
super.onDestroy();
}
private void startTimer() {
viewModel.startTimer();
mCountDownTimer = new CountDownTimer(viewModel.getTimeLeftInMillis(), 1000) {
@Override
public void onTick(long millisUntilFinished) {
viewModel.updateCounter(millisUntilFinished);
updateCountDownText();
}
@Override
public void onFinish() {
viewModel.finish();
Toast.makeText(MainActivity.this, "Timer finished", Toast.LENGTH_SHORT).show();
updateButtons();
}
}.start();
updateButtons();
}
private void pauseTimer() {
if (mCountDownTimer != null) {
mCountDownTimer.cancel();
}
viewModel.finish();
updateButtons();
}
private void resetTimer() {
viewModel.restart();
updateCountDownText();
updateButtons();
}
private void updateCountDownText() {
int minutes = (int) (viewModel.getTimeLeftInMillis() / 1000) / 60;
int seconds = (int) (viewModel.getTimeLeftInMillis() / 1000) % 60;
String timeLeftFormatted = String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
mTextViewCountDown.setText(timeLeftFormatted);
}
private void updateButtons() {
if (viewModel.getTimerRunning()) {
mButtonReset.setVisibility(View.INVISIBLE);
mButtonStartPause.setText("Pause");
} else {
mButtonStartPause.setText("Start");
if (viewModel.getTimeLeftInMillis() < 1000) {
mButtonStartPause.setVisibility(View.INVISIBLE);
} else {
mButtonStartPause.setVisibility(View.VISIBLE);
}
if (viewModel.remainingTime()) {
mButtonReset.setVisibility(View.VISIBLE);
} else {
mButtonReset.setVisibility(View.INVISIBLE);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong("millisLeft", viewModel.getTimeLeftInMillis());
outState.putBoolean("timerRunning", viewModel.getTimerRunning());
outState.putLong("endTime", viewModel.getEndTime());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
updateCountDownText();
updateButtons();
long timeLeftInMillis = savedInstanceState.getLong("millisLeft");
if (viewModel.getTimerRunning()) {
timeLeftInMillis = viewModel.getEndTime() - System.currentTimeMillis();
startTimer();
}
viewModel.restoreTimer(savedInstanceState.getBoolean("timerRunning"),
timeLeftInMillis,
savedInstanceState.getLong("endTime"));
}
}
請注意它是如何使用 ViewModelProvider() 獲取 ViewModel 的,一旦重新創建活動,它將接收相同的 ViewModel 實例,該實例的范圍僅限於此活動。
此外,不要忘記在 onDestroy() 中銷毀計時器,當此活動完成且未重新創建時。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.