Chronometer & Fragment,旋转后恢复状态

Chronometer & Fragment, Restore State after rotation

我试过 Android: How to restore the state of a stopped chronometer after rotation? and Fragments restore state on orientation changed 但它们不适用于我,因为它们要么不起作用,要么我正在使用片段。

此外,任何对计时器的引用似乎都会引发应用程序崩溃。

错误:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.elliot_labs.timetracker/com.elliot_labs.timetracker.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Chronometer.setBase(long)' on a null object reference
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
                      at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4519)
                      at android.app.ActivityThread.-wrap19(ActivityThread.java)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1483)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6119)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
                   Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Chronometer.setBase(long)' on a null object reference
                      at com.elliot_labs.timetracker.MainFragment.onCreate(MainFragment.java:95)
                      at android.support.v4.app.Fragment.performCreate(Fragment.java:2172)
                      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1243)
                      at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1523)
                      at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1585)
                      at android.support.v4.app.FragmentManagerImpl.dispatchCreate(FragmentManager.java:2827)
                      at android.support.v4.app.FragmentController.dispatchCreate(FragmentController.java:190)
                      at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:353)
                      at android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:88)
                      at com.elliot_labs.timetracker.MainActivity.onCreate(MainActivity.java:30)
                      at android.app.Activity.performCreate(Activity.java:6679)
                      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
                      at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4519) 
                      at android.app.ActivityThread.-wrap19(ActivityThread.java) 
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1483) 
                      at android.os.Handler.dispatchMessage(Handler.java:102) 
                      at android.os.Looper.loop(Looper.java:154) 
                      at android.app.ActivityThread.main(ActivityThread.java:6119) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
Application terminated.

代码(fragment.java):

package com.elliot_labs.timetracker;


import android.content.Context;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.Toast;


/**
 * A simple {@link Fragment} subclass.
 */
public class MainFragment extends Fragment implements OnClickListener {

    Button toggleButton;
    Button resetButton;
    Chronometer chronometer;
    long timeWhenStopped = 0;
    boolean currentlyTiming = false;

    public MainFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View v = inflater.inflate(R.layout.fragment_main, container, false);

        toggleButton = (Button) v.findViewById(R.id.buttonToggleChronometer);
        resetButton = (Button) v.findViewById(R.id.buttonReset);
        chronometer = (Chronometer) v.findViewById(R.id.mainChronometer);

        toggleButton.setOnClickListener(this);
        resetButton.setOnClickListener(this);

        return v;
    }

    public void toggleChronometer(){
        if(!currentlyTiming){
            chronometer.setBase(SystemClock.elapsedRealtime() + timeWhenStopped);
            chronometer.start();
            toggleButton.setText("Stop");
            currentlyTiming = true;
        } else {
            timeWhenStopped = chronometer.getBase() - SystemClock.elapsedRealtime();
            chronometer.stop();
            toggleButton.setText("Start");
            currentlyTiming = false;
        }
    }
    public void resetChronometer(){
        timeWhenStopped = 0;
        chronometer.setBase(SystemClock.elapsedRealtime());
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.buttonToggleChronometer:
                toggleChronometer();
                break;
            case R.id.buttonReset:
                resetChronometer();
                break;
        }
    }


    @Override
    public void onSaveInstanceState(Bundle outState) {
        // Save myVar's value in saveInstanceState bundle
        outState.putLong("time", chronometer.getBase());
        outState.putBoolean("isTiming", currentlyTiming);

        super.onSaveInstanceState(outState);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // savedInstanceState is the bundle in which we stored myVar in onSaveInstanceState() method above
        // savedInstanceState == null means that activity is being created a first time
        if (savedInstanceState != null) {
            chronometer.setBase(savedInstanceState.getLong("time"));
            if (savedInstanceState.getBoolean("isTiming")) {
                chronometer.start();
                currentlyTiming = savedInstanceState.getBoolean("isTiming");
            }
        }
    }
}

代码(mainActivity.java):

package com.elliot_labs.timetracker;

import android.os.Bundle;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    NavigationView navigationView = null;
    Toolbar toolbar = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Starts the main fragment
        MainFragment fragment = new MainFragment();
        android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragment_container, fragment);
        fragmentTransaction.commit();

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.setNavigationItemSelectedListener(this);

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.setDrawerListener(toggle);
        toggle.syncState();
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }


    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_timer) {
            MainFragment fragment = new MainFragment();
            android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.fragment_container, fragment);
            fragmentTransaction.commit();
        } else if (id == R.id.nav_categories) {
            CategoryFragment fragment = new CategoryFragment();
            android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.fragment_container, fragment);
            fragmentTransaction.commit();
        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_settings) {
            SettingsFragment fragment = new SettingsFragment();
            android.support.v4.app.FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
            fragmentTransaction.replace(R.id.fragment_container, fragment);
            fragmentTransaction.commit();
        } else if (id == R.id.nav_feedback) {

        } else if (id == R.id.nav_help) {

        }

        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }
}

旋转屏幕后如何恢复状态?

谢谢!

onCreate在onCreateView之前调用,所以chonometer字段为null,只需恢复onCreateView中的chronometer并删除onCreate中的代码。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View v = inflater.inflate(R.layout.fragment_main, container, false);

    toggleButton = (Button) v.findViewById(R.id.buttonToggleChronometer);
    resetButton = (Button) v.findViewById(R.id.buttonReset);
    chronometer = (Chronometer) v.findViewById(R.id.mainChronometer);

    toggleButton.setOnClickListener(this);
    resetButton.setOnClickListener(this);

    if (savedInstanceState != null) {
        chronometer.setBase(savedInstanceState.getLong("time"));
        if (savedInstanceState.getBoolean("isTiming")) {
            chronometer.start();
            currentlyTiming = savedInstanceState.getBoolean("isTiming");
        }
    }

    return v;
}

然后在您的 activity 中条件化片段替换为:

if (savedInstanceState == null) {
    MainFragment fragment = new MainFragment();
    FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
}