I have the following scenario:
Activity A > Activity B
Activity A < Activity B
What i would like to do is keep the state of Activity A when clicking Activity B's back button.
Activity A code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_details);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null)
{
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
// check for saved instance
if (savedInstanceState != null)
{
//restore saved values
}
else
{
//initialize members with default values
}
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putString("typeID", typeID);
super.onSaveInstanceState(savedInstanceState);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
typeID = savedInstanceState.getString("typeID");
}
public boolean gotoActivityB(View view)
{
Intent intent = new Intent(getApplicationContext(), ActivityB.class);
startActivity(intent);
return false;
}
Activity B code:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_buy_item);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar != null)
{
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
//do some magic...
}
public boolean onOptionsItemSelected(MenuItem item)
{
Intent intent = new Intent(getApplicationContext(), ActivityA.class);
startActivity(intent);
return false;
}
Every time i go from Activity B to Activity A, savedInstanceState is equal to null, in other words, Activity A state isn't saved or restored.
What am i missing here?
!!SOLUTION!!
Based on #cybersam's answer, Activities maintain their state by default. So there is no need for the savedInstanceState. To solve my problem i only had to update my back button events to:
public boolean onOptionsItemSelected(MenuItem item)
{
finish();
return true;
}
As the documentation for onSaveInstanceState() states:
An example when onPause() is called and not
onSaveInstanceState(Bundle) is when activity B is launched in front of
activity A: the system may avoid calling onSaveInstanceState(Bundle)
on activity A if it isn't killed during the lifetime of B since the
state of the user interface of A will stay intact.
So you cannot assume that onSaveInstanceState() would be called on A just because B is launched in front of it. In fact, most of the time, it will not be.
[EDITED]
Your code for B seems to be calling startActivity() to "go back" to the prior activity. If you just want B to go back to the prior activity, you can (usually) just call finish() to exit B, which should allow A to reappear (with its state intact), since it will become the top Activity in the stack.
Related
I have a back button in one activity and when I tap on it to return to the parent activity it will reset the parent activity. It's like onCreate() is being called again. I'm not sure why that is because when you tap on the back button it just calls finish() to exist the activity I'm currently in.
Here is how I'm declaring the toolbar:
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_item);
Toolbar toolbar = (Toolbar) findViewById(R.id.my_toolbar);
setSupportActionBar(toolbar);
if(getSupportActionBar() != null)
{
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
This is what happens when you tap on the button:
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId()==android.R.id.home)
{
finish();
}
return super.onOptionsItemSelected(item);
}
The strange this is that when I hit the save button I return to the parent activity without any reset. So I'm not sure why this is happening.
You need to return true or else it will always call the onCreat() method. Also, you can create an "empty" intent and just not process it on the activity you return true.
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId()==android.R.id.home)
{
Intent intent = new Intent();
setResult(Intent_Constant.TAPPED_BACK_BUTTON, intent);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
I have a fragment with a view and an options menu:
public class OfferingsFragment extends Fragment
{
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState)
{
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.offering_tiles, null);
...
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
Intent intent = new Intent(getActivity(), SettingsActivity.class);
startActivity(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
}
From the options menu, the user opens this preference fragment, which is hosted by the SettingsActivity:
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
}
The view of the OfferingsFragment depends on one of the preferences. That is, after this preference has changed, the OfferingsFragment must be refreshed by calling onCreateView again. What I do is this:
Open preference screen from OfferingsFragment's option menu
Change preference
Return to OfferingsFragment
If I return to the OfferingsFragment via the Home Button (left arrow in ActionBar), then the OfferingsFragment gets refreshed by calling its onCreateView (which is the desired effect). However, if I return to the OfferingsFragment via the Back Button (on the device), onCreateView is NOT CALLED and thus the view is NOT re-created. What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
What Happens
When you press Up button parent activity is called via startActivity which means a new instance is created by default.
When you press Back button current activity is finished and you're back in the previous activity and its already existing instance (it was in stopped state).
How To Deal With It
What I want is that the view is also re-created when the user presses the Back Button. Any ideas how to achieve this?
Start the settings activity via startActivityForResult:
public static final int RC_SETTINGS = 1;
private void startSettingsActivity() {
Intent i = new Intent(this, SettingsActivity.class);
startActivityForResult(i, RC_SETTINGS);
}
When the result comes back reattach the fragment. This will recreate its view hierarchy.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Call to super if you value your life. And want proper lifecycle handling.
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SETTINGS) {
// If we just came back from SettingsActivity...
// ...reattach fragment and trigger view recreation.
final FragmentManager fm = getFragmentManager();
final f = fm.findFragmentById(android.R.id.content);
fm.beginTransaction().detach(f).attach(f).commit();
}
}
Replace the fragment ID with whatever you used.
Pro tip
If your fragment is not misconfigured you should be able to call
public class SettingsActivity extends Activity {
private SettingsFragment settingsFragment = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
settingsFragment = new SettingsFragment();
getFragmentManager().beginTransaction()
.replace(android.R.id.content, settingsFragment)
.commit();
} else {
settingsFragment = (SettingsFragment) getFragmentManager().findFragmentById(android.R.id.content);
}
}
}
This is both resourceful and practical as your original code would lose state (for example scroll position) on configuration change.
Right off the top, I want to clarify something: The button that I am struggling with is NOT the back button. I am referring to the up/home button in the ActionBar / Toolbar at the top of the app, not the Android button at the bottom. There are a few posts of a similar nature, but they address the back button, not the up button.
Here is the situation: I have an Activity A that has a ListView fragment. When the user clicks on a list view item, it launches Activity B. Pretty typical. Activity A has an EditText field in the toolbar that allows the user to enter a search parameter. If the user hits the up/home button from Activity B, I return successfully to Activity A. However, I want Activity A to show the same text in the EditText field that was there when they left it. If the user hits the back button, this text is restored. But if they navigate with the up/home button, the EditText field is empty.
Using some log statements, I can see that when a list item is tapped from activity A, onSaveInstanceState and onStop are both called (but onDestroy is NOT called at that point.) From activity B, when the up/home button is tapped, onDestroy from activity A is immediately called, followed by onCreate, etc. However, the bundle savedInstanceState is null, presumably since onDestroy was just called.
Why is onDestroy called when returning to Activity A? This makes no sense to me. Here is what I have in the manifest:
<activity
android:name=".Activity.ActivityA"
android:label="#string/app_name"
android:parentActivityName=".Activity.ParentActivity"
android:theme="#style/AppTheme"
android:launchMode="singleTop"
android:windowSoftInputMode="stateVisible" />
<activity
android:name=".Activity.ActivityB"
android:label="#string/app_name"
android:parentActivityName=".Activity.ActivityA"
android:theme="#style/AppTheme" />
Here are the relevant methods in Activity A:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
actionBar = getSupportActionBar();
if (actionBar != null)
initializeActionBar();
if (getSupportActionBar() != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Log.d(TAG, "on create");
if (savedInstanceState != null) {
Log.d(TAG, "saved instance state not null");
if (savedInstanceState.getString("search_text") != null)
etSearch.setText(savedInstanceState.getString("search_text"));
}
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("search_text", etSearch.getText().toString());
Log.d(TAG, "on Save instance state");
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "on restore instance state");
if (savedInstanceState != null) {
if (savedInstanceState.getString("search_text") != null)
etSearch.setText(savedInstanceState.getString("search_text"));
}
}
#Override
protected void onResume() {
super.onResume();
Log.d(TAG, "on resume");
}
#Override
protected void onStop() {
super.onStop();
Log.d(TAG, "on stop");
}
#Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "on destroy");
}
private void initializeActionBar() {
actionBar.setCustomView(R.layout.actionbar_with_edittext);
etSearch = (EditText) actionBar.getCustomView().findViewById(R.id.actionbar_searchfield);
etSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) {
initiateNewSearch();
etSearch.clearFocus();
}
return false;
}
});
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(etSearch, InputMethodManager.SHOW_IMPLICIT);
etSearch.requestFocus();
}
I don't think that any of the code in Activity B is relevant here.
This is my console output when a user taps on a listview item in Activity A:
on Save instance stateon stop
And then this is what is generated when the user taps on the up/home button from activity B:
on destroy on create on resume
If there is anything else that may be of help, please let me know. Thanks for any advice!
I don't know why default up button implementation creates a new activity but a working solution for me is to override onOptionsItemSelected:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if(id== android.R.id.home ){
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
Also this solution works for me:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
NavUtils.navigateUpTo(this, intent);
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
I had this problem too, and after a little more digging found the correct solution is to make a change in the manifest to add the following to your parent activity:
android:launchMode="singleTop"
Details are explained here.
I've hit a weird problem with my Android app. My main activity has a menu attached to the menu button. Problem is, the menu button works exactly once. Once pressed, the app has to be restarted before the menu button will work again.
The (sanitised) code is:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
startActivity(new Intent(this, PreferencesActivity.class));
return(super.onCreateOptionsMenu(menu));
}
#Override
public void onBackPressed() {
this.finish();
}
#Override
public void onDestroy() {
super.onDestroy();
this.finish();
}
#Override
public void onStop() {
super.onStop();
this.finish();
}
and the preferences activity looks like
public class PreferencesActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
addPreferencesFromResource(R.xml.preferences);
}
#Override
public void onResume() {
super.onResume();
}
#Override
public void onStop() {
super.onStop();
this.finish();
}
#Override
public void onBackPressed() {
this.finish();
}
}
Any ideas how I should resolve this?
TIA
The onCreateOptionsMenu is only called once, right before you open the OptionsMenu the first time.
Instead use onPrepareOptionsMenu to call your startActivity(new Intent(this, PreferencesActivity.class));
onPrepareOptionsMenuis called everytime you click on the Menu-Button.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
startActivity(new Intent(this, PreferencesActivity.class));
return super.onPrepareOptionsMenu(menu);
}
The problem is that you are starting an Activity instead of showing a menu.
The normal way would be to inflate a menu inside the onCreate Optionsmenu and have a settings item in this menu, that if pressed shows the Preference Activity.
Thommy is right. If you want to start the preference Activity right away (keep in mind that this is unusual behavior for an Android App) you can do this in the onPrepareOptionsMenu.
In the normal flow the menu is created once in the onCreateOptionsMenu callback and after that it is reused.
After clicking on the menu button you are moving to the PreferencesActivity and whenever you click back over there... the first activity is closed because of the onBackPressed method.
Can you tell me what exactly you are looking for ?
I have an Activity A with 2 tabs and each tab have its own Activity (B and C) . Activity B and Activity C each have 2 text fields . I want to save value of these text field in SharedPreferences when user changes tabs.
How can I do this?
What you probably want is save your activity state in onSaveInstanceState instead, like this:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(MY_KEY, myStringValue);
// ...
}
And then in onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// other stuff...
if(savedInstanceState != null) {
myStringValue = savedInstanceState.getString(MY_KEY);
}
}