簡體   English   中英

倒數計時器似乎在后台運行,即使我在方向更改后暫停它(縱向到橫向,反之亦然)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM