Comment puis-je revenir correctement à une activité parentale?

179

J'ai 2 activités (A et B) dans mon application Android et j'utilise une intention pour passer de l'activité A à l'activité B. L'utilisation de parent_activity est activée:

 <activity
        android:name=".B"
        android:label="B" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app_name.A" />
  </activity>

J'utilise également un thème qui fournit un bouton UP.

Ainsi, après avoir appelé l'activité, BI peut utiliser le bouton UP pour revenir à l'activité A. Le problème est que l'application semble appeler à nouveau la fonction onCreate () de l'activité A et ce n'est pas le comportement dont j'ai besoin. J'ai besoin que l'activité A ressemble à ce qu'elle était avant d'appeler l'activité B.

Y a-t-il un moyen d'y parvenir?

Merci d'avance

ÉDITER:

Je n'ai écrit aucun code pour démarrer l'activité B à partir de l'activité A. Je pense qu'elle est générée automatiquement par eclipse.

La classe B ressemble à:

    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_b, menu);
    return true;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpFromSameTask(this);
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Ashiaka
la source
Postez votre code, pour démarrer l'activité A à partir de B ..
user370305
Si je vous comprends bien, vous pouvez utiliser startActivityForResult () et renvoyer un resultCode ou quelque chose.
Law Gimenez
Veuillez mettre à jour votre réponse correcte étiquetée! La bonne réponse vient de LorenzCK - pas de l'utilisateur ......! Marquer ceci comme correct est trompeur et rend encore plus de programmeurs une mauvaise compréhension de la navigation par opposition à la navigation arrière!
Zordid
Gee, tant de mauvaises réponses ici, pourriez-vous s'il vous plaît aider à nettoyer ça ...?
Zordid
@ashiaka - La réponse correcte selon la conception de votre code est mise à jour.
user370305

Réponses:

334

Vous avez déclaré l'activité A avec la norme launchModedans le manifeste Android. Selon la documentation , cela signifie ce qui suit:

Le système crée toujours une nouvelle instance de l'activité dans la tâche cible et y achemine l'intention.

Par conséquent, le système est obligé de recréer l'activité A (c'est-à-dire l'appel onCreate) même si la pile de tâches est gérée correctement.

Pour résoudre ce problème, vous devez modifier le manifeste, en ajoutant l'attribut suivant à la déclaration d'activité A:

android:launchMode="singleTop"

Remarque: l' appel finish()(comme suggéré comme solution précédemment) ne fonctionne que lorsque vous êtes complètement sûr que l'instance d'activité B à laquelle vous mettez fin réside au-dessus d'une instance de l'activité A. Dans des flux de travail plus complexes (par exemple, lancer l'activité B à partir d'une notification) cela peut ne pas être le cas et vous devez lancer correctement l'activité A à partir de B.

