Android
Изменения ориентации
Поиск…
замечания
Сохранение и восстановление состояния активности
По мере того как ваше действие начинает останавливаться, система вызывает onSaveInstanceState()
поэтому ваша активность может сохранять информацию о состоянии с помощью набора пар ключ-значение. По умолчанию реализация этого метода автоматически сохраняет информацию о состоянии иерархии представлений активности, такую как текст в EditText
или позиции прокрутки ListView
.
Чтобы сохранить дополнительную информацию о состоянии вашей деятельности, вы должны реализовать onSaveInstanceState()
и добавить пары ключ-значение в объект Bundle. Например:
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);
}
}
Система вызовет этот метод до того, как действие будет уничтожено. Затем система будет вызывать onRestoreInstanceState
где мы можем восстановить состояние из пакета:
@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);
}
Состояние экземпляра также можно восстановить в стандартном методе Activity # onCreate, но это удобно делать в onRestoreInstanceState
который гарантирует, что вся инициализация выполнена, и позволяет подклассам решать, использовать ли реализацию по умолчанию. Подробнее читайте в этой статье stackoverflow .
Обратите внимание, что onSaveInstanceState
и onRestoreInstanceState
не могут быть вызваны вместе. Android вызывает onSaveInstanceState()
когда есть вероятность, что активность может быть уничтожена. Однако бывают случаи, когда onSaveInstanceState
но действие не уничтожается и в результате onRestoreInstanceState
не вызывается.
Сохранение и восстановление состояния фрагмента
Фрагменты также имеют метод onSaveInstanceState()
который вызывается, когда их состояние необходимо сохранить:
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);
}
}
Затем мы можем вытащить данные из этого сохраненного состояния в 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;
}
}
Чтобы состояние фрагмента сохранялось должным образом, мы должны быть уверены, что мы не будем излишне воссоздавать фрагмент при изменении конфигурации. Это означает быть осторожным, чтобы не повторно инициализировать существующие фрагменты, когда они уже существуют. Любые фрагменты, которые инициализируются в Activity, должны быть просмотрены тегом после изменения конфигурации:
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();
}
}
}
Это требует от нас тщательного включения тега для поиска, когда вы добавляете фрагмент в операцию внутри транзакции:
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();
}
}
}
С помощью этого простого шаблона мы можем правильно повторно использовать фрагменты и восстановить их состояние при изменении конфигурации.
Сохраняющиеся фрагменты
Во многих случаях мы можем избежать проблем при восстановлении активности путем простого использования фрагментов. Если ваши представления и состояние находятся внутри фрагмента, мы можем легко сохранить фрагмент при повторном создании действия:
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;
}
}
Этот подход позволяет уничтожить фрагмент во время жизненного цикла активности. Вместо этого они сохраняются внутри диспетчера фрагментов. Смотрите Android официальные документы для получения дополнительной информации .
Теперь вы можете проверить, не существует ли фрагмент уже тегом перед его созданием, а фрагмент сохранит его состояние при изменении конфигурации. См Handling выполнения Changes руководство для получения более подробной информации .
Фиксирование ориентации экрана
Если вы хотите заблокировать изменение ориентации экрана на любом экране (активности) вашего приложения для Android, вам просто нужно установить свойство android:screenOrientation
для <activity>
в AndroidManifest.xml :
<activity
android:name="com.techblogon.screenorientationexample.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
<!-- ... -->
</activity>
Теперь эта активность всегда отображается в режиме « портрет ».
Управление изменениями вручную
Если вашему приложению не требуется обновлять ресурсы во время определенного изменения конфигурации, и у вас есть ограничение по производительности, которое требует от вас перезапуска активности, вы можете объявить, что ваша активность сама обрабатывает сами изменения конфигурации, что предотвращает перезапуск системы деятельность.
Однако этот метод следует рассматривать как последнее средство, когда вы должны избегать перезапуска из-за изменения конфигурации и не рекомендуется для большинства приложений. Чтобы воспользоваться этим подходом, мы должны добавить узел android:configChanges
в действие в AndroidManifest.xml :
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:label="@string/app_name">
Теперь, когда одна из этих конфигураций изменяется, активность не перезапускается, а вместо этого получает вызов 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();
}
}
См. Документы « Обработка документов изменения» . Подробнее о том, какие изменения конфигурации вы можете обрабатывать в своей деятельности, см . В документации android: configChanges и классе Configuration .
Обработка AsyncTask
Проблема:
- Если после
AsyncTask
происходит поворот экрана, активность владельца уничтожается и воссоздается. - Когда
AsyncTask
завершает работу, он хочет обновить пользовательский интерфейс, который больше недействителен.
Решение:
Используя Loaders , можно легко преодолеть разрушение / отдых.
Пример:
Основная деятельность:
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;
}
}
Замечания:
Важно использовать библиотеку совместимости v4 или нет, но не используйте часть одной и части другой, так как это приведет к ошибкам компиляции. Чтобы проверить, вы можете посмотреть на импорт для android.support.v4.content
и android.content
(у вас не должно быть обоих).
Заблокировать экранирование экрана программно
Очень часто во время разработки может оказаться очень полезным блокировать / разблокировать экран устройства в определенных частях кода .
Например, при отображении диалога с информацией разработчик может захотеть заблокировать поворот экрана, чтобы предотвратить отклонение диалога и текущую активность от перестроить, чтобы разблокировать его снова, когда диалог отклонен.
Хотя мы можем добиться блокировки вращения из манифеста, выполнив:
<activity
android:name=".TheActivity"
android:screenOrientation="portrait"
android:label="@string/app_name" >
</activity>
Можно сделать это также программно, выполнив следующие действия:
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);
}
}
}
А затем вызывая следующее, чтобы соответственно блокировать и разблокировать вращение устройства
lockDeviceRotation(true)
а также
lockDeviceRotation(false)