How to navigate up to the same parent state - android

From my observation from Gmail and TED app the behavior of up navigation it will navigate to parent with the same state (scroll position) not like what Google say in their doc Implement Up Navigation which like create a parent intent and start it.
I implement the code from Android sample code and all state are gone (All Extra parameters I have previously set and scroll position). What is the proper way on this ? I can't find any on Android document.
Below is the code:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent upIntent = new Intent(this, MyParentActivity.class);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is not part of the application's task, so create a new task
// with a synthesized back stack.
TaskStackBuilder.from(this)
.addNextIntent(new Intent(this, MyGreatGrandParentActivity.class))
.addNextIntent(new Intent(this, MyGrandParentActivity.class))
.addNextIntent(upIntent)
.startActivities();
finish();
} else {
// This activity is part of the application's task, so simply
// navigate up to the hierarchical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
return true;
}
return super.onOptionsItemSelected(item);
}
In my case I got 3 activities say A B and C, when user navigate from A to B I put some extras and onCreate of B I use that extras to query data from database to populate my rows and when I navigate back from C all extras are gone and Activity B show nothing.

The "standard" behaviour for an android activity is, that a new instance of the activity is created, every time there is a new intent for this activity (see launchMode-docu here). Because of this your extras seem to be gone, if you call navigateUpTo.
In your case, I would advise to use
android:launchMode="singleTop"
for your parent activity in your AndroidManifest.xml. This way you will return to your existing activity (as long as it is on the top of the back stack of your task). This way your extras will be preserved.
I too, do not understand why this is not mentioned in the Google doc you cite, as this seems the behaviour one expects if using up-navigation.

This is an alternative solution to the accepted answer:
If you cannot change the launchMode of your activity or if the parent activity is not on top of the back stack (e.g. A is parent of C), you cannot use the solution above. In this case you have to extend your navigateUpTo call to tell the activity, that it should not be recreated, if it is on the back stack:
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
NavUtils.navigateUpTo(this, intent);

I had a similar issue when I called startActivityForResult() in my main activity with fragments and then tried to return to it from the callee using Up navigation.
Solved by implementing Up navigation as:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
setResult(RESULT_CANCELED);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
In this case Up button behaves like an ordinary Back button and all states are preserved.

you can use this :
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}

You would need to save the state on the parent activity and recover it after returning from the calling one.
See Saving Android Activity state using Save Instance State for a complete explanation of the preocess, with code.

Related

How to prevent .setDisplayHomeAsUpEnabled from re-creating the parent activity?

When I press the "back/up" button in the toolbar to go up one level, the parent activity is re-creating itself (re-loading data, etc.) when I'd just like to finish the current activity.
Right now my activity is automatically creating the Toolbar (support Toolbar), so I'm not sure how to select it to call NavUtils.navigateUpFromSameTask(this); when it's clicked.
Is there a simple flag I can set it to tell it to just finish instead of re-creating the parent activity?
I use this trick and works, returns back to parent without re-creating the activity. The commented code recreates the parent. so i used finish()
#Override
public Intent getSupportParentActivityIntent() {
/*String from = getIntent().getExtras().getString("from");
Intent newIntent = null;
if(from.equals("MAIN")){
newIntent = new Intent(this, MainActivity.class);
}else if(from.equals("FAV")){
newIntent = new Intent(this, FavoriteActivity.class);
}
return newIntent;*/
finish();
return null;
}
in manifest delete android:parentActivityName="YourParentActivity" if you want to disable it completely
update
override onOptionItemSelected() if you want to change its behavior
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
}

Pressing back button calls onCreate (Android)

