Android
Zmiany orientacji
Szukaj…
Uwagi
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)