LorenzCK
la source
11
Voici l'explication tirée de la documentation Android, Les modes "standard" et "singleTop" diffèrent l'un de l'autre sur un seul point: chaque fois qu'il y a une nouvelle intention pour une activité "standard", une nouvelle instance de la classe est créée pour répondre à cette intention. Chaque instance gère un seul intent. De même, une nouvelle instance d'une activité "singleTop" peut également être créée pour gérer une nouvelle intention. Cependant, si la tâche cible a déjà une instance existante de l'activité en haut de sa pile, cette instance recevra le nouvel intent (dans un appel onNewIntent ()); une nouvelle instance n'est pas créée.
kpsfoo
Notez que selon la documentation navigateUpFromSameTask(...) devrait ajouter FLAG_ACTIVITY_CLEAR_TOPà upIntent(via navigateUpTo(...)ce qui devrait entraîner le même comportement (selon ce guide ), mais le drapeau n'est jamais défini - et donc cette réponse a du sens
Anigif
82

Réponse mise à jour: conception de la navigation vers le haut

Vous devez déclarer quelle activité est le parent approprié pour chaque activité. Cela permet au système de faciliter les modèles de navigation tels que Up car le système peut déterminer l'activité parent logique à partir du fichier manifeste.

Donc pour cela vous devez déclarer votre activité parente dans la balise Activité avec attribut

android:parentActivityName

Comme,

<!-- The main/home activity (it has no parent activity) -->
    <activity
        android:name="com.example.app_name.A" ...>
        ...
    </activity>
    <!-- A child of the main activity -->
    <activity
        android:name=".B"
        android:label="B"
        android:parentActivityName="com.example.app_name.A" >
        <!-- Parent activity meta-data to support 4.0 and lower -->
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.app_name.A" />
    </activity>

Avec l'activité parent déclarée de cette façon, vous pouvez naviguer vers le parent approprié comme ci-dessous,

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    // Respond to the action bar's Up/Home button
    case android.R.id.home:
        NavUtils.navigateUpFromSameTask(this);
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Ainsi, lorsque vous appelez NavUtils.navigateUpFromSameTask(this);cette méthode, elle termine l'activité en cours et démarre (ou reprend) l'activité parent appropriée. Si l'activité parent cible se trouve dans la pile arrière de la tâche, elle est avancée comme défini par FLAG_ACTIVITY_CLEAR_TOP.

Et pour afficher le bouton Haut, vous devez déclarer setDisplayHomeAsUpEnabled():

@Override
public void onCreate(Bundle savedInstanceState) {
    ...
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

Ancienne réponse: (sans navigation vers le haut, navigation arrière par défaut)

Cela ne se produit que si vous recommencez l'activité A à partir de l'activité B.

Utilisation startActivity().

Au lieu de cela, à partir de l'activité A, démarrez l'activité B en utilisant startActivityForResult()et remplacez-la onActivtyResult()dans l'activité A.

Maintenant, dans l'activité B, appelez simplement finish()le bouton Up. Alors maintenant, vous êtes dirigé vers l'activité A onActivityResult()sans créer à nouveau l'activité A.

user370305
la source
Je ne sais pas où startActivity () dans l'activité B est appelée. J'ai posté le code source de l'activité B ...
ashiaka
18
Au lieu d' NavUtils.navigateUpFromSameTask(this); écrire en ligne de code finish().
user370305
4
Cela soulève la question, comment se fait-il que Google ne mentionne rien sur finish()ou startActivityForResult()dans sa documentation sur la navigation ( developer.android.com/design/patterns/navigation.html )?
wired00
cela ressemble à une solution de contournement laborieuse. La réponse @LorenzCK semble être bien meilleure.
carmen_munich
Merci beaucoup d'utiliser NavUtils.navigateUpFromSameTask (this). Gros plus! J'ai cherché une méthode comme celle-ci pour remplacer finish () qui ne retourne pas à l'activité parent dans certaines circonstances. finish () revient essentiellement à l'activité précédente qui n'est pas nécessairement l'activité parent.
Hong
22

J'avais à peu près la même configuration conduisant au même comportement indésirable. Pour moi, cela a fonctionné: ajouter l'attribut suivant à une activité A dans le Manifest.xmlde mon application:

android:launchMode="singleTask"

Consultez cet article pour plus d'explications.

androidnewbie
la source
1
De mon point de vue, la bonne solution au problème. Cela se comportera comme si vous appeliez finish () si l'activité existe dans la pile de tâches. Si ce n'est pas le cas, il sera créé et démarré.
Johan
5
Ce n'est pas la bonne façon car cela créera une nouvelle tâche inutilement à chaque fois, vous devriez plutôt utiliser le launchMode = "singleTop".
kpsfoo
19

Bien qu'il s'agisse d'une vieille question, voici une autre solution (à mon avis la plus propre et la meilleure) car toutes les réponses précédentes n'ont pas fonctionné pour moi depuis que j'ai intégré l'activité B à partir d'un widget .

public void navigateUp() {
final Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
    Log.v(logTag, "Recreate back stack");
        TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
  } else {
    NavUtils.navigateUpTo(this, upIntent);
  }
}

[ https://stackoverflow.com/a/31350642/570168 ]

Mais voir aussi: https://speakerdeck.com/jgilfelt/this-way-up-implementing-effective-navigation-on-android

Tobias
la source
7

Une meilleure façon d'y parvenir est d'utiliser deux choses: appelez:

NavUtils.navigateUpFromSameTask(this);

Maintenant, pour que cela fonctionne, vous devez avoir votre fichier manifeste indiquant que l'activité A a une activité parente B. L'activité parente n'a besoin de rien. Dans la version 4 et au-dessus, vous obtiendrez une belle flèche de retour sans effort supplémentaire (cela peut également être fait sur les versions inférieures avec un peu de code, je vais le mettre ci-dessous) Vous pouvez définir ces données dans l'onglet manifest-> application dans l'interface graphique (faites défiler jusqu'au nom de l'activité parent et mettez-le à la main)

Nœud de support:

si vous souhaitez prendre en charge la version inférieure à la version 4, vous devez également inclure des métadonnées. faites un clic droit sur l'activité, ajoutez-> métadonnées, nom = android.support.PARENT_ACTIVITY et value = your.full.activity.name

pour obtenir également la belle flèche dans les versions inférieures:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

veuillez noter que vous aurez besoin de la version 7 de la bibliothèque de support pour que tout fonctionne, mais cela en vaut la peine!

Donald
la source
5

Ce qui a fonctionné pour moi, c'est d'ajouter:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case android.R.id.home:
                onBackPressed();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        finish();
    }

à TheRelevantActivity.javaet maintenant il fonctionne comme prévu

et ouais n'oubliez pas d'ajouter:

getSupportActionbar.setDisplayHomeAsUpEnabled(true);dans la onCreate()méthode

Hammad Nasir
la source
4

J'ai essayé android:launchMode="singleTask", mais cela n'a pas aidé. A travaillé pour moi en utilisantandroid:launchMode="singleInstance"

Pnémonique
la source
L'ajout d'android: launchMode = "singleInstance" à l'activité des parents a résolu le problème pour moi
émir
4

Ajouter à la réponse de @ LorenCK, changer

NavUtils.navigateUpFromSameTask(this);

au code ci-dessous si votre activité peut être initiée à partir d'une autre activité et que cela peut faire partie d'une tâche démarrée par une autre application

Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
    TaskStackBuilder.create(this)
            .addNextIntentWithParentStack(upIntent)
            .startActivities();
} else {
    NavUtils.navigateUpTo(this, upIntent);
}

