I'm creating a Wizard-style application, where I need to keep the user data between activities A and B alive. A has a Next soft button and B has a Back soft button.
When using FLAG_ACTIVITY_REORDER_TO_FRONT I can keep the user data alive when the soft buttons are used, because each activity is reused.
But, when the user presses the Back hard button from B, B dies, due to that hard button uses finish() implicitly.
Then, I tried overriding onBackPressed in B, adding to it the same behavior as my Back soft button, thinking that the Back hard button will behave exactly like the former button (not finish B).
Now, getting back from B to A with Back hard key, everything is fine. At this point with the focus in A, when the user presses the back hard button again, the expected behavior is that the application leaves.
The problem is that expected behavior does not occur, given that B is still alive; so that overriden onBackPressed in B is still listening, and some other behavior ocurr instead.
How can I finish listening with the overriden onBackPressed in B, so that when the focus is in A the application leaves?
Thanks in advance.
Consider doing as Krylez's comment. Or you might want to use fragments. If you target SDKs which are older than 3.x, see support library.
There are sample projects inside SDK folder, which use wizard style.
Well, I could solved my problem with a natural Android solution:
Following the Krylez tip, I've stopped using FLAG_ACTIVITY_REORDER_TO_FRONT, so I don't have conflicts with the hard button anymore, and now I'm recycling the Intent which starts my wizard.
In A, I have a very common method which is called when the user presses the continue soft button to go to B. Activity A is just informative, so it doesn't need to put Intent's extras with user's data when going to B, like this:
/** Called when the user presses the Continue button*/
public void continueButtonOnClick(View view) {
Intent intent = this.getIntent();
intent.setClass(this, StepOneRegisterWizardActivity.class);
startActivity(intent);
}
When activity B starts, it always must seek if there are user's data available in Intent's extras, like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_step_one_register_wizard);
// Get the components of the content layout
usernameEditText = (EditText)findViewById(R.id.usernameEditText);
passwordEditText = (EditText)findViewById(R.id.passwordEditText);
getIntentExtras();
}
private void getIntentExtras() {
Intent intent = this.getIntent();
Bundle bundle = intent.getExtras();
if (bundle != null) {
usernameEditText.setText(bundle.getCharSequence("usernameEditText"));
passwordEditText.setText(bundle.getCharSequence("passwordEditText"));
}
}
Now, maybe from B, the user presses any back button available (soft or hard) to back to A. In this case, we need to put the user's data in Intent's extras, like this:
/** Called when the user presses the Back soft button*/
public void backButtonOnClick(View view) {
onBackPressed();
}
#Override
/** Called when the user presses the Back hard button*/
public void onBackPressed() {
finish();
Intent intent = this.getIntent();
intent.setClass(this, StepZeroRegisterWizardActivity.class);
intent.putExtra("usernameEditText", usernameEditText.getText());
intent.putExtra("passwordEditText", passwordEditText.getText());
startActivity(intent);
}
Finally, when the user presses the continue soft button again, the new Activity B will have the data that user entered las time.
I hope it helps someone.
Related
I have application and when I navigate back using Intent and startActivity(), views are null, onCreate() is called and activities are re-initialized. Why is that and how to bypass it?
I navigate back to activity like that:
#Override
public void onBackPressed() {
if (this.getClass() == XXX.class) {
Intent i = new Intent(this, YYY.class);
startActivity(i); //<-- activity restarts
return;
}
}
super.onBackPressed();
}
I use ActionbarSherlock, so I have activity with ActionBar initialization and every single activity just extends it. The way I navigate back to activity is described in this activity.
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_screen);
initUIComponents();
setListeners();
resetProgress();
}
and initUI() initializes UI.
EDIT
What I mean, how can I go back to previously created activity (not the one that is called via onBackPressed) and not recreate it? I use startActivity(), but apparently it recreates the whole thing
If you want that when you press back, you want to show the previous screen, then you don't have to do it in your code. Android Runtime internally maintains the stack, and will take care of showing the last-shown-activity when you press back. No need to handle it via onBackPressed()
However, if you want something other than this default action, that is when you should use onBackPressed(). Else, just let Android handle it.
So, in your application, if Activity 1 calls Activity 2, and user presses back, then the default action would be to show Activity 1 again. Don't override the onBackPressed() method
Edit:
For a custom flow of activities, you'll have to build the logic yourself. You need to override onRestart() in Activity 1, and onStop() in Activity 3. That way, onCreate won't be called again. By your logic, I mean, flags to keep track of which activity you're in, checking those flags, and calling the desired activity from there.
Edit 2:
This previous SO question, answers what you need:
Android Activity management , which suggests setting the flag FLAG_ACTIVITY_REORDER_TO_FRONT on the intent, and then calling startActivity()
Check out Android activity stack management using Intent flags for other stack reordering options: Stack management
I have an Android app made of 3 activities A, B and C:
A is the application itself (a regular Activity)
B is the settings screen (made with PreferenceActivity)
C is the about screen (a regular Activity)
The relationship between the activities is as follows:
A is the main activity
B's parent is A
C's parent is B
Activity A raises a notification when going to background, this is accomplished by calling NotificationManager's notify() inside A's onStop() method.
When the user launches the app it starts from activity A, then if the user presses the home button the notification will be raised as soon as the home screen is shown. Clicking on the notification will get the user back to the app.
Everything seems to work but now there's a behaviour I tend not to like:
When the user launches the app and then goes to settings (therefore showing activity A and then B) the notification is also raised (because A has gone to background in order to show B).
This is not desired: The notification should be raised only when the application goes to background regardless of the activity the user was looking at.
How to implement this proper behaviour?
Thanks in advance,
Marco
just create a boolean flag shouldNotify and set it to false when you are opening one of your other Activities. Without your code I can't tell you exactly how to implement it but here is an example to get the idea accross:
private boolean shouldNotify = true;
#Override
public void onStart(){
super.onStart();
shouldNotify = true;
}
#Override
public void onStop(){
super.onStop();
if(shouldNotify){
sendNotification();
}
}
// Where you need to put these next snippets depends on how you've made your Activity
/*
* Launching settings activity
*/
shouldNotify = false;
Intent settingsIntent = new Intent(this, YourSettingsActivity.class);
startActivity(settingsIntent);
You could set a boolean flag which is checked before raising the notification and set it to "false" before startig Activity B.
Of course this way you would have to do the same in Activity B and C for them to raise the notification, too.
Or, as hinted to before you could implement an own application class and fetch the callback there.
I have two Activities, A and B. Here is a normal scenario: A is running, then sends an intent to B. A is paused, and B displays. When the user presses the back button from B, B us destroyed and the user is returned to A.
However, there is a case where B needs to re-create itself. To do this, I call finish() and then startActivity() on B and that works fine. But then, when I click the back button, it shows B again, and so I need to click the back button once more to get back to A.
How can I re-start B, but still be able to press the back button only once to return to A?
The following will dispose of the current activity while launching the next intent:
Intent launchNext = new Intent(getApplicationContext(), NextActivity.class);
launchNext.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(launchNext);
To override the back button, use the following:
#Override
public void onBackPressed() {
super.onBackPressed();
this.finish(); // or do something else
}
This can be solved by taking a closer look at your intent flags. Check out http://developer.android.com/reference/android/content/Intent.html and they give more information about what lifecycle you are shooting for.
Also, don't forget that you can override the back button functionality.
This may be helpful in case you want to manage your life cycle more closely.
For example, you can also make sure to go back to A if back from B. And close your app if back on A.
I am writing an InputMethodService, basically just a soft keyboard, from which I start another activity:
class Foo extends InputMethodService {
// ...
private void startNewActivity() {
Intent i = new Intent(this, NewActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
// ...
}
But when I do this, the user will be in the process of typing. For example, the user presses the 'a' key to start the new activity. Is there a way, when the user finishes the new activity, that I can return them to typing and put the keyboard back as it were?
It is up the the application in which you are typing to store the data you've typed during it's onPause() and related methods. Then the app should restore its state in onResume(). Notice neither of these can be controlled by your keyboard.
One thing you might be able to do (depending on the nature of the activity you are starting) is let the activity be a dialog. Then it will appear over the current activity, and when the dialog is closed (back button pressed or you call finish()) everything returns to how it was.
Let's consider simple DB access application with two activities:
A - list of entries from DB
B - input form to enter new data to DB, with two buttons: Save / Cancel
Application starts with A (list) and from A user may go to B (input form).
To make entering new data more efficient I created a widget to jump directly to B (PendingIntent).
The observed behaviour of the application is like that:
If the first action of the user is widget (empty back stack) => the application opens B and when user click Save or Cancel activity is finished and focus goes back to Android desktop.
If main application was started before (A is on back stack) => B is still properly opened from widget however when user click Save or Cancel focus goes back to A
The behaviour described in 2 is OK when user starts B from A. However I would like to avoid it when B is started from widget.
Any hints ?
I have a situation where I need to do something similar. My quick fix was to add a "EXTRA_LAUNCHED_BY_WIDGET" Extra to the Intent launched by the widget. Then, in my Activity I treat that as a special case.
I needed to override the Back button behaviour, but you could just as easily use this case elsewhere, e.g. in other overridden Activity methods.
#Override
public void onBackPressed()
{
Bundle extras = getIntent().getExtras();
boolean launchedFromWidget = false;
if (extras.containsKey("EXTRA_LAUNCHED_BY_WIDGET"))
{
launchedFromWidget = extras.getBoolean("EXTRA_LAUNCHED_BY_WIDGET");
}
if (launchedFromWidget)
{
// Launched from widget, handle as special case
}
else
{
// Not launched from widget, handle as normal
}
}