onCreate is not called after switching back from preferences - android

My app only has two activities: main and preferences
In the PreferenceActivity (or in my case SettingsActivity), the user can change some stuff, that affects the MainActivity.
This is my SettingsActivity:
public class SettingsActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
public static class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
return false;
}
}
}
Now I have two issue:
When the user presses the HomeButton the main screen does not seem to call the onCreate so the changed preferences are not loaded in the main activity. What do I need to change, to tell android to call the onCreate function?
Since android nougat there are these "activity-change-animations", so when switching to a new activity, the new screen kind of slides in. But when the user presses the HomeButton the expected animation would be slide out, but it is also slide in. How can I fix that?

Explanation for the first issue:
There's a difference between onCreate and onResume
onCreate: Activity launched for the first time. Here is where you may initialize your stuff.
onResume: User returns to the activity after another activity goes to the background. (onPause is called on the other activity)
For the second issue, try this:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
NavUtils.navigateUpTo(this, intent);
Instead of
NavUtils.navigateUpFromSameTask(this);

Related

Android: Data passed with intent disappears when coming back from deeper hierarchy

I have an OverviewActivity that contains a listview. When an item is selected, an intent is created to move to the DetailActivity and I pass an int with it.
This int is assigned to a private variable and is used to query the database.
DetailActivity code:
private int mIssueId;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_issue_detail);
mIssueId = getIntent().getIntExtra(IssueOverviewFragment.INTENT_ISSUE_ID, -1);
...
}
In the DetailActivity I can go to a GraphActivity. But when I press the upButton in the GraphActivity, the application crashes because the variable became -1 in the DetailActivity (and the database can thus not be queried properly).
The hierarchy is:
OverviewActivity -> DetailActivity -> GraphActivity
GraphActivity code:
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_graph );
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
...
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_detail, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_logout: {
Utility.redirectToLogin(this);
break;
}
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
How do I retain the values of my mIssueId attribute in the DetailActivity?
The Intent intends to pass information between activities. When Activity1 pass control to Activity2 mostly related to the behavior of the activity.
If you have information you need to share across a complex hierarchy or to be available for your entire app, you can choice between Shared Preferences if you need persistence or use a Singleton class if the data only will be needed while the app is running.
This is a sample for a Singleton class keeping information to available to the entire app:
public class AppData {
private static AppData ourInstance = new AppData ();
public int score;
public static AppData getInstance () {
return ourInstance;
}
}
And how to access it:
AppData.getInstance().score = 100;
Hope it helps.
It looks like you are getting as a default value because there was an issue getting the intent in DetailActivity. You could try breaking up your request a little with
mIssueId = getIntent().getExtras().getInt();
But I think the issue is probably with how you are putting the int into the intent.
It should look something like
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra(IssueOverviewFragment.INTENT_ISSUE_ID, mIssueId);
startActivity(intent);

Is there a better way of checking how activity was finished in onDestroy than setting a flag?

I want to call foo() every time my activity is destroyed, unless it is destroyed as result of clicking on a specific menu option (that eventually calls finish()). Currently I do this by calling foo() on default in onDestroy, unless a FLAG is set to true, where FLAG is set when I intercept the click on the menu option.
Is there a better way of doing this than setting a flag? Perhaps some way I can attach a tag to Android's finish() so that I can see the reason? Normally I would just try to call foo where it applies, but I can't account for every reason an activity might be destroyed.
Further caveat is that I would prefer not to make changes to base class (RootActivity)
public abstract class RootActivity extends Activity{
private flag someCondition;
#Override
protected void onCreate(Bundle savedInstanceState){
//...
}
// ...
public void startJob(JobAction.Id jobaction){
if (!jobaction.someCondition){
return;
}else{
startSomeLongAsynchronousJob(someCondition);
finish(); //If this is why onDestroy happened in subclass, I dont want to call foo()
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
//...
startJob(JobAction.SOMEENUM); //Startjob is being called in the superclass
}
//...
}
public class SpecificJob extends SomeClassThatExtendsRoot{
private boolean FLAG = false;
#Override
public void onCreate(Bundle bundle){
super.onCreate(bundle);
//...
}
// ... some code ...
#Override
public onDestroy(){
if (!FLAG){ //Check if it was finish() that did this
foo();
}
super.onDestroy();
}
#Override
onOptionsItemSelected(MenuItem item){
super.onOptionsItemSelected(item);
if (item.getItemId()==r.id.DONTCALLFOO){
flag=true;
}
}
}
You have to override onSaveInstanceState in your activity.
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.d("ApplicationFlow","onSaveInstanceState was called. System destroy your activity");
foo();
super.onSaveInstanceState(outState);
}
It is always called when the activity is destroyed by the system, and not by you (when you call finish()).
The purpose is give to user a chance to save some state in Bundle outState parameter. This bundle will be passed to onCreate(Bundle savedInstanceState) to the user restore the state, when the activity is going to be recreated.
See documentation

clicking 'back' on PreferenceScreen closes current activity