Cela lancera une nouvelle tâche et démarrera l'activité parent de votre activité que vous pouvez définir dans le manifeste comme ci-dessous de la version Min SDK <= 15

<meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.app_name.A" />

Ou en utilisant parentActivityNamesi son> 15

Sourabh
la source
Remarque: déclarez parentActivityNamepour SDK> 15 sinon cela vous donnera des plantages aléatoires
Sourabh
3

J'ai eu un problème similaire en utilisant Android 5.0 avec un nom d'activité parent incorrect

<activity
        android:name=".DisplayMessageActivity"
        android:label="@string/title_activity_display_message"
        android:parentActivityName=".MainActivity" >
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value="com.example.myfirstapp.MainActivity" />
    </activity>

J'ai supprimé com.example.myfirstapp du nom de l'activité parent et cela a fonctionné correctement

Patrick Bergthold
la source
2

Ajoutez à votre activité des informations de manifeste avec l'attribut

android:launchMode="singleTask"

fonctionne bien pour moi

Pravesh Kumar
la source
1
Le flux Comlete est comme ça @Override public boolean onOptionsItemSelected (MenuItem item) {switch (item.getItemId ()) {case android.R.id.home: NavUtils.navigateUpFromSameTask (this); retourne vrai; } return super.onOptionsItemSelected (élément); }
Pravesh Kumar
Ajoutez à votre manifeste <activity android: name = ". MainActivity" android: label = "@ string / title_activity_main"> </activity> <activity android: name = ". CreateEventActivity" android: label = "@ string / title_activity_create_event" android : launchMode = "singleTask" android: parentActivityName = ". MainActivity"> <meta-data android: name = "android.support.PARENT_ACTIVITY" android: value = ". MainActivity" /> </activity>
Pravesh Kumar
2

En classe Java: -

    toolbar = (Toolbar) findViewById(R.id.apptool_bar);
    setSupportActionBar(toolbar);

    getSupportActionBar().setTitle("Snapdeal");

    getSupportActionBar().setHomeButtonEnabled(true);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

Dans le manifeste: -

<activity
            android:name=".SubActivity"
            android:label="@string/title_activity_sub"
            android:theme="@style/AppTheme" >
            <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"></meta-data>
    </activity>

Cela vous aidera

Kasthuri Shravankumar
la source
1
    @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);

comme une presse arrière

Dan Alboteanu
la source
1

essaye ça:

Intent intent;
@Override
public void onCreate(Bundle savedInstanceState) {
    intent = getIntent();   
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_b);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_b, menu);
    return true;
}  

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpTo(this,intent);
            return true;
    }
    return super.onOptionsItemSelected(item);
}
Luis Valadez
la source
0

Aller dans mon manifeste et ajouter android:launchMode="singleTop" à l'activité a fait l'affaire pour moi.

Cela a spécifiquement résolu mon problème car je ne voulais pas qu'Android crée une nouvelle instance de l'activité précédente après avoir frappé le Up bouton dans la barre d'outils - je voulais plutôt utiliser l'instance existante de l'activité précédente lorsque je suis monté dans la hiérarchie de navigation.

Référence: android: launchMode

mumush
la source