工程地址:https://github.com/MADMAX110/Stopwatch
上次实现了一个Android秒表应用,在模拟器里运行这个应用,应用没有任何问题,但是在真实设备上运行这个应用时,旋转设备的方向,秒表会自动归零。下面分析一下是什么导致了这个问题。
1、用户启动应用,单击Start,开始运行。runTimer使用seconds和running变量开始递增文本视图中显示的秒数。
2、用户旋转设备之后,Android看到屏幕方向和大小发生变化,他会撤销这个活动,包括runTimer使用的所有变量。
3、然后重新创建StopwatchActivity。变量均会重置为默认值。
这是因为旋转屏幕会改变Android设备配置,设备配置包括用户指定的选项(如本地化环境),以及与真实设备相关的选项(如方向和屏幕大小),其中任何选项的改变都会导致撤销活动,然后重新创建活动。
要处理配置改变的问题,最好的办法是保存活动的当前状态,然后在活动的onCreate方法中恢复这个状态。要保存活动的当前状态,需要实现onSaveInstanceState方法,这个方法会在活动被撤消之前调用。
@Override
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
savedInstanceState.putInt("seconds", seconds);
savedInstanceState.putBoolean("running", running);
}
onSavedInstanceState()方法有一个参数Bundle,利用Bundle可以把不同类型的数据收集到一个对象中。
onCreate()方法得到作为参数传入的Bundle,onCreate方法会在活动创建时重新得到这些值。
Bundle可以使用bundle.put*(“name”, value)方法添加值。
在onCreate中添加一下代码可以获取之前存过的信息
if (savedInstanceState != null) {
seconds = savedInstanceState.getInt("seconds");
running = savedInstanceState.getBoolean("running");
}
以下是改好后的完整的StopwatchActivity代码。
package com.hfad.stopwatch;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import java.util.Locale;
import android.os.Handler;
import androidx.annotation.NonNull;
public class StopwatchActivity extends Activity {
private int seconds = 0;//记录已经过去的秒数
private boolean running;//秒表是否正常运行
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stopwatch);
if (savedInstanceState != null) {
seconds = savedInstanceState.getInt("seconds");
running = savedInstanceState.getBoolean("running");
}
runTimer();//使用单独的方法更新秒表。创建活动会调用这个方法
}
@Override
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
savedInstanceState.putInt("seconds", seconds);
savedInstanceState.putBoolean("running", running);
}
//启动秒表
public void onClickStart(View view) {
running = true;
}
//停止秒表
public void onClickStop(View view) {
running = false;
}
//单击reset按钮时会调用这个方法
public void onClickReset(View view) {
running = false;
seconds = 0;
}
private void runTimer() {
//得到文本视图
final TextView timeView = (TextView) findViewById(R.id.time_view);
//创建一个新地Handler
final Handler handler = new Handler();
//调用post()方法,传入一个新的Runnable。post()方法会立即运行代码
handler.post(new Runnable() {
public void run() {
int hours = seconds / 3600;
int minutes = (seconds%3600)/60;
int secs = seconds % 60;
//设置显示格式
String time = String.format(Locale.getDefault(), "%d:%02d%02d", hours, minutes, secs);
//设置文本视图
timeView.setText(time);
if (running) {
++seconds;
}
//在1000ms后再次提交并运行Runnable中的代码,会反复调用
handler.postDelayed(this, 1000);
}
});
}
}