I have an activity that opens PreferenceScreen. When I click 'back' - I expect that the preference screen will be closed and I'll go back to the activity, but instead - the current activity is closed and I go back to the previous activity. How can I fix that?
public class MyActivity extends Activity {
//....
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return (new Helper()).onOptionsItemSelected_menu(item,this,mFragmentManager);
}
}
public class Helper {
// ....
public boolean onOptionsItemSelected_menu(MenuItem item, Activity activity, FragmentManager mFragmentManager)
{
switch (item.getItemId()) {
case R.id.action_settings:
MenuHelper.settings(activity, mFragmentManager);
return true;
default:
return onOptionsItemSelected_menu(item, activity, mFragmentManager);
}
}
public static void settings(Activity activity, FragmentManager mFragmentManager) {
FragmentTransaction mFragmentTransaction = mFragmentManager
.beginTransaction();
PrefsFragment mPrefsFragment = new PrefsFragment(activity);
mFragmentTransaction.replace(android.R.id.content, mPrefsFragment);
mFragmentTransaction.commit();
}
public static class PrefsFragment extends PreferenceFragment {
Activity m_activity;
public PrefsFragment(Activity activity)
{
m_activity = activity;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
Daniel's answer is totally correct, though you can achieve same thing just by adding mFragmentTransaction.addToBackStack(null) right before transaction commit.
It looks like your issue is that you're replacing the fragment in the current activity with your Preferences screen.
You will have to re-factor your code a bit, but the key is to open a new Activity for your Preferences screen instead of replacing the fragment in the current Activity. This will add a new Activity to the Back Stack for the Preferences screen, and when you click back, it will pop the Preferences Activity from the Back Stack and return you to the previous Activity as you desire.
Edit: Don't re-factor your code, just do what #GnoX suggests.
For more info, see this guide: http://developer.android.com/guide/components/tasks-and-back-stack.html

Not able to restore saved instance of activity

Here is my GamePlay Activity code
public class GamePlay extends Activity implements OnClickListener {
private boolean disableSound = false;
//.....
//Code Code
//.....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//.....
//Code Code
//.....
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putBoolean("disableSound", disableSound);
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
disableSound = savedInstanceState.getBoolean("disableSound");
Menu menu = (Menu)findViewById(R.menu.tic_tac_toe);
MenuItem toggleSoundItemMenu = menu.findItem(R.id.toogle_sound_menu);
if(disableSound)
toggleSoundItemMenu.setTitle(R.string.toggle_sound_off_label);
else
toggleSoundItemMenu.setTitle(R.string.toggle_sound_on_label);
}
//other functions and code
}
Now on game restart I am restarting the activity. Following code is inside the onClickListener() withing appropriate case
case R.id.game_play_restart_button:
Intent restartActivity = new Intent(this,GamePlay.class);
finish();
startActivity(restartActivity);
break;
But still the state does not persist. I disable the sound and restart the game then sound turns back on which is the default behavior. What am I missing? Any suggestion is appreciated.
The savedInstanceState bundle is kept by the system as long as the activity hasn't been destroyed by the system.
When you call finish, you destroy the current activity, and the bundle that comes with it.
That's the reason why you can't get your boolean back.
You should consider passing this boolean as an extra in the intent like:
restartActivity.putExtra("disableSound", disableSound)
And then on the onCreate of your activity:
getIntent().getBooleanExtra("disableSound", false)
Please note that the last parameter false is just a default value. You can set it to true if that's the behaviour you want.

Multiple activity instances problem

In my application I have an activity class A that has a listview with a cursor adapter.
From A I can go to the activity B, by pressing a button. From B I can go back to A by pressing a button (not by pressing the BACK button). This means that a new instance of the A activity is created.
From this point, if I press the BACK key, the current A activity is destroyed and B is popped. And if I press BACk again the initial A activity is popped. I hope it is clear.
My problem is that when the second A activity is destroyed, the database connection is reseted, in a static manner. So in the end, when the initial A activity is displayed, the listview will be empty.
My question is: should I try to have a single instance for the A activities, or shoud I change the database connection (to link it with the activity instance)?
Thanks a lot
Gratzi
First Of All In class A which is carrying your ListView . on clicking any Listview call the startActivity method for the Class B Activity without calling any finish().
I hope which is you are already doing.
Now in the Second Activity The button (Not the Back Button) you are using for calling Activity A . in its clickListener for calling Activity A dont call the startActivity(intentForA) instead call the finish(); for ending the Activity B. this will resume the A activity which is paused..
I hope this will help
You will need to create 3 Activities rather than 2.
Have a MAIN activity that does not really display anything.
So You have Activity A that is your main activity that can handle the connection to the DB etc.
Then Activity B and C can be the A and B that you have used.
Activity A (Main activity) can have a static instance of itself so you can refernce it's
Variables etc -OR- you can pass data from one activity to the other using Intent.put, etc.
I prefer the global static instance way as I'm a little old school on Java.
Edit:
Forgot to mention, to handle the 'closing' of the app, either Activity B or C must also close Activity.
public class ActivityA extends Activity {
ActivityA act_a_instance;
public int some_integer = 22;
#Override
public void onCreate(Bundle savedInstanceState) {
act_a_instance = this;//Now you can reference this Activity outside
//Your creation stuff etc
}
}
public class ActivityB extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
//Your creation stuff etc
//Reference stuff from ActivityA like so :
int temp_integer = ActivityA.act_a_instance.some_integer;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.options_back:
startActivity(new Intent(this, ActivityC.class));
break;
}
}
#Override
protected void onStop() {
finish();
super.onStop();
}
}
public class ActivityB extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
//Your creation stuff etc
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.options_back:
startActivity(new Intent(this, ActivityB.class));
break;
}
}
#Override
protected void onStop() {
finish();
super.onStop();
}
}
Use below code hope this will solve your problem
Intent i = new Intent(B.this, A.class);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);

Categories

Resources