Activity A starts Activity B. In Activity B, I have this method:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpTo(this, new Intent(this,
ArticleListActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
When I press that home button, it takes me to Activity A as it should. However, onCreate is being called again. I do not want this behavior.
I'm guessing its because this implementation uses new Intent to previous item in the navigation stack. This is just code I got from Eclipse when creating the double pane project though. I looked around on stack overflow, and though it seems that using an Intent to go back is causing this behavior, I do not understand why Google would provide this in a default template.
How should I make this call differently so that onCreate is not called again when going back to Activity A?
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
just finish current activity it will take you on previous activity
if your previous activity is activity A then just use finish(); method instead of creating object of intent
As pointed out by #Rajesh you need to call onBackPressed().
Your code detects the home button press and creates a new ArticleListActivity class.However, you don't want to create a new class you only want to go back to your already created class/activity. so use onBackPressed() instead.
You can try to set the launch mode in the manifest, because the parent activity can be popped off the stack.
android:launchMode="singleTop"
In this way, the onCreate() is not called again.
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}

How can I programmatically set a parent activity in android [duplicate]

So at the moment I have an activity that can be reached from two different activities, the problem is that I can only set one activity as the parent activity in the manifest XML file. Obviously this is bad UX/UI design because the activity may send the user back to the wrong activity they were at previously and so I'm trying to dynamically set which activity is the parent activity.
The trouble is I'm not quite sure how to go about this, whether in code or XML so any pointers are appreciated. :)
For future readers here's an example of how to actually implement the official/proper solution as per the developer guides (scroll to the paragraph beginning with "This is appropriate when the parent activity may be different...").
Note that this solution assumes you are using the Support Library to implement your ActionBar and that you can at least set a 'default' parent Activity in your manifest XML file to fallback on if the Activity you are backing out of is in a 'task' that doesn't belong to your app (read the linked docs for clarification).
// Override BOTH getSupportParentActivityIntent() AND getParentActivityIntent() because
// if your device is running on API 11+ it will call the native
// getParentActivityIntent() method instead of the support version.
// The docs do **NOT** make this part clear and it is important!
#Override
public Intent getSupportParentActivityIntent() {
return getParentActivityIntentImpl();
}
#Override
public Intent getParentActivityIntent() {
return getParentActivityIntentImpl();
}
private Intent getParentActivityIntentImpl() {
Intent i = null;
// Here you need to do some logic to determine from which Activity you came.
// example: you could pass a variable through your Intent extras and check that.
if (parentIsActivityA) {
i = new Intent(this, ActivityA.class);
// set any flags or extras that you need.
// If you are reusing the previous Activity (i.e. bringing it to the top
// without re-creating a new instance) set these flags:
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
// if you are re-using the parent Activity you may not need to set any extras
i.putExtra("someExtra", "whateverYouNeed");
} else {
i = new Intent(this, ActivityB.class);
// same comments as above
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
i.putExtra("someExtra", "whateverYouNeed");
}
return i;
}
NOTE: If you do not set a default parent Activity in the manifest XML file then you'll also need to implement onCreateSupportNavigateUpTaskStack() since the system will have no idea how to generate a backstack for your task. I have not provided any example for this part.
My thoughts on the finish() type solutions
On my searching for a solution to this problem I came across a few answers that advocate the strategy of overriding onOptionsItemSelected() and intercepting the android.R.id.home button so they could simply finish() the current Activity to return to the previous screen.
In many cases this will achieve the desired behavior, but I just want to point out that this is definitely not the same as a proper UP navigation. If you were navigating to the child Activity through one of the parent Activities, then yes finish() will return you to the proper previous screen, but what if you entered the child Activity through a notification? In that case finish()ing by hitting the UP button would drop you right back onto the home screen or whatever app you were viewing before you hit the notification, when instead it should have sent you to a proper parent Activity within your app.
Like this way you can navigate dynamically to your parent activity:
getActionBar().setDisplayHomeAsUpEnabled(true);
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
NOTE: It redirects you to the activity or fragment where you came from, no matter whether it's a parent or not. Clicking on the action bar Up/Home button will just finish the current activity.
There are two concepts in play here 'Up' and 'Back'. 'Back' is the obvious one: take me to where I was just before I came here. Usually you don't need to be concerned with 'Back', as the system will handle it just fine. 'Up' is not so obvious - it's analogous to Zoom Out - from an element to the collection, from a detail to the wider picture.
Which of these fits your use case?
As per comment below: the up button pulls the destination from the android manifest, but it can be customized programmatically.
The method to override is getParentActivityIntent.
Here is my code and works perfectly fine.
#Override
public Intent getParentActivityIntent() {
Intent parentIntent= getIntent();
String className = parentIntent.getStringExtra("ParentClassSource");
Intent newIntent=null;
try {
//you need to define the class with package name
newIntent = new Intent(OnMap.this, Class.forName(className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return newIntent;
}
From the parent activities;
Intent i = new Intent(DataDetailsItem.this, OnMap.class);
i.putExtra("ParentClassSource", "com.package.example.YourParentActivity");
startActivity(i);
To find out how to use Up Navigation properly see this Android Dev Guide.
Note that there is a big flaw in the above Android Dev Guide as the NavUtils functions work differently for ICS(and lower) and JellyBean(and higher). This flaw in NavUtils is explained beautifully here.
Generally, a 'detail' type of activity will have to provide the 'up' navigation if it has nested/related contents. The 'back' navigation is handled by the system so you really don't have to worry about it.
Now for the extra effort to support the 'up' navigation, there are a few ways of doing it:
Activity that has the parent activity defined in the AndroidManifest.
Your Android Manifest
---------------------
<activity
android:name="com.example.app.DetailActivity"
android:parentActivityName="com.example.app.MainActivity" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.app.MainActivity" />
</activity>
Your Detail Activity
--------------------
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
This works well if there's only one parent activity meaning if the (DetailActivity) always gets launched from (MainActivity). Otherwise this solution will not work if (DetailActivity) gets launched from different places.
More here: http://developer.android.com/training/implementing-navigation/ancestral.html
(Easier and Recommended) Activity with Fragment and Fragment back-stack:
Your Detail Activity
--------------------
protected void replaceFragment(Bundle fragmentArguments, boolean addToBackStack) {
DetailFragment fragment = new DetailFragment();
fragment.setArguments(fragmentArguments);
// get your fragment manager, native/support
FragmentTransaction tr = fragmentManager.beginTransaction();
tr.replace(containerResId, fragment);
if (addToBackStack) {
tr.addToBackStack(null);
}
tr.commit();
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
In this solution, if the user presses 'back', the fragment will be popped from the fragment backstack and the user is taken back to the previous fragment while still remaining in the same activity. If the user presses the 'up', the activity dismisses and the user is lead back to the previous activity (being the parent activity). The key here is to use the Fragment as your UI and the activity as the host of the fragment.
Hope this helps.
You can override the Up button to act like the back button as following:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
You will need to keep track of the parent activity. One way to do this is by storing it as an extra in the intent you create to start the child activity). For example, if Activity A starts Activity B, store A's intent in the intent created for B. Then in B's onOptionsItemSelected where you handle the up navigation, retrieve A's intent and start it with the desired intent flags.
The following post has a more complex use-case with a chain of child activites. It explains how you can navigate up to the same or new instance of the parent and finish the intermediate activities while doing so.
Android up navigation for an Activity with multiple parents
Kotlin 2020
My activity launches from different activities so the AndroidManifest only works with 1 parent activity.
You can return to the previous activity like this:
supportActionBar?.setDisplayHomeAsUpEnabled(true)
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when(item!!.itemId){
android.R.id.home -> {
finish()
return true
}
}
return super.onOptionsItemSelected(item)
}

Implementing ActionBar Up button

I am implementing the Up button in the ActionBar using this method posted here:
ActionBar Up button and Navigation pattern
It works ok except in one scenario: If Activity A creates Activity B, and then I press Up it will navigate to A no problem.
However, when I get to Activity B, and then I switch to another App, then switch back to my App, and now I press the Up button, it will navigate me to the home screen instead of Activity A.
When I debug I can see that NavUtils.shouldUpRecreateTask(this, upIntent) returns false in both cases, and the upIntent is indeed Activity A for both cases as well. So not sure what the problem is.
#SuppressLint("NewApi")
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is NOT part of this app's task, so create a new task
// when navigating up, with a synthesized back stack.
TaskStackBuilder.create(this)
// Add all of this activity's parents to the back stack
.addNextIntentWithParentStack(upIntent)
// Navigate up to the closest parent
.startActivities();
} else {
// This activity is part of this app's task, so simply
// navigate up to the logical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
//finish();
return true;
} else if (itemId == R.id.wrap_menu_item) {
wrapText();
invalidateOptionsMenu();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}
Changed Activity A property from
android:launchMode="singleInstance"
to
android:launchMode="singleTask"
resolved the issue. Makes sense because A "singleInstance" activity, permits no other activities to be part of its task. It's the only activity in the task. If it starts another activity, that activity is assigned to a different task. So the only reason Up was working before was because Activity A was "underneath" the previous activity: it gave the illusion it was going back to previous activity.

Android Actionbar Up button versus system Back button

I'm using the Actionbar and it's "up" button to return from a detail activity to the main activity, which works fine. Similarly, the user can press the system "back" button to return to the main activity.
In my main activity, in onCreate() data is downloaded from the internet to display upon app start. I noticed that when I use the Actionbar "up" button to go from detail to main activity, onCreate() is run, re-downloading the data. But onCreate() is not run when I use the system "back" button, therefore immediately showing the main activity view.
The code I use in the detail activity to implement the "up" button is:
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
I would like the "up" button to behave like the "back" button and not rerun onCreate(). But I'm unsure how to make this happen, or which code path the "back" button implements to return to the main activity.
Thanks!
Instead of starting a whole new activity simply finish the details activity you are in
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
Then you will return to the previous activity on the activity stack (your main activity) and onCreate shouldn't be called
If you want Up to do exactly what Back does, you can do this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
break;
}
return super.onOptionsItemSelected(item);
}
Note that the default implementation of onBackPressed() just calls finish(), but onBackPressed can be overridden.
I think a better solution can be found in this post.
Calling finish() works in specific situations but may not always produce the behavior described in the documentation e.g:
By calling
Intent intent = NavUtils.getParentActivityIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
NavUtils.navigateUpTo(this, intent);
you'll return to the parent activity in the state in which you left it. If you have a flat app structure, it'll still act just like the Back button.
for a real "home" functionality , you should see the api demos ,under "App/Activity/Reorder Activities" .
the reason : what if you have something like this : activity1->activity2->activity3 , and now you wish to go to activity1 by pressing the home button on the action bar?
I believe the simplest way is to override the "getParentActivityIntent" method of the detail activity adding the flag "Intent.FLAG_ACTIVITY_CLEAR_TOP":
#Nullable
#Override
public Intent getParentActivityIntent() {
Intent intent = super.getParentActivityIntent();
if (intent != null) {
return super.getParentActivityIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
}
return intent;
}
There is another solution which can be in hand for somebody. I had the same double-behavior when user pressed Back and ActionbarBack buttons. I was fine with Back btn behaviour. I didn't want an activity to be recreated. So I overrode the method
#Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
Works fine for me

Categories

Resources