文章目录
    
    
    
    1、功能分析
   
    
    
    1.1、秒表功能界面
   
     
   
    
    
    1.2、App结构
   
- 1个Activity :MainActivity
- 1个Layout :activity_main.xml
    
    
    2、开发视图布局
   
    
    
    2.1、activity_main.xml
   
<?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">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical">
        <TextView
            android:id="@+id/time_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="0:00:00"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            android:textSize="80sp"/>
        <Button
            android:id="@+id/button_start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/start"
            android:onClick="onClickStart"/>
        <Button
            android:id="@+id/button_stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/stop"
            android:onClick="onClickStop"/>
        <Button
            android:id="@+id/button_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="@string/reset"
            android:onClick="onClickReset"/>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
    
    
    2.2、string.xml
   
<resources>
    <string name="app_name">Stopwatch</string>
    <string name="start">start</string>
    <string name="stop"> Stop</string>
    <string name="reset">Reset</string>
    <string name="time">Time</string>
</resources>
    
    
    3、Activity实现
   
    
    
    3.1、MainActivity类
   
public class MainActivity extends AppCompatActivity {
    //计时的秒数
    private  int seconds = 0;
    //计时的状态
    private  boolean running = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在Activity启动时,onCreate()方法中启动runTimer()
        runTimer();
    }
    //启动计时
    public void onClickStart(View view){
        running = true;
    }
    //停止计时
    public void onClickStop(View view){
        running = false;
    }
    //重置秒表
    public void onClickReset(View view){
        running = false;
        seconds = 0;
    }
  	//循环计时方法
    private void runTimer(){
        final TextView timeView = findViewById(R.id.time_view);
        //创建UI线程的handler,用于消息处理
        final Handler handler = new Handler();
        handler.post(new Runnable() {//立即交一个Runnable,任务在Runnable的run()方法中
            @Override
            public void run() {
                int hours = seconds/3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds%60;
                String time = String.format("%d:%02d:%02d", hours, minutes, secs);
                timeView.setText(time);
                if(running){
                    seconds++;
                }            
                //每隔1000ms,重复提交该任务
                handler.postDelayed(this,1000);
            }
        });
    }
}
    
    
    4、生命周期的应用
   
    
    
    4.1、问题分析
   
- 问题一:旋转屏幕,Android检测到屏幕方向变化,计时会重置
- 问题二:App被切换至后台,秒表不能暂停
    
    
    4.2、Activity运行过程
   
     
   
    
    
    4.2、屏幕旋转,计时不重置
   
- 
设备配置变化时,如屏幕旋转,需保存状态,重启时恢复 
- 
在运行后(running),销毁(onDestroy())前会调用 onSaveInstanceState(),保存状态到Bundle - 
保存状态需要覆盖onSaveInstanceState()方法 
- 
Bundle可存储键值对 bundle.put*(“name”,value) 
- 
在Bundle中存储running和seconds @Override public void onSaveInstanceState(Bundle savedInstanceState){ super.onSaveInstanceState(savedInstanceState); savedInstanceState.putInt("seconds",seconds); savedInstanceState.putBoolean("running",running); }
 
- 
- 
在onCreate()方法中恢复,Bundle是其参数,进程第一次新建Activity时为null, 
 
 之后为onSaveInstanceState()保存的Bundle- 
从Bundle取出键值对 bundle.get*(“name”); 
- 
在Bundle中取出running和seconds @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d("life cycle","onCreate"); setContentView(R.layout.activity_main); if(savedInstanceState!=null){ seconds = savedInstanceState.getInt("seconds"); running = savedInstanceState.getBoolean("running"); } runTimer(); }
 
- 
- 
程序运行流程 - 
       用户启动App,点击start按钮,开始计时
 
 runTimer()方法开始递增seconds,并显示到文本框time_view中
- 旋转手机,Android检测到屏幕方向变化,销毁原Activity前,调用onSaveInstanceState()保存实例变量
- Android销毁Activity,再次新建该Activity再次调用onCreate()方法,将保存的Bundle作为参数传入
- onCreate()方法中,取出Bundle存储的值并恢复到销毁前的状态
- runTimer()方法从旋转前的seconds继续计时
 
- 
       用户启动App,点击start按钮,开始计时
    
    
    4.3、App被切换至后台,秒表可以暂停
   
- 
解决方法 - 
覆盖onStop(),在消失前停止计时 @Override protected void onStop(){ super.onStop(); Log.d("life cycle","onStop"); wasRunning = running; // 记录之前是否运行, running = false; //将running设置为false以停止计时 }
- 
覆盖生命周期方法前,必须先调用父类的生命周期 super.onStop(); 
- 
覆盖onStart(),在可见前继续计时 @Override //如果之前running==true,则将running设置为true,继续计时 protected void onStart(){ super.onStart(); Log.d("life cycle","onStop"); if(wasRunning){ running = true; } }
- 
App被切换至后台,Activity对象仍存在,可以使用实例变量存储状态 //计时的秒数 private int seconds = 0; //计时的状态 private boolean running = false; //新的变量,用于在onStop()中保存消失前running的状态 private boolean wasRunning = false; @Override protected void onStop(){ super.onStop(); Log.d("life cycle","onStop"); //日志并暂停 wasRunning = running; running = false; } @Override protected void onStart(){ super.onStart(); Log.d("life cycle","onStop"); //日志并恢复 if(wasRunning){ running = true; } }
- 
Activity销毁前在Bundle保存wasRunning, 
 
 Activity重新实例化后从Bundle恢复wasRunning@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d("life cycle","onCreate"); setContentView(R.layout.activity_main); if(savedInstanceState!=null){ seconds = savedInstanceState.getInt("seconds"); running = savedInstanceState.getBoolean("running"); wasRunning = savedInstanceState.getBoolean("wasRunning"); } runTimer(); } @Override public void onSaveInstanceState(Bundle savedInstanceState){ super.onSaveInstanceState(savedInstanceState); savedInstanceState.putInt("seconds",seconds); savedInstanceState.putBoolean("running",running); savedInstanceState.putBoolean("wasRunning",wasRunning); }
 
- 
- 
运行流程 - 用户启动App,点击Start按钮,runTimer()开始递增seconds并更新文本框
- 用户点击Home键,Activity消失,Android调用onStop()
- 用户返回秒表App,Activity可见,Android调用onStart()
 
    
    
    5、MainActivity完整代码
   
public class MainActivity extends AppCompatActivity {
    //计时的秒数
    private  int seconds = 0;
    //计时的状态
    private  boolean running = false;
    //新的变量,用于在onStop()中保存消失前running的状态
    private boolean wasRunning = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("life cycle","onCreate");
        setContentView(R.layout.activity_main);
        if(savedInstanceState!=null){
            seconds = savedInstanceState.getInt("seconds");
            running = savedInstanceState.getBoolean("running");
            wasRunning = savedInstanceState.getBoolean("wasRunning");
        }
        //在Activity启动时,onCreate()方法中启动runTimer()
        runTimer();
    }
    @Override
    protected void onStart(){
        super.onStart();
        Log.d("life cycle","onStop");
        if(wasRunning){
            running = true;
        }
    }
    @Override
    protected void onStop(){
        super.onStop();
        Log.d("life cycle","onStop");
        wasRunning = running;
        running = false;
    }
    @Override
    public void onSaveInstanceState(Bundle savedInstanceState){
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putInt("seconds",seconds);
        savedInstanceState.putBoolean("running",running);
        savedInstanceState.putBoolean("wasRunning",wasRunning);
    }
    protected void onDestroy(){
        super.onDestroy();
        Log.d("life cycle","onDestroy");
    }
    //启动计时
    public void onClickStart(View view){
        running = true;
    }
    //停止计时
    public void onClickStop(View view){
        running = false;
    }
    //重置秒表
    public void onClickReset(View view){
        running = false;
        seconds = 0;
        wasTiming = false;
    }
    //循环计时方法
    private void runTimer(){
        final TextView timeView = findViewById(R.id.time_view);
        //创建UI线程的handler,用于消息处理
        final Handler handler = new Handler();
        handler.post(new Runnable() {//立即交一个Runnable,任务在Runnable的run()方法中
            @Override
            public void run() {
                int hours = seconds/3600;
                int minutes = (seconds%3600)/60;
                int secs = seconds%60;
                String time = String.format("%d:%02d:%02d", hours, minutes, secs);
                timeView.setText(time);
                if(running){
                    seconds++;
                }
                //每隔1000ms,重复提交该任务
                handler.postDelayed(this,1000);
            }
        });
    }
}
 
