Como posso retornar a uma atividade pai corretamente?

179

Eu tenho 2 atividades (A e B) no meu aplicativo Android e uso a intenção de passar da atividade A para a atividade B. O uso da parent_activity está ativado:

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

Eu também uso um tema que fornece um botão PARA CIMA.

Então, depois que chamei a atividade, o BI pode usar o botão UP para voltar à atividade A. O problema é que o aplicativo parece chamar a função onCreate () da atividade A novamente e esse não é o comportamento de que eu preciso. Preciso da atividade A para ter a mesma aparência antes de ligar para a atividade B.

Existe uma maneira de conseguir isso?

desde já, obrigado

EDITAR:

Não escrevi nenhum código para iniciar a atividade B a partir da atividade A. Acho que é gerado automaticamente pelo eclipse.

A classe B se parece com:

    @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
fonte
Publique seu código, para iniciar a Atividade A a partir de B ..
user370305
Se eu entendi direito, você pode usar startActivityForResult () e retornar um resultCode ou algo assim.
Lei Gimenez
Atualize sua resposta correta marcada! A resposta CORRETA é proveniente do LorenzCK - não do usuário ......! Marcar isso como correto é enganoso e faz com que ainda mais programadores entendam mal a navegação em vez da navegação traseira!
Zordid 25/03
Puxa, tantas respostas erradas aqui, você poderia ajudar a limpar isso ...?
Zordid 25/03
@ashiaka - A resposta correta, conforme o design do seu código, é atualizada.
user370305

Respostas:

334

Você declarou a atividade A com o padrão launchModeno manifesto do Android. De acordo com a documentação , isso significa o seguinte:

O sistema sempre cria uma nova instância da atividade na tarefa de destino e direciona a intenção para ela.

Portanto, o sistema é forçado a recriar a atividade A (ou seja, chamada onCreate), mesmo que a pilha de tarefas seja manipulada corretamente.

Para corrigir esse problema, você precisa alterar o manifesto, adicionando o seguinte atributo à declaração de atividade A:

android:launchMode="singleTop"

Nota: a chamada finish()(como sugerida como solução antes) funciona apenas quando você tem certeza absoluta de que a instância da atividade B que você está encerrando vive sobre uma instância da atividade A. Em fluxos de trabalho mais complexos (por exemplo, iniciando a atividade B a partir de uma notificação) pode não ser esse o caso e você deve iniciar corretamente a atividade A de B.

LorenzCK
fonte
11
Aqui está a explicação dos documentos do Android: Os modos "padrão" e "singleTop" diferem entre si em apenas um aspecto: sempre que há uma nova intenção de uma atividade "padrão", uma nova instância da classe é criada para responder a essa intenção. Cada instância lida com uma única intenção. Da mesma forma, uma nova instância de uma atividade "singleTop" também pode ser criada para lidar com uma nova intenção. No entanto, se a tarefa de destino já tiver uma instância existente da atividade no topo de sua pilha, essa instância receberá a nova intenção (em uma chamada onNewIntent ()); uma nova instância não é criada.
kpsfoo
Note-se que de acordo com a documentação navigateUpFromSameTask(...) deve ser a adição FLAG_ACTIVITY_CLEAR_TOPde upIntent(via navigateUpTo(...)que deve resultar em mesmo comportamento (de acordo com este guia ), mas a bandeira não está definido - e, portanto, esta resposta faz sentido
AniGif
82

Resposta atualizada: Projeto de navegação

Você deve declarar qual atividade é o pai apropriado para cada atividade. Isso permite que o sistema facilite padrões de navegação como Up, porque o sistema pode determinar a atividade pai lógica a partir do arquivo de manifesto.

Portanto, para isso, você deve declarar sua atividade pai na tag Activity with attribute

android:parentActivityName

Gostar,

<!-- 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>

Com a atividade pai declarada dessa maneira, você pode navegar até o pai apropriado, como abaixo,

@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);
}

Portanto, quando você chama NavUtils.navigateUpFromSameTask(this);esse método, ele finaliza a atividade atual e inicia (ou retoma) a atividade pai apropriada. Se a atividade pai de destino estiver na pilha traseira da tarefa, ela será adiantada conforme definido por FLAG_ACTIVITY_CLEAR_TOP.

E para exibir o botão Para Cima, você deve declarar setDisplayHomeAsUpEnabled():

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

Resposta antiga: (sem navegação para cima, navegação para trás padrão)

Isso acontece apenas se você estiver iniciando a Atividade A novamente a partir da Atividade B.

Usando startActivity().

Em vez disso, na Atividade A, inicie a Atividade B usando startActivityForResult()e substituindo onActivtyResult()na Atividade A.

Agora, na Atividade B, basta finish()clicar no botão Acima. Então agora você direcionou para a Atividade A onActivityResult()sem criar a Atividade A novamente ..

