Jak mogę poprawnie powrócić do działania nadrzędnego?

179

Mam 2 działania (A i B) w mojej aplikacji na Androida i używam zamiaru przejścia z działania A do działania B. Korzystanie z aktywności parent_activity jest włączone:

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

Używam również motywu, który zawiera przycisk UP.

Po wywołaniu działania BI mogę użyć przycisku UP, aby wrócić do działania A. Problem polega na tym, że aplikacja wydaje się ponownie wywoływać funkcję onCreate () działania A i nie jest to zachowanie, którego potrzebuję. Potrzebuję działania A, aby wyglądało tak samo, jak wyglądało, zanim nazwałem działanie B.

Czy istnieje sposób na osiągnięcie tego?

Z góry dziękuję

EDYTOWAĆ:

Nie napisałem żadnego kodu, aby rozpocząć działanie B od działania A. Myślę, że jest ono automatycznie generowane przez zaćmienie.

Klasa B wygląda następująco:

    @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
źródło
Wyślij kod, aby rozpocząć działanie A od B ..
user370305
Jeśli dobrze cię rozumiem, możesz użyć startActivityForResult () i zwrócić wynikCode lub coś takiego.
Law Gimenez,
Zaktualizuj swoją otagowaną poprawną odpowiedź! PRAWIDŁOWA odpowiedź pochodzi od LorenzCK - nie od użytkownika ......! Oznaczenie tego jako poprawnego jest mylące i sprawia, że ​​jeszcze więcej programistów źle rozumie nawigację w przeciwieństwie do nawigacji wstecznej!
Zordid
Rany, tyle błędnych odpowiedzi tutaj, czy mógłbyś pomóc w oczyszczeniu tego ...?
Zordid
@ashiaka - Poprawna odpowiedź zgodnie z projektem kodu została zaktualizowana.
user370305

Odpowiedzi:

334

Zadeklarowałeś aktywność A ze standardem launchModew manifeście Androida. Zgodnie z dokumentacją oznacza to, co następuje:

System zawsze tworzy nową instancję działania w zadaniu docelowym i kieruje do niego zamiar.

Dlatego system jest zmuszony do odtworzenia działania A (tj. Wywołania onCreate), nawet jeśli stos zadań jest obsługiwany poprawnie.

Aby rozwiązać ten problem, musisz zmienić manifest, dodając następujący atrybut do deklaracji działania A:

android:launchMode="singleTop"

Uwaga: wywoływanie finish()(jak sugerowano wcześniej jako rozwiązanie) działa tylko wtedy, gdy masz całkowitą pewność, że wystąpienie działania B, które kończysz, działa nad wystąpieniem działania A. W bardziej złożonych przepływach pracy (na przykład uruchamianie działania B z powiadomienia) może tak nie być i musisz poprawnie uruchomić działanie A z B.

LorenzCK
źródło
11
Oto wyjaśnienie z dokumentów Androida: tryby „standardowy” i „singleTop” różnią się od siebie tylko pod jednym względem: za każdym razem, gdy pojawia się nowy zamiar dla „standardowej” aktywności, tworzona jest nowa instancja klasy, aby odpowiedzieć na ten zamiar. Każde wystąpienie obsługuje jedną intencję. Podobnie można utworzyć nowe wystąpienie działania „singleTop”, aby obsłużyć nowy zamiar. Jeśli jednak zadanie docelowe ma już instancję działania na górze stosu, instancja ta otrzyma nową intencję (w wywołaniu onNewIntent ()); nowa instancja nie jest tworzona.
kpsfoo
Zauważ, że zgodnie z dokumentacją navigateUpFromSameTask(...) należy dodawać FLAG_ACTIVITY_CLEAR_TOPdo upIntent(przez navigateUpTo(...)co powinno to skutkować takim samym zachowaniem (zgodnie z tym przewodnikiem ), ale flaga nigdy nie jest ustawiona - i dlatego ta odpowiedź ma sens
Anigif
82

Zaktualizowana odpowiedź: Up Design nawigacji

Musisz zadeklarować, które działanie jest odpowiednim rodzicem dla każdego działania. Dzięki temu system może ułatwić wzorce nawigacji, takie jak Up, ponieważ system może określić logiczną aktywność nadrzędną na podstawie pliku manifestu.

W tym celu musisz zadeklarować swoją aktywność nadrzędną w tagu Aktywność z atrybutem

android:parentActivityName

Lubić,

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

Po zadeklarowaniu w ten sposób działania nadrzędnego możesz nawigować w górę do odpowiedniego rodzica, jak poniżej,

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

Wywołanie NavUtils.navigateUpFromSameTask(this);tej metody powoduje zakończenie bieżącej czynności i rozpoczęcie (lub wznowienie) odpowiedniej czynności nadrzędnej. Jeśli docelowa aktywność nadrzędna znajduje się na tylnym stosie zadania, jest przekazywana do przodu zgodnie z definicją FLAG_ACTIVITY_CLEAR_TOP.

Aby wyświetlić przycisk Up, musisz zadeklarować setDisplayHomeAsUpEnabled():

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

Stara odpowiedź: (Bez nawigacji w górę, domyślna nawigacja wstecz)

Dzieje się tak tylko wtedy, gdy ponownie zaczynasz działanie A od działania B.

Korzystanie startActivity().

Zamiast tego z działania A rozpocznij działanie B, używając startActivityForResult()i zastępując onActivtyResult()działanie A.

Teraz w ćwiczeniu B po prostu wywołaj finish()przycisk W górę. Więc teraz skierowałeś się do działania A onActivityResult()bez ponownego tworzenia działania A.

użytkownik370305
źródło
Nie wiem, jak nazywa się startActivity () w działaniu B. Opublikowałem kod źródłowy działania B ...
ashiaka,
18
Zamiast NavUtils.navigateUpFromSameTask(this); pisać wiersz kodu finish().
user370305
4
Nasuwa się pytanie, dlaczego Google nie wspomina o finish()ani startActivityForResult()w swojej dokumentacji dotyczącej nawigacji ( developer.android.com/design/patterns/navigation.html )?
przewodowy00
to brzmi jak żmudne obejście. Odpowiedź @LorenzCK wydaje się znacznie lepsza.
carmen_munich
Wielkie dzięki za korzystanie z NavUtils.navigateUpFromSameTask (this). Duży plus! Szukałem takiej metody, która zastąpiłaby metodę finish (), która w niektórych okolicznościach nie powraca do działania nadrzędnego. Zakończ () zasadniczo powraca do poprzedniej aktywności, która niekoniecznie jest aktywnością nadrzędną.
Hong
22

Miałem prawie taką samą konfigurację, co prowadzi do tego samego niepożądanego zachowania. Dla mnie to zadziałało: dodanie następującego atrybutu do działania A w Manifest.xmlmojej aplikacji:

android:launchMode="singleTask"

Więcej informacji znajdziesz w tym artykule .

androidnewbie
źródło
1
Z mojego punktu widzenia prawidłowe rozwiązanie problemu. Będzie to zachowywać się tak samo, jak w przypadku wywołania metody finish (), jeśli działanie istnieje na stosie zadań. Jeśli tak nie jest, zostanie utworzony i uruchomiony.
Johan
5
To nie jest poprawny sposób, ponieważ niepotrzebnie utworzy nowe zadanie za każdym razem, powinieneś zamiast tego użyć launchMode = "singleTop".
kpsfoo,
19

Chociaż stare pytanie, oto inne (imho najczystsze i najlepsze) rozwiązanie, ponieważ wszystkie poprzednie odpowiedzi nie działały dla mnie, ponieważ głęboko powiązałem działanie B z widżetu .

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 ]

Ale zobacz także: https://speakerdeck.com/jgilfelt/this-way-up-implementing-effective-navigation-on-android

Tobiasz
źródło
7

Lepszym sposobem na osiągnięcie tego jest użycie dwóch rzeczy: połączenie:

NavUtils.navigateUpFromSameTask(this);

Teraz, aby to zadziałało, musisz mieć plik manifestu informujący, że działanie A ma działanie nadrzędne B. Działanie nadrzędne nie potrzebuje niczego. W wersji 4 i wyższej dostaniesz ładną strzałkę wstecz bez dodatkowego wysiłku (można to zrobić również w niższych wersjach za pomocą małego kodu, umieszczę ją poniżej). Możesz ustawić te dane na karcie manifest-> w GUI (przewiń w dół do nazwy działania rodzica i umieść go ręcznie)

Węzeł wsparcia:

jeśli chcesz obsługiwać wersję poniżej wersji 4, musisz również dołączyć metadane. kliknij aktywność prawym przyciskiem myszy, dodaj-> metadane, name = android.support.PARENT_ACTIVITY i value = your.full.activity.name

aby uzyskać ładną strzałkę również w niższych wersjach:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);

Uwaga: aby wszystko działało, potrzebujesz biblioteki pomocy technicznej w wersji 7, ale warto!

Donald
źródło
5

Dla mnie zadziałało dodanie:

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

do TheRelevantActivity.java i teraz działa zgodnie z oczekiwaniami

i tak nie zapomnij dodać:

getSupportActionbar.setDisplayHomeAsUpEnabled(true);w onCreate()metodzie

Hammad Nasir
źródło
4

Próbowałem android:launchMode="singleTask", ale to nie pomogło. Pracował dla mnie przy użyciuandroid:launchMode="singleInstance"

Pnemoniczny
źródło
Dodanie Androida: launchMode = "singleInstance" do aktywności rodzica rozwiązało dla mnie problem
emir
4

Dodając do odpowiedzi @ LorenCK, zmień

NavUtils.navigateUpFromSameTask(this);

na poniższy kod, jeśli twoja aktywność może być zainicjowana z innej aktywności, która może stać się częścią zadania rozpoczętego przez inną aplikację

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

Spowoduje to rozpoczęcie nowego zadania i rozpoczęcie działania nadrzędnego działania, które można zdefiniować w Manifeście, jak poniżej wersji Min SDK <= 15

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

Lub używając, parentActivityNamejeśli jego> 15

Sourabh
źródło
Uwaga: zadeklaruj parentActivityNameSDK> 15, bo
spowoduje
3

Miałem podobny problem podczas używania Androida 5.0 ze złą nazwą aktywności rodzica

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

Usunąłem com.example.myfirstapp z nazwy działania rodzica i działało to poprawnie

Patrick Bergthold
źródło
2

Dodaj do manifestu informacje o aktywności za pomocą atrybutu

android:launchMode="singleTask"

działa dobrze dla mnie

Pravesh Kumar
źródło
1
Kompletny przepływ jest podobny do tego: @Override public boolean onOptionsItemSelected (Element menuItem) {switch (item.getItemId ()) {case android.R.id.home: NavUtils.navigateUpFromSameTask (this); zwróć prawdę; } return super.onOptionsItemSelected (item); }
Pravesh Kumar
Dodaj do manifestu <aktywność android: nazwa = ". MainActivity" android: label = "@ string / title_activity_main"> </activity> <aktywność android: name = ". CreateEventActivity" android: label = "@ string / title_activity_create_event" android : launchMode = "singleTask" android: ParentActivityName = ". MainActivity"> <meta-dane android: name = "android.support.PARENT_ACTIVITY" android: value = ". MainActivity" /> </activity>
Pravesh Kumar
2

W klasie Java: -

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

    getSupportActionBar().setTitle("Snapdeal");

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

W Manifeście: -

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

To ci pomoże

Kasthuri Shravankumar
źródło
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);

jak prasa wsteczna

Dan Alboteanu
źródło
1

Spróbuj tego:

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
źródło
0

Przejście do mojego manifestu i dodanie android:launchMode="singleTop"do aktywności pomogło mi.

To konkretnie rozwiązało mój problem, ponieważ nie chciałem, aby Android utworzył nową instancję poprzedniej aktywności po naciśnięciu Upprzycisku na pasku narzędzi - zamiast tego chciałem użyć istniejącej instancji poprzedniej aktywności, gdy wszedłem do hierarchii nawigacji.

Odniesienie: android: launchMode

mumush
źródło