67

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. :)

Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
Darryl Bayliss
  • 2,677
  • 2
  • 19
  • 28
  • 2
    Parent activities are used for up navigation (e.g. the up arrow in the action bar). Back navigation does not use parent activities, it uses the back stack. In your case, you should be just fine using `startActivity(ForResult)` and `finish` to manage the back stack. – Mattias Buelens Oct 04 '13 at 15:00
  • 2
    I'd like to make use of the up arrow in the action bar, so I guess what I'm really after is a way to dynamically set what activity up navigation should be referring to when touched. – Darryl Bayliss Oct 04 '13 at 15:06
  • another related question - also with no real answer: [How do I set the parent activity of an activity at runtime?](http://stackoverflow.com/questions/20378894/how-do-i-set-the-parent-activity-of-an-activity-at-runtime) – Richard Le Mesurier Jul 03 '14 at 11:22

9 Answers9

59

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.

Tony Chan
  • 8,017
  • 4
  • 50
  • 49
  • 1
    Thank you for providing an example of how this should be done - that is indeed missing from the Android guide lines. – user1419999 May 23 '15 at 03:30
  • getSupportParentActivityIntent is not firing for me in API 15 devices... any ideas? – worked Oct 10 '15 at 14:49
  • @worked it does appear based on the answer that getSupportParentActivityIntent() should only fire on devices running < API 11. – Chantell Osejo Dec 15 '16 at 21:10
  • If you wanna update your answer, the new link for android x is https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity#onCreateSupportNavigateUpTaskStack(androidx.core.app.TaskStackBuilder) – Snicolas Feb 04 '19 at 07:14
33

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.

qwerty_so
  • 35,448
  • 8
  • 62
  • 86
Saj
  • 849
  • 7
  • 12
  • It isn't exactly parent activity, but It works great. – skywall Nov 14 '14 at 10:52
  • 4
    For those reading, while `finish()` does achieve the desired behavior in many situations, it will not always result in a proper UP navigation in some cases. Please see my answer and thoughts [explaining this](http://stackoverflow.com/a/28334824/708906). – Tony Chan Feb 05 '15 at 01:50
  • The return true is a very important aspect. I had the finish but no return true. Works now. Thanks – Zapnologica Oct 14 '15 at 17:42
  • what's the diff between finish() and NavUtils.navigateUpFromSameTask – chrizonline Feb 05 '16 at 15:56
  • @nuttynibbles finish closes the current activity. It's not the proper way to use the Up Navigation. – Vincent D. Mar 08 '17 at 20:03
8

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.

Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
  • I'm more interested in handling the back behaviour, but from your description the system should automatically handle this? What about the "up" button in the action bar, does this pull its destination from the XML parent set or does it simply link back to the previous activity it came from? – Darryl Bayliss Oct 04 '13 at 15:53
  • 6
    Yup, the up button pulls the destination from the android manifest, but it can be [customized programmatically](http://developer.android.com/guide/topics/ui/actionbar.html#Home). – Tassos Bassoukos Oct 04 '13 at 15:57
  • 4
    If you're serious about your app, also read the [navigation design guide](http://developer.android.com/design/patterns/navigation.html) - keeping the flow will really help an app feel natural. Don't feed that you need to know it all by heart, just be aware that there's a Way That Things Are Done. – Tassos Bassoukos Oct 04 '13 at 16:04
  • Why is this answer voted -2 without comments ? The poster should have added an example instead of a link but the link is leading to the correct manual page. +1 – John Oct 29 '14 at 02:33
  • 5
    This answer should probably contain a nice example instead of just the link which might die in the future. But I'm giving this a +1 to bring the score back up positive where it belongs since it did lead me to a truly proper solution unlike some of the other answers given. – Tony Chan Feb 04 '15 at 01:34
  • @TonyChan ...and now the link _is_ dead. – Vicky Chijwani Jun 08 '16 at 17:15
  • @VickyChijwani It's [here](https://developer.android.com/training/appbar/index.html#Home) now, I think. – Tassos Bassoukos Jun 08 '16 at 18:58
  • @TassosBassoukos that page doesn't have any code on it at all. Are you sure that's the link you meant to post? – Vicky Chijwani Jun 08 '16 at 19:32
  • @VickyChijwani It's the training path; read it, and read the Lessons in order. – Tassos Bassoukos Jun 13 '16 at 20:20
  • @TassosBassoukos I agree with you that it's a great thing to read those guides, but this kinda defeats Stack Overflow's purpose. Lest you should misinterpret my argument: I'm not entitled, and I 100% support the practice of reading official documentation thoroughly -- it's just that answers are _meant_ to be specific, and have context around links. (Here are [the guidelines for answers](http://stackoverflow.com/help/how-to-answer), for clarification; also [this post on Meta](http://meta.stackexchange.com/a/8259/302619). I understand you're a long-time user of the site, and I mean no offense). – Vicky Chijwani Jun 13 '16 at 20:39
  • @VickyChijwani I agree with you that one learns by doing (nobody ever can learn to ride a bike in a classroom), and code is preferable than plain prose. However, in the frame of *this question*, this was not a question of code, but of architecture - the OP was confused about concepts, and there's no easy way to pass that in code (and no, I will not write a page long diatribe, I have a life and a paying job). Perhaps I'm getting senile in my old age, but I do thing that the skill of googling stuff up and noticing the keywords used is still important. – Tassos Bassoukos Jun 14 '16 at 07:59
5

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);
Michael Tarimo
  • 1,375
  • 1
  • 12
  • 12
4

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.

Community
  • 1
  • 1
kpsfoo
  • 392
  • 4
  • 18
4

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:

  1. 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

  2. (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.

Pirdad Sakhizada
  • 608
  • 7
  • 12
4

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);
}
Hasan El-Hefnawy
  • 1,249
  • 1
  • 14
  • 20
1

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

Community
  • 1
  • 1
rohans310
  • 21
  • 1
  • 4
1

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)
    }
finalpets
  • 281
  • 4
  • 6