user370305
fonte
Não sei onde se chama startActivity () na atividade B. Eu publiquei o código fonte de atividade B ...
ashiaka
18
Em vez de NavUtils.navigateUpFromSameTask(this); escrever na linha de código finish().
User370305
4
Ele levanta a questão: como o Google não menciona nada sobre finish()ou startActivityForResult()na documentação deles sobre Navegação ( developer.android.com/design/patterns/navigation.html )?
precisa saber é o seguinte
isso soa como uma solução trabalhosa. A resposta do @LorenzCK parece ser muito melhor.
Carmen_munich
Muito obrigado por usar o NavUtils.navigateUpFromSameTask (this). Big plus! Eu estava procurando por um método como este para substituir finish () que não retorne à atividade pai em algumas circunstâncias. finish () está voltando essencialmente à atividade anterior que não é necessariamente a atividade pai.
Hong
22

Eu tinha praticamente a mesma configuração, levando ao mesmo comportamento indesejado. Para mim, isso funcionou: adicionar o seguinte atributo a uma atividade A no Manifest.xmlmeu aplicativo:

android:launchMode="singleTask"

Veja este artigo para mais explicações.

androidnewbie
fonte
1
Do meu ponto de vista, a solução correta para o problema. Isso se comportará como se você chamasse finish () se a Activity existir na pilha de tarefas. Se não for esse o caso, ele será criado e iniciado.
30413 Johan
5
Essa não é a maneira correta, pois ela criará uma nova tarefa desnecessariamente todas as vezes; você deve usar o launchMode = "singleTop".
kpsfoo
19

Embora seja uma pergunta antiga, aqui está outra (imho a mais limpa e a melhor) solução, pois todas as respostas anteriores não funcionaram para mim desde que eu aprofundei o link da Atividade B a partir de um 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 ]

Mas também veja: https://speakerdeck.com/jgilfelt/this-way-up-implementing-effective-navigation-on-android

Tobias
fonte
7

Uma maneira melhor de conseguir isso é usando duas coisas: call:

NavUtils.navigateUpFromSameTask(this);

Agora, para que isso funcione, o arquivo de manifesto declara que a atividade A tem uma atividade pai B. A atividade pai não precisa de nada. Na versão 4 e acima, você obterá uma bela seta para trás sem nenhum esforço adicional (isso pode ser feito em versões inferiores e com um pouco de código, vou colocá-lo abaixo). Você pode definir esses dados na guia manifest-> application na GUI (role para baixo até o nome da atividade pai e coloque-o manualmente)

Nó de suporte:

se você deseja oferecer suporte à versão abaixo da versão 4, também precisa incluir metadados. clique com o botão direito do mouse na atividade, adicione-> metadados, nome = android.support.PARENT_ACTIVITY e valor = your.full.activity.name

para obter a boa seta nas versões inferiores também:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

observe que você precisará da versão 7 da biblioteca de suporte para que tudo funcione, mas vale a pena!

donald
fonte
5

O que funcionou para mim foi adicionar:

    @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();
    }

para TheRelevantActivity.javae agora está funcionando como esperado

e sim, não esqueça de adicionar:

getSupportActionbar.setDisplayHomeAsUpEnabled(true);no onCreate()método

Hammad Nasir
fonte
4

Eu tentei android:launchMode="singleTask", mas não ajudou. Trabalhou para mim usandoandroid:launchMode="singleInstance"

Pnemônico
fonte
Adicionar android: launchMode = "singleInstance" à atividade dos pais resolveu o problema para mim
emir
4

Adicionando à resposta de @ LorenCK, altere

NavUtils.navigateUpFromSameTask(this);

para o código abaixo se sua atividade puder ser iniciada a partir de outra atividade e isso puder se tornar parte da tarefa iniciada por outro aplicativo

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

Isso iniciará uma nova tarefa e iniciará a atividade pai da sua atividade, que você pode definir no manifesto como abaixo da versão mínima do SDK <= 15

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

Ou usando parentActivityNamese for> 15

Sourabh
fonte
Nota: Do declarar parentActivityNamepara SDK> 15 ou então ele vai dar-lhe algumas falhas aleatórias
Sourabh
3

Eu tive um problema semelhante ao usar o Android 5.0 com um nome de atividade pai incorreto

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

Eu removi o com.example.myfirstapp do nome da atividade pai e ele funcionou corretamente

Patrick Bergthold
fonte
2

Adicione à sua informação de manifesto de atividade com atributo

android:launchMode="singleTask"

está funcionando bem para mim

Pravesh Kumar
fonte
1
O fluxo de completas é assim @Override public boolean onOptionsItemSelected (item de MenuItem) {switch (item.getItemId ()) {case android.R.id.home: NavUtils.navigateUpFromSameTask (this); return true; } retornar super.onOptionsItemSelected (item); }
Pravesh Kumar
Adicione ao seu manifesto <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

Na classe Java: -

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

    getSupportActionBar().setTitle("Snapdeal");

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

Em manifesto: -

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

Isso vai te ajudar

Kasthuri Shravankumar
fonte
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);

como uma pressão traseira

Dan Alboteanu
fonte
1

tente isto:

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
fonte
0

Indo para o meu manifesto e adicionando android:launchMode="singleTop" a atividade fez o truque para mim.

Isso resolveu meu problema especificamente porque eu não queria que o Android criasse uma nova instância da atividade anterior depois de pressionar o Up botão na barra de ferramentas. Em vez disso, queria usar a instância existente da atividade anterior quando subi na hierarquia de navegação.

Referência: android: launchMode

mumush
fonte