Szukaj…


Uwagi

Odniesienie: https://guides.codepath.com/android/Handling-Configuration-Changes#references

Zapisywanie i przywracanie stanu aktywności

Gdy aktywność zaczyna się zatrzymywać, system wywołuje onSaveInstanceState() aby można było zapisać informacje o stanie z kolekcją par klucz-wartość. Domyślna implementacja tej metody automatycznie zapisuje informacje o stanie hierarchii widoku działania, takie jak tekst w widżecie EditText lub pozycja przewijania ListView .

Aby zapisać dodatkowe informacje o stanie dla twojej aktywności, musisz zaimplementować onSaveInstanceState() i dodać pary klucz-wartość do obiektu pakietu. Na przykład:

public class MainActivity extends Activity {
    static final String SOME_VALUE = "int_value";
    static final String SOME_OTHER_VALUE = "string_value";

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        // Save custom values into the bundle
        savedInstanceState.putInt(SOME_VALUE, someIntValue);
        savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
        // Always call the superclass so it can save the view hierarchy state
        super.onSaveInstanceState(savedInstanceState);
    }
}

System wywoła tę metodę przed zniszczeniem działania. Następnie system wywoła onRestoreInstanceState gdzie możemy przywrócić stan z pakietu:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);
    // Restore state members from saved instance
    someIntValue = savedInstanceState.getInt(SOME_VALUE);
    someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}

Stan instancji można również przywrócić w standardowej metodzie Activity # onCreate, ale wygodnie jest to zrobić w onRestoreInstanceState która zapewnia wykonanie całej inicjalizacji i pozwala onRestoreInstanceState zdecydować, czy użyć domyślnej implementacji. Przeczytaj ten post dotyczący przepływu stosu, aby uzyskać szczegółowe informacje.

Należy pamiętać, że nie można zagwarantować, że onSaveInstanceState i onRestoreInstanceState będą wywoływane razem. Android wywołuje onSaveInstanceState() gdy istnieje szansa, że aktywność może zostać zniszczona. Są jednak przypadki, w których onSaveInstanceState jest onSaveInstanceState ale działanie nie jest niszczone, w wyniku czego onRestoreInstanceState nie jest wywoływana.

Zapisywanie i przywracanie stanu fragmentu

Fragmenty mają również metodę onSaveInstanceState() , która jest wywoływana, gdy należy zapisać ich stan:

public class MySimpleFragment extends Fragment {
    private int someStateValue;
    private final String SOME_VALUE_KEY = "someValueToSave";
   
    // Fires when a configuration change occurs and fragment needs to save state
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt(SOME_VALUE_KEY, someStateValue);
        super.onSaveInstanceState(outState);
    }
}

Następnie możemy wyciągnąć dane z tego zapisanego stanu w onCreateView :

public class MySimpleFragment extends Fragment {
   // ...

   // Inflate the view for the fragment based on layout XML
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.my_simple_fragment, container, false);
        if (savedInstanceState != null) {
            someStateValue = savedInstanceState.getInt(SOME_VALUE_KEY);
            // Do something with value if needed
        }
        return view;
   }
}

Aby stan fragmentu został poprawnie zapisany, musimy mieć pewność, że nie będziemy niepotrzebnie odtwarzać fragmentu po zmianie konfiguracji. Oznacza to, że należy uważać, aby nie inicjować ponownie istniejących fragmentów, gdy już istnieją. Wszelkie fragmenty inicjowane w działaniu muszą zostać sprawdzone przez tag po zmianie konfiguracji:

public class ParentActivity extends AppCompatActivity {
    private MySimpleFragment fragmentSimple;
    private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState != null) { // saved instance state, fragment may exist
           // look up the instance that already exists by tag
           fragmentSimple = (MySimpleFragment)  
              getSupportFragmentManager().findFragmentByTag(SIMPLE_FRAGMENT_TAG);
        } else if (fragmentSimple == null) { 
           // only create fragment if they haven't been instantiated already
           fragmentSimple = new MySimpleFragment();
        }
    }
}

Wymaga to od nas ostrożnego dodawania tagu do wyszukiwania za każdym razem, gdy wstawiamy fragment do działania w ramach transakcji:

public class ParentActivity extends AppCompatActivity {
    private MySimpleFragment fragmentSimple;
    private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... fragment lookup or instantation from above...
        // Always add a tag to a fragment being inserted into container
        if (!fragmentSimple.isInLayout()) {
            getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.container, fragmentSimple, SIMPLE_FRAGMENT_TAG)
                .commit();
        }
    }
}

Dzięki temu prostemu wzorowi możemy poprawnie ponownie używać fragmentów i przywracać ich stan w trakcie zmian konfiguracji.

Zatrzymywanie fragmentów

W wielu przypadkach możemy uniknąć problemów przy ponownym utworzeniu działania, po prostu używając fragmentów. Jeśli Twoje poglądy i stan znajdują się w obrębie fragmentu, możemy łatwo zachować ten fragment, gdy działanie zostanie ponownie utworzone:

public class RetainedFragment extends Fragment {
    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment when activity is re-initialized
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

Takie podejście zapobiega niszczeniu fragmentu podczas cyklu życia aktywności. Zamiast tego są przechowywane w Menedżerze fragmentów. Zobacz oficjalne dokumenty Androida, aby uzyskać więcej informacji .

Teraz możesz sprawdzić, czy fragment już istnieje za pomocą znacznika przed jego utworzeniem, a fragment zachowa swój stan po zmianie konfiguracji. Aby uzyskać więcej informacji, zobacz Podręcznik obsługi zmian środowiska wykonawczego.

Orientacja ekranu blokady

Jeśli chcesz zablokować zmianę orientacji ekranu dowolnego ekranu (aktywności) aplikacji na Androida, wystarczy ustawić właściwość android:screenOrientation dla <activity> w pliku AndroidManifest.xml :

<activity
    android:name="com.techblogon.screenorientationexample.MainActivity"
    android:screenOrientation="portrait"
    android:label="@string/app_name" >
    <!-- ... -->
</activity>

Teraz aktywność musi być zawsze wyświetlana w trybie „ portretowym ”.

Ręczne zarządzanie zmianami konfiguracji

Jeśli aplikacja nie musi aktualizować zasobów podczas określonej zmiany konfiguracji i masz ograniczenie wydajności, które wymaga unikania ponownego uruchomienia działania, możesz zadeklarować, że działanie obsługuje samą zmianę konfiguracji, co uniemożliwia ponowne uruchomienie systemu czynność.

Jednak tę technikę należy uznać za ostateczność, gdy należy unikać ponownego uruchamiania z powodu zmiany konfiguracji i nie jest zalecana w przypadku większości aplikacji. Aby zastosować to podejście, musimy dodać węzeł android:configChanges do aktywności w android:configChanges AndroidManifest.xml :

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

Teraz, gdy zmienia się jedna z tych konfiguracji, działanie nie uruchamia się ponownie, ale otrzymuje wywołanie funkcji onConfigurationChanged() :

// Within the activity which receives these changes
// Checks the current device orientation, and toasts accordingly
@Override
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();
    }
}

Zobacz dokumentację Obsługa zmian . Aby uzyskać więcej informacji na temat zmian konfiguracji, które możesz obsłużyć w swojej działalności, zobacz dokumentację android: configChanges i klasę Configuration .

Obsługa AsyncTask

Problem:

  • Jeśli po uruchomieniu AsyncTask nastąpi obrót ekranu, działanie właściciela zostanie zniszczone i ponownie utworzone.
  • Po zakończeniu AsyncTask chce zaktualizować interfejs użytkownika, który może już nie być prawidłowy.

Rozwiązanie:

Korzystając z modułów ładujących , można łatwo pokonać zniszczenie / odtwarzanie aktywności.

Przykład:

Główna aktywność:

public class MainActivity extends AppCompatActivity 
        implements LoaderManager.LoaderCallbacks<Bitmap> {

    //Unique id for the loader
    private static final int MY_LOADER = 0; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        LoaderManager loaderManager = getSupportLoaderManager();

        if(loaderManager.getLoader(MY_LOADER) == null) {
            loaderManager.initLoader(MY_LOADER, null, this).forceLoad();
        }
    }

    @Override
    public Loader<Bitmap> onCreateLoader(int id, Bundle args) {
        //Create a new instance of your Loader<Bitmap>
        MyLoader loader = new MyLoader(MainActivity.this);
        return loader;
    }

    @Override
    public void onLoadFinished(Loader<Bitmap> loader, Bitmap data) {
        // do something in the parent activity/service
        // i.e. display the downloaded image
        Log.d("MyAsyncTask", "Received result: ");
    }

    @Override
    public void onLoaderReset(Loader<Bitmap> loader) {

    }
}

AsyncTaskLoader:

public class MyLoader extends AsyncTaskLoader<Bitmap> {
    private WeakReference<Activity> motherActivity;

    public MyLoader(Activity activity) {
        super(activity);
        //We don't use this, but if you want you can use it, but remember, WeakReference
        motherActivity = new WeakReference<>(activity);
    }

    @Override
    public Bitmap loadInBackground() {
        // Do work. I.e download an image from internet to be displayed in gui.
        // i.e. return the downloaded gui
        return result;
    }
}

Uwaga:

Ważne jest, aby używać albo biblioteki kompatybilności v4, albo nie, ale nie używaj części jednej i części drugiej, ponieważ doprowadzi to do błędów kompilacji. Aby to sprawdzić, możesz przejrzeć import dla android.support.v4.content i android.content (nie powinieneś mieć obu).

Zablokuj programowo obrót ekranu

Bardzo często podczas programowania bardzo przydatne może być zablokowanie / odblokowanie ekranu urządzenia podczas określonych części kodu .

Na przykład, pokazując okno dialogowe z informacjami, programista może chcieć zablokować obrót ekranu, aby zapobiec zamknięciu okna dialogowego i odbudowaniu bieżącej aktywności, aby odblokować je ponownie po zamknięciu okna dialogowego.

Mimo że możemy osiągnąć blokowanie obrotu z manifestu, wykonując:

<activity
    android:name=".TheActivity"
    android:screenOrientation="portrait"
    android:label="@string/app_name" >
</activity>

Można to również zrobić programowo, wykonując następujące czynności:

public void lockDeviceRotation(boolean value) {
    if (value) {
        int currentOrientation = getResources().getConfiguration().orientation;
        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
        }
    } else {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        }
    }
}

A następnie wywoływanie następujących czynności, aby odpowiednio zablokować i odblokować obrót urządzenia

lockDeviceRotation(true)

i

lockDeviceRotation(false) 


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow