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();
}
我试过 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();
}