Ponowne uruchomienie aktywności po rotacji Androida


1379

W mojej aplikacji na Androida, kiedy obracam urządzenie (wysuwam klawiaturę), to mój Activityrestart jest uruchamiany ( onCreatenazywany). Prawdopodobnie tak właśnie powinno być, ale wykonuję wiele wstępnych ustawień w onCreatemetodzie, więc potrzebuję:

  1. Umieść wszystkie ustawienia początkowe w innej funkcji, aby nie wszystkie zostały utracone podczas obracania urządzenia lub
  2. Spraw, aby tak onCreatesię nie nazywało, a układ po prostu dostosowuje lub
  3. Ogranicz aplikację do samego portretu, aby onCreatesię nie nazywała.

4
W tym wpisie na blogu znajduje się dość kompletne wyjaśnienie, jak zachować długotrwałe zadania asynchroniczne podczas zmian konfiguracji działań !
Adrian Monk

3
To nie jest bezpośrednia odpowiedź, jak już inni odpowiedzieli, ale zapraszam do zapoznania się z LogLifeCycle, aby zrozumieć, co dzieje się w aplikacjach na Androida w odniesieniu do cykli życia.
Snicolas

Odpowiedzi:


965

Korzystanie z klasy aplikacji

W zależności od tego, co robisz podczas inicjalizacji, możesz rozważyć utworzenie nowej klasy rozszerzającej Applicationi przeniesienie kodu inicjalizacji na przesłoniętą onCreatemetodę w tej klasie.

public class MyApplicationClass extends Application {
  @Override
  public void onCreate() {
    super.onCreate();
    // TODO Put your application initialization code here.
  }
}

Klasa onCreatew aplikacji jest wywoływana tylko wtedy, gdy tworzona jest cała aplikacja, więc działanie uruchamia się ponownie po zmianie orientacji lub zmiany widoczności klawiatury nie powodują jej uruchomienia.

Dobrą praktyką jest ujawnianie wystąpienia tej klasy jako singletonu i ujawnianie zmiennych aplikacji, które inicjujesz za pomocą metod pobierających i ustawiających.

UWAGA: Musisz podać nazwę nowej klasy aplikacji w manifeście, aby mogła zostać zarejestrowana i użyta:

<application
    android:name="com.you.yourapp.MyApplicationClass"

Reagowanie na zmiany konfiguracji [AKTUALIZACJA: jest to przestarzałe od API 13; zobacz zalecaną alternatywę ]

Jako kolejną alternatywę możesz poprosić aplikację o wykrywanie zdarzeń, które spowodowałyby ponowne uruchomienie - takie jak zmiany orientacji i widoczności klawiatury - i obsługę ich w ramach działania.

Zacznij od dodania android:configChangeswęzła do manifestu Twojej aktywności

 <activity android:name=".MyActivity"
      android:configChanges="orientation|keyboardHidden"
      android:label="@string/app_name">

lub na Androida 3.2 (API poziom 13) i nowsze :

<activity android:name=".MyActivity"
      android:configChanges="keyboardHidden|orientation|screenSize"
      android:label="@string/app_name">

Następnie w ramach działania zastąp onConfigurationChangedmetodę i wywołanie, setContentViewaby wymusić ponowne wykonanie układu GUI w nowej orientacji.

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  setContentView(R.layout.myLayout);
}

17
Nie sądzę, aby drugie podejście działało. Próbowałem tego; jedno działanie z tekstem edycji. Napisałem tam trochę tekstu, zmień orientację i tekst zniknął / zresetował.
Ted

231
Mamy nadzieję, że w przyszłości zobaczymy metodę onRotate (). Nawet martwienie się o takie rzeczy jest - szczerze mówiąc - frustrujące.
Kelly Sutton

84
Pamiętaj, że Android Dev Guide odradza korzystanie z tego: Uwaga: android:configChangesNależy unikać używania ( ) i używania jej tylko w ostateczności. Zapoznaj się z sekcją Obsługa zmian w środowisku wykonawczym, aby uzyskać więcej informacji o tym, jak poprawnie obsługiwać ponowne uruchomienie z powodu zmiany konfiguracji. Zamiast tego, aby utrwalić dane w zdarzeniach rotacyjnych, wydaje się, że wolą używać onSaveInstanceState Bundle; lub @ Jon-O wskazuje , onRetainNonConfigurationInstance.
Jeffro

19
To złe rozwiązanie, ponieważ reaguje tylko na obecnie znane zmiany konfiguracji. W przypadku nowszych wersji Androida mogą wystąpić inne zmiany konfiguracji, których ten kod nie przechwytuje (ponieważ musi zawierać listę wszystkich zmian konfiguracji w manifeście). Rozwiązanie polegające na zapisaniu stanu onRetainNonConfigurationChangesjest bardziej odporne na uszkodzenia i proste.
Bananeweizen

16
Myślę, że powinieneś dodać tę aktualizację do wersji 3.2 do swojej odpowiedzi, jest to dość ważne (właśnie zmierzyłem się z tym problemem) i może zostać przeoczone.
bigstones

185

Aktualizacja dla Androida 3.2 i nowszych:

Uwaga : Począwszy od Androida 3.2 (poziom API 13) „rozmiar ekranu” zmienia się również, gdy urządzenie przełącza się między orientacją pionową a poziomą. Dlatego jeśli chcesz zapobiec restartowaniu środowiska wykonawczego z powodu zmiany orientacji podczas programowania dla interfejsu API poziomu 13 lub wyższego (zadeklarowanego przez atrybuty minSdkVersion i targetSdkVersion), musisz dołączyć "screenSize"wartość oprócz "orientation"wartości. Oznacza to, że musisz zadeklarować android:configChanges="orientation|screenSize". Jeśli jednak aplikacja jest kierowana na interfejs API w wersji 12 lub niższej, wówczas aktywność zawsze sama obsługuje tę zmianę konfiguracji (ta zmiana konfiguracji nie powoduje ponownego uruchomienia aktywności, nawet jeśli jest uruchomiona na urządzeniu z Androidem 3.2 lub nowszym).


1
Dziękuję za to wyjaśnienie, ponieważ powyższy komentarz prawie mnie odesłał, aby się temu przyjrzeć. Obecnie celuję w API 8, a mój kod nie ma screenSize na configChanges i mogę potwierdzić, że działa dobrze (bez zmiany orientacji) na urządzeniu, na którym działam ICS.
Carl

Dzięki za zwrócenie na to uwagi, miałem tylko Androida: configChanges = "orientacja | screenSize", a zmiana orientacji odtwarzała moją Aktywność, a przez całe życie nie mogłem zrozumieć, dlaczego!
Christopher Perry,

5
Dodanie Androida: configChanges powinien być używany tylko w ostateczności . Rozważ użycie Fragmentsi setRetainInstancezamiast tego.
Simon Forsberg,

Kluczową kwestią jest system screenSizeAndroid 3.2 i wyższy, który rozwiązał mój problem. Dziękuję!
fantouch

127

Zamiast próbować onCreate()całkowicie zwolnić odpalanie, może spróbuj sprawdzić Bundle savedInstanceState, czy zdarzenie zostało przekazane do zdarzenia, aby sprawdzić, czy jest nieważne.

Na przykład, jeśli mam jakąś logikę, która powinna być uruchomiona, gdy Activityjest naprawdę tworzona, a nie przy każdej zmianie orientacji, uruchamiam tę logikę onCreate()tylko wtedy, gdy savedInstanceStatejest ona pusta.

W przeciwnym razie nadal chcę, aby układ poprawnie przerysował odpowiednio do orientacji.

public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game_list);

        if(savedInstanceState == null){
            setupCloudMessaging();
        }
}

nie jestem pewien, czy jest to ostateczna odpowiedź, ale działa dla mnie.


6
a gdzie właściwie zapisujesz stan?
Ewoks,

5
wydaje mi się, że to działa i wydaje się być najprostszą metodą. Zauważyłem, że masz za to tylko 4 ulepszenia (5 w tym moje) w porównaniu z 373 za pomysł na zastosowanie aplikacji podklasowania, co wydaje mi się znacznie bardziej skomplikowane. czy jest jakaś wada tej metody?
steveh

4
To rozwiązanie działało dla mnie WIELKIE. udało mi się Intent serverintent = new Intent(MainActivity.this, MessageListener.class);i startService(serverintent);stworzyć serverSocket = new ServerSocket(0xcff2);i Socket client = serverSocket.accept();z BufferedReader(new InputStreamReader(client.getInputStream()));i może obracać się moja android i utrzymanie połączenia klient / serwer aktywny, jeszcze obracania GUI. Zgodnie z instrukcją, saveInstanceState zostaje zainicjowane po zamknięciu ostatniej aktywności.
Fred F

3
Nie rozumiem, co jest haczykiem? Działa to świetnie i przy znacznie mniejszej złożoności niż jakiekolwiek inne rozwiązanie.
RTF

3
To jest właściwy sposób, aby to zrobić w Androidzie. Inne sposoby łapania rotacji za pomocą configChanges i wszystkiego, co jest nieporęczne, złożone i niepotrzebne.
LukeWaggoner,

99

co ja zrobiłem...

w manifeście do sekcji aktywności dodano:

android:configChanges="keyboardHidden|orientation"

w kodzie działania zaimplementowano:

//used in onCreate() and onConfigurationChanged() to set up the UI elements
public void InitializeUI()
{
    //get views from ID's
    this.textViewHeaderMainMessage = (TextView) this.findViewById(R.id.TextViewHeaderMainMessage);

    //etc... hook up click listeners, whatever you need from the Views
}

//Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    InitializeUI();
}

//this is called when the screen rotates.
// (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
@Override
public void onConfigurationChanged(Configuration newConfig)
{
    super.onConfigurationChanged(newConfig);
    setContentView(R.layout.main);

    InitializeUI();
}

3
dla wyjaśnienia: dzięki mojej implementacji możesz teraz mieć zmienną inicjalizację w onCreate (), a onConfigurationChanged () będzie po prostu wywoływany do obracania ekranu. Twoje zmienne są teraz odizolowane od rotacji ekranu ;-) ładne i ez
Someone Somewhere

2
Zrobiłem wszystko tak, jak tu opisano, ale otrzymuję NullPointerException, gdy próbuję nacisnąć przycisk po zmianie orientacji. Co może być nie tak?
Finnboy11

5
pamiętaj, że moja odpowiedź ma 3 lata, a Android ewoluuje ... Simon - czy masz link do przykładowego kodu? Tego potrzebują ludzie.
Ktoś gdzieś

3
Ostrzegając przed Androidem: configChanges, @ SimonAndréForsberg parafrazuje dokumenty systemu Android . Obsługa zmian w środowisku wykonawczym zawiera bardziej szczegółowe informacje na temat alternatyw (w tym przykładowy kod).
Leif Arne Storset

67

Opisujesz zachowanie domyślne. Musisz samodzielnie wykryć i obsłużyć te zdarzenia, dodając:

android:configChanges

do manifestu, a następnie zmiany, które chcesz obsłużyć. Więc do orientacji użyłbyś:

android:configChanges="orientation"

a do otwierania lub zamykania klawiatury można użyć:

android:configChanges="keyboardHidden"

Jeśli chcesz obsłużyć oba, możesz po prostu je rozdzielić za pomocą polecenia potoku, takiego jak:

android:configChanges="keyboardHidden|orientation"

Spowoduje to wyzwolenie metody onConfigurationChanged w dowolnej wywoływanej czynności. Jeśli zastąpisz metodę, możesz przekazać nowe wartości.

Mam nadzieję że to pomoże.


2
@GregD Wiem, dlatego teraz jest dobry czas na aktualizację, aby odzwierciedlić dzisiejszą sytuację. Biorąc pod uwagę liczbę głosów pozytywnych na to pytanie, wciąż jest przywoływane w innych pytaniach dotyczących SO.
Simon Forsberg,

48

Właśnie odkryłem tę wiedzę:

Aby utrzymać Aktywność przy życiu przez zmianę orientacji i obsłużyć ją onConfigurationChanged, dokumentacja i przykładowy kod powyżej sugerują to w pliku manifestu:

<activity android:name=".MyActivity"
      android:configChanges="orientation|keyboardHidden"
      android:label="@string/app_name">

co ma tę dodatkową zaletę, że zawsze działa.

Dodatkowa wiedza polega na tym, że pominięcie tego keyboardHiddenmoże wydawać się logiczne, ale powoduje awarie w emulatorze (przynajmniej dla Androida 2.1): określenie tylko orientationspowoduje, że emulator będzie wywoływał zarówno, OnCreatea onConfigurationChangedczasem i tylko OnCreateraz.

Nie widziałem awarii urządzenia, ale słyszałem o awarii emulatora dla innych. Warto więc to udokumentować.


14
Uwaga: Począwszy od Androida 3.2 (poziom API 13) „rozmiar ekranu” zmienia się również, gdy urządzenie przełącza się między orientacją pionową a poziomą. Dlatego jeśli chcesz zapobiec ponownemu uruchomieniu środowiska wykonawczego ze względu na zmianę orientacji podczas programowania dla interfejsu API na poziomie 13 lub wyższym: android: configChanges = "orientacja | keyboardHidden | screenSize"
Geltrude

Tak, emulator jest do bani. Nie można na nim polegać w celu dokładnego zgłaszania zmian konfiguracji.
IgorGanapolsky

Dodanie Androida: configChanges powinien być używany tylko w ostateczności . Rozważ użycie Fragmentsi setRetainInstancezamiast tego.
Simon Forsberg,

38

Możesz także rozważyć sposób utrwalania danych platformy Android w przypadku zmian orientacji: onRetainNonConfigurationInstance()i getLastNonConfigurationInstance().

Umożliwia to utrwalanie danych podczas zmian konfiguracji, takich jak informacje uzyskane z pobierania z serwera lub czegoś innego, co zostało obliczone w przeszłości onCreatelub później, a także pozwala Androidowi zmienić układ Activityza pomocą pliku xml dla obecnie używanej orientacji .

Zobacz tutaj lub tutaj .

Należy zauważyć, że metody te są obecnie przestarzałe (choć wciąż bardziej elastyczne niż obsługa orientacji zmieniają się, jak sugeruje większość powyższych rozwiązań), z zaleceniem, że wszyscy przełączają się Fragmentsi używają setRetainInstance(true)każdego z nich, Fragmentktóry chcesz zachować.


3
Naprawdę uważam, że Fragments i setRetainInstance to najlepszy sposób (i zalecany przez Google), aby to zrobić, +1 dla ciebie i -1 dla wszystkich innych. Dodanie Androida: configChanges powinien być używany tylko w ostateczności
Simon Forsberg,

32

Podejście to jest przydatne, ale jest niepełne, gdy używa się Fragmentów.

Fragmenty są zwykle odtwarzane po zmianie konfiguracji. Jeśli nie chcesz, aby tak się stało, użyj

setRetainInstance(true); w konstruktorze (konstruktorach) fragmentu

Spowoduje to zachowanie fragmentów podczas zmiany konfiguracji.

http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)


7
Zgoda. Dzięki najnowszemu interfejsowi API Androida wydaje się, że Fragmenty są właściwym sposobem na poradzenie sobie z tym. Nie próbowałem tego jeszcze sam, ale z tego, co zebrałem czytając tę stronę , w zasadzie przenosisz 99% tego, co wykorzystałeś w działaniu do podklasy fragmentu, a następnie dodajesz ten fragment do działania. Aktywność będzie nadal niszczona i odtwarzana przy obracaniu ekranu, ale możesz konkretnie powiedzieć Androidowi, aby nie niszczył Fragmentu, używając wspomnianej setRetainInstance()metody @Abdo.
brianmearns


19

onCreateMetoda nadal nazywa nawet jeśli zmienić orientationAndroida. Przeniesienie wszystkich ciężkich funkcji do tej metody nie pomoże


18

Umieść poniższy kod w <activity>tagu Manifest.xml:

android:configChanges="screenLayout|screenSize|orientation"

17
 onConfigurationChanged is called when the screen rotates. 
 (onCreate is no longer called when screen rotates due to manifest, see:  
 android:configChanges)

Która część manifestu mówi, że „nie dzwoń onCreate()”?

Ponadto, Google Docs powiedzieć, aby uniknąć używania android:configChanges(z wyjątkiem w ostateczności) .... Ale wtedy alternatywne metody sugerują wszystkie DO wykorzystania android:configChanges.

Z mojego doświadczenia wynika, że ​​emulator ZAWSZE wzywa onCreate()do rotacji.
Ale 1-2 urządzenia, na których uruchamiam ten sam kod ... nie. (Nie jestem pewien, dlaczego miałaby istnieć różnica)


16

To bardzo proste, po prostu wykonaj następujące czynności:

<activity
    android:name=".Test"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="landscape" >
</activity>

To działa dla mnie:

Uwaga: orientacja zależy od wymagań


15

Zmiany, które należy wprowadzić w manifeście Androida:

android:configChanges="keyboardHidden|orientation" 

Dodatki, które należy wprowadzić w ramach działania, to:

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

15

Dodaj tę linię do manifestu: -

android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"

i ten fragment do działania: -

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
    }

14

Można to zrobić na kilka sposobów:

Zapisz stan aktywności

Możesz zapisać stan aktywności w onSaveInstanceState.

@Override
public void onSaveInstanceState(Bundle outState) {
    /*Save your data to be restored here
    Example : outState.putLong("time_state", time); , time is a long variable*/
    super.onSaveInstanceState(outState);
}

a następnie użyj przycisku, bundleaby przywrócić stan.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(savedInstanceState!= null){
       /*When rotation occurs
        Example : time = savedInstanceState.getLong("time_state", 0); */
    } else {
      //When onCreate is called for the first time
    }
}

Obsługuj zmiany orientacji samodzielnie

Inną alternatywą jest samodzielne radzenie sobie ze zmianami orientacji. Ale nie jest to uważane za dobrą praktykę.

Dodaj to do pliku manifestu.

android:configChanges="keyboardHidden|orientation"

dla Androida 3.2 i nowszych:

android:configChanges="keyboardHidden|orientation|screenSize"

@Override
public void onConfigurationChanged(Configuration config) {
    super.onConfigurationChanged(config);

if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        //Handle rotation from landscape to portarit mode here
    } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
        //Handle rotation from portrait to landscape mode here
    }
}

Ogranicz rotację

Możesz także ograniczyć aktywność do trybu pionowego lub poziomego, aby uniknąć rotacji.

Dodaj to do tagu aktywności w pliku manifestu:

        android:screenOrientation="portrait"

Lub zaimplementuj to programowo w swojej działalności:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

11

Sposób, w jaki znalazłem to zrobić, użyj onRestoreInstanceStatei onSaveInstanceStatezdarzeń, aby zapisać coś w Bundle(nawet jeśli dont potrzeba żadnych zmienne zapisane, wystarczy umieścić coś tam, więc Bundlenie jest pusta). Następnie w onCreatemetodzie sprawdź, czy Bundlejest pusty, a jeśli tak, to wykonaj inicjalizację, jeśli nie, to zrób to.


11

Mimo że nie jest to „sposób na Androida”, osiągnąłem bardzo dobre wyniki, samodzielnie zajmując się zmianami orientacji i po prostu zmieniając położenie widżetów w widoku, aby uwzględnić zmienioną orientację. Jest to szybsze niż jakiekolwiek inne podejście, ponieważ twoich widoków nie trzeba zapisywać i przywracać. Zapewnia również bardziej płynne wrażenia dla użytkownika, ponieważ repozycjonowane widżety są dokładnie tymi samymi widżetami, po prostu przeniesione i / lub zmienione. W ten sposób można zachować nie tylko stan modelu, ale także stan widoku.

RelativeLayoutczasami może być dobrym wyborem dla widoku, który musi się od czasu do czasu zmieniać. Udostępniasz tylko zestaw parametrów układu pionowego i zestaw parametrów układu krajobrazowego, z różnymi względnymi regułami pozycjonowania dla każdego widżetu podrzędnego. Następnie w swojej onConfigurationChanged()metodzie przekazujesz odpowiednią do setLayoutParams()wywołania każdego dziecka. Jeśli jakakolwiek kontrola dziecka musi zostać wewnętrznie zmieniona, wystarczy wywołać metodę na tym dziecku, aby wykonać zmianę orientacji. To dziecko podobnie wywołuje metody w swoich kontrolkach potomnych, które wymagają wewnętrznej zmiany orientacji i tak dalej.


Chciałbym zobaczyć przykładowy kod tego, wydaje się genialny!
Henrique de Sousa

8

Za każdym razem, gdy ekran jest obracany, otwarte działanie jest zakończone, a funkcja onCreate () jest wywoływana ponownie.

1. Możesz zrobić jedną rzecz, aby zapisać stan aktywności, gdy ekran jest obrócony, dzięki czemu możesz odzyskać wszystkie stare rzeczy, gdy funkcja onCreate () aktywności zostanie ponownie wywołana. Zobacz to link

2) Jeśli chcesz zapobiec ponownemu uruchomieniu działania, po prostu umieść następujące wiersze w pliku manifest.xml.

  <activity android:name=".Youractivity"
  android:configChanges="orientation|screenSize"/>

7

musisz użyć metody onSavedInstanceState, aby zapisać całą wartość w parametrze to has is is bundle

@Override
    public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
        super.onSaveInstanceState(outState, outPersistentState);
        outPersistentState.putBoolean("key",value);
    }

I użyć

@Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        savedInstanceState.getBoolean("key");
    } 

aby pobrać i ustawić wartość wyświetlania obiektów, obsłuży obrót ekranu


Wymaga interfejsu API na poziomie 22.
Mohammad Afrashteh,

6

Uwaga: zamieszczam tę odpowiedź, jeśli ktoś w przyszłości napotka taki sam problem jak ja. Dla mnie następujący wiersz nie był wystarczający:

android:configChanges="orientation"

Kiedy obróciłem ekran, metoda `onConfigurationChanged (Configuration newConfig) nie została wywołana.

Rozwiązanie: Musiałem także dodać „screenSize”, nawet jeśli problem dotyczył orientacji. Więc w pliku AndroidManifest.xml dodaj to:

android:configChanges="keyboardHidden|orientation|screenSize"

Następnie zaimplementuj metodę onConfigurationChanged(Configuration newConfig)




4

Ludzie mówią, że powinieneś używać

android:configChanges="keyboardHidden|orientation"

Ale najlepszym i najbardziej profesjonalnym sposobem obsługi rotacji w Androidzie jest użycie klasy Loader. To nie jest słynna klasa (nie wiem dlaczego), ale jest o wiele lepsza niż AsyncTask. Aby uzyskać więcej informacji, możesz zapoznać się z samouczkami na temat Androida, które można znaleźć w kursach na temat Androida w Udacity.

Oczywiście w inny sposób można przechowywać wartości lub widoki za pomocą onSaveInstanceState i czytać je za pomocą onRestoreInstanceState. To naprawdę zależy od ciebie.


Tak, dodajmy dodatkowe kody, aby wyglądały „profesjonalnie”. A może po prostu trzymaj się szybkiego, łatwego, prawdziwego i sprawdzonego sposobu na zrobienie tego za pomocą atrybutu configurationChanges.
AndroidDev

3

Po chwili prób i błędów znalazłem rozwiązanie, które odpowiada moim potrzebom w większości sytuacji. Oto kod:

Konfiguracja manifestu:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.pepperonas.myapplication">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

Główna aktywność:

import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";

    private Fragment mFragment;

    private int mSelected = -1;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate  " + "");

        // null check not realy needed - but just in case...
        if (savedInstanceState == null) {

            initUi();

            // get an instance of FragmentTransaction from your Activity
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

            /*IMPORTANT: Do the INITIAL(!) transaction only once!
            * If we call this everytime the layout changes orientation,
            * we will end with a messy, half-working UI.
            * */
            mFragment = FragmentOne.newInstance(mSelected = 0);
            fragmentTransaction.add(R.id.frame, mFragment);
            fragmentTransaction.commit();
        }
    }


    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged  " +
                   (newConfig.orientation
                    == Configuration.ORIENTATION_LANDSCAPE
                    ? "landscape" : "portrait"));

        initUi();

        Log.i(TAG, "onConfigurationChanged - last selected: " + mSelected);
        makeFragmentTransaction(mSelected);
    }


    /**
     * Called from {@link #onCreate} and {@link #onConfigurationChanged}
     */
    private void initUi() {
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate  instanceState == null / reinitializing..." + "");
        Button btnFragmentOne = (Button) findViewById(R.id.btn_fragment_one);
        Button btnFragmentTwo = (Button) findViewById(R.id.btn_fragment_two);
        btnFragmentOne.setOnClickListener(this);
        btnFragmentTwo.setOnClickListener(this);
    }


    /**
     * Not invoked (just for testing)...
     */
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME!!!");
    }


    /**
     * Not invoked (just for testing)...
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME, AS WELL!!!");
    }


    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume  " + "");
    }


    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause  " + "");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy  " + "");
    }


    @Override
    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.btn_fragment_one:
                Log.d(TAG, "onClick btn_fragment_one " + "");
                makeFragmentTransaction(0);
                break;

            case R.id.btn_fragment_two:
                Log.d(TAG, "onClick btn_fragment_two " + "");
                makeFragmentTransaction(1);
                break;

            default:
                Log.d(TAG, "onClick  null - wtf?!" + "");
        }
    }


    /**
     * We replace the current Fragment with the selected one.
     * Note: It's called from {@link #onConfigurationChanged} as well.
     */
    private void makeFragmentTransaction(int selection) {

        switch (selection) {
            case 0:
                mFragment = FragmentOne.newInstance(mSelected = 0);
                break;
            case 1:
                mFragment = FragmentTwo.newInstance(mSelected = 1);
                break;
        }

        // Create new transaction
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        // Replace whatever is in the fragment_container view with this fragment,
        // and add the transaction to the back stack
        transaction.replace(R.id.frame, mFragment);

        /*This would add the Fragment to the backstack...
        * But right now we comment it out.*/
        //        transaction.addToBackStack(null);

        // Commit the transaction
        transaction.commit();
    }

}

I przykładowy fragment:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

/**
 * @author Martin Pfeffer (pepperonas)
 */
public class FragmentOne extends Fragment {

    private static final String TAG = "FragmentOne";


    public static Fragment newInstance(int i) {
        Fragment fragment = new FragmentOne();
        Bundle args = new Bundle();
        args.putInt("the_id", i);
        fragment.setArguments(args);
        return fragment;
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView  " + "");
        return inflater.inflate(R.layout.fragment_one, container, false);
    }

}

Można znaleźć na github .


3

Użyj orientationnasłuchiwania, aby wykonywać różne zadania o różnej orientacji.

@Override
public void onConfigurationChanged(Configuration myConfig) 
{
    super.onConfigurationChanged(myConfig);
    int orient = getResources().getConfiguration().orientation; 
    switch(orient) 
    {
       case Configuration.ORIENTATION_LANDSCAPE:
          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    break;
       case Configuration.ORIENTATION_PORTRAIT:
          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    break;
       default:
          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }
}

3

Umieścić to poniżej kod w swoim ActivityIN Android Manifest.

android:configChanges="orientation"

Nie spowoduje to ponownego uruchomienia aktywności po zmianie orientacji.


2
@Mavamaarten Prawdopodobnie dlatego, że jak zauważyli inni, jest to zła praktyka, a dziesięć innych odpowiedzi już to omówiło.
MikkoP

3

Napraw orientację ekranu (poziomą lub pionową) w AndroidManifest.xml

android:screenOrientation="portrait" lub android:screenOrientation="landscape"

w tym celu twoja onResume()metoda nie jest wywoływana.


5
jak do diabła naprawianie czegoś jest odpowiedzią? Dlaczego nasze urządzenia mogą się obracać, jeśli blokujemy ich użytkownikom?
Reinherd

3

Jeden z najlepszych komponentów architektury Androida wprowadzony przez Google spełni wszystkie Twoje wymagania, jakim jest ViewModel.

Ma to na celu przechowywanie i zarządzanie danymi związanymi z interfejsem użytkownika w cyklu życia oraz pozwala na przetrwanie danych podczas obracania ekranu

class MyViewModel : ViewModel() {

Proszę odnieść się do tego: https://developer.android.com/topic/libraries/architecture/viewmodel


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.