Перезапуск активности при ротации Android
В моем приложении для Android, когда я поворачиваю устройство (выдвигаю клавиатуру), то мой Activity
перезапущен (onCreate
называется). Теперь, вероятно, так и должно быть, но я делаю много начальных настроек в onCreate
метод, поэтому мне нужно либо:
- Поместите все начальные настройки в другую функцию, чтобы они не терялись при вращении устройства или
- Сделай это так
onCreate
не вызывается снова и макет просто настраивается или - Ограничьте приложение только портретом, чтобы
onCreate
не называется.
35 ответов
Использование класса приложения
В зависимости от того, что вы делаете в своей инициализации, вы можете рассмотреть возможность создания нового класса, который расширяет Application
и переместить ваш код инициализации в переопределенный onCreate
метод в этом классе.
public class MyApplicationClass extends Application {
@Override
public void onCreate() {
super.onCreate();
// TODO Put your application initialization code here.
}
}
onCreate
в классе приложения вызывается только при создании всего приложения, поэтому действие перезапускается при изменении ориентации, или изменения видимости клавиатуры не будут вызывать его.
Хорошей практикой является представление экземпляра этого класса как одиночного и предоставление переменных приложения, которые вы инициализируете, с помощью методов получения и установки.
ПРИМЕЧАНИЕ. Вам необходимо указать имя вашего нового класса приложения в манифесте, чтобы он был зарегистрирован и использован:
<application
android:name="com.you.yourapp.MyApplicationClass"
Реакция на изменения конфигурации [ОБНОВЛЕНИЕ: это устарело с API 13; см. рекомендуемый вариант ]
В качестве дополнительной альтернативы вы можете заставить ваше приложение прослушивать события, которые могут вызвать перезапуск, например, изменения ориентации и видимости клавиатуры, и обрабатывать их в вашей активности.
Начните с добавления android:configChanges
узел к манифест-узлу вашей деятельности
android:configChanges="keyboardHidden|orientation"
или для Android 3.2 (уровень API 13) и новее:
android:configChanges="keyboardHidden|orientation|screenSize"
Затем в рамках действия переопределите onConfigurationChanged
метод и вызов setContentView
принудительно сделать макет графического интерфейса в новой ориентации.
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.myLayout);
}
Обновление для Android 3.2 и выше:
Внимание! Начиная с Android 3.2 (уровень API 13), "размер экрана" также изменяется, когда устройство переключается между книжной и альбомной ориентацией. Таким образом, если вы хотите предотвратить перезапуск среды выполнения из-за изменения ориентации при разработке для API уровня 13 или выше (как заявлено атрибутами minSdkVersion и targetSdkVersion), необходимо включить
"screenSize"
значение в дополнение к"orientation"
значение. То есть вы должны объявитьandroid:configChanges="orientation|screenSize"
, Однако, если ваше приложение предназначено для уровня API 12 или ниже, то ваша активность всегда сама обрабатывает это изменение конфигурации (это изменение конфигурации не перезапускает вашу активность, даже при работе на устройстве Android 3.2 или выше).
Вместо того, чтобы пытаться остановить onCreate()
от увольнения, может быть, попробуйте проверить Bundle
savedInstanceState
быть переданным в событие, чтобы увидеть, является ли оно нулевым или нет.
Например, если у меня есть какая-то логика, которая должна быть запущена, когда Activity
действительно создается, а не при каждом изменении ориентации, я запускаю эту логику только в onCreate()
только если savedInstanceState
нулевой.
В противном случае я все еще хочу, чтобы макет правильно перерисовывался для ориентации.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_list);
if(savedInstanceState == null){
setupCloudMessaging();
}
}
не уверен, что это окончательный ответ, но он работает для меня.
Что я сделал...
в манифесте к разделу активности добавлено:
android:configChanges="keyboardHidden|orientation"
В коде для деятельности реализовано:
//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();
}
То, что вы описываете, является поведением по умолчанию. Вы должны обнаружить и обработать эти события самостоятельно, добавив:
android:configChanges
в ваш манифест, а затем изменения, которые вы хотите обработать. Так что для ориентации вы бы использовали:
android:configChanges="orientation"
и для открытия или закрытия клавиатуры вы должны использовать:
android:configChanges="keyboardHidden"
Если вы хотите обработать оба, вы можете просто разделить их командой pipe, например:
android:configChanges="keyboardHidden|orientation"
Это вызовет метод onConfigurationChanged в любой операции, которую вы вызываете. Если вы переопределите метод, вы можете передать новые значения.
Надеюсь это поможет.
Я только что обнаружил это знание:
Для поддержания Деятельности через изменение ориентации и обработки ее через onConfigurationChanged
документация и пример кода выше предлагают это в файле Manifest:
android:configChanges="keyboardHidden|orientation"
который имеет дополнительное преимущество, что он всегда работает.
Знания о бонусах - это отсутствие keyboardHidden
может показаться логичным, но это вызывает сбои в эмуляторе (по крайней мере для Android 2.1): указание только orientation
заставит эмулятор вызвать оба OnCreate
а также onConfigurationChanged
иногда и только OnCreate
в других случаях.
Я не видел сбой на устройстве, но я слышал о сбое эмулятора для других. Так что стоит документировать.
Вы также можете рассмотреть возможность сохранения данных платформы при изменении ориентации на платформе Android: onRetainNonConfigurationInstance()
а также getLastNonConfigurationInstance()
,
Это позволяет вам сохранять данные при изменениях конфигурации, такие как информация, которую вы, возможно, получили из выборки сервера или что-то еще, что было вычислено в onCreate
или с тех пор, также позволяя Android изменить макет вашего Activity
используя XML-файл для ориентации в настоящее время используется.
Следует отметить, что эти методы в настоящее время устарели (хотя они по-прежнему более гибкие, чем управление изменением ориентации самостоятельно, как предполагает большинство из приведенных выше решений) с рекомендацией, чтобы все переключились на Fragments
и вместо этого использовать setRetainInstance(true)
на каждой Fragment
Вы хотите сохранить.
Подход полезен, но не является полным при использовании фрагментов.
Фрагменты обычно воссоздаются при изменении конфигурации. Если вы не хотите, чтобы это произошло, используйте
setRetainInstance(true);
в конструкторе (ах) фрагмента
Это приведет к тому, что фрагменты будут сохранены во время изменения конфигурации.
http://developer.android.com/reference/android/app/Fragment.html
Я просто добавил
android:configChanges="keyboard|keyboardHidden|orientation"
в файле манифеста и не добавил ни одного onConfigurationChanged
метод в моей деятельности.
Поэтому каждый раз, когда клавиатура выдвигается или ничего не происходит.
Put the code below inside your <activity>
отметить в Manifest.xml
:
android:configChanges="screenLayout|screenSize|orientation"
onCreate
метод по-прежнему вызывается, даже если вы измените orientation
андроид. Таким образом, перенос всех тяжелых функций на этот метод не поможет вам
Это очень просто, просто сделайте следующие шаги:
<activity
android:name=".Test"
android:configChanges="orientation|screenSize"
android:screenOrientation="landscape" >
</activity>
Это работает для меня:
Примечание: ориентация зависит от вашего требования
onConfigurationChanged is called when the screen rotates. (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
Какая часть манифеста говорит: "Не звони onCreate()
"?
Кроме того, документы Google говорят, чтобы избежать использования android:configChanges
(кроме как в крайнем случае).... Но тогда альтернативные методы, которые они предлагают, все используют android:configChanges
,
По моему опыту, эмулятор ВСЕГДА вызывает onCreate()
при вращении.
Но на 1-2 устройствах, на которых я запускаю один и тот же код... нет.
(Не уверен, почему будет какая-то разница.)
В манифесте Android необходимо внести следующие изменения:
android:configChanges="keyboardHidden|orientation"
Дополнения, которые будут сделаны внутри деятельности:
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="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"
и этот фрагмент к деятельности:-
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
Есть несколько способов сделать это:
Сохранить состояние активности
Вы можете сохранить состояние активности в 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);
}
а затем использовать bundle
восстановить состояние.
@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
}
}
Обращайтесь с изменениями ориентации самостоятельно
Другая альтернатива - справиться с изменениями ориентации самостоятельно. Но это не считается хорошей практикой.
Добавьте это в свой файл манифеста.
android:configChanges="keyboardHidden|orientation"
для Android 3.2 и более поздних версий:
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
}
}
Ограничить вращение
Вы также можете ограничить свою деятельность портретным или ландшафтным режимом, чтобы избежать вращения.
Добавьте это в тег активности в файле манифеста:
android:screenOrientation="portrait"
Или внедрите это программно в свою деятельность:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
Несмотря на то, что это не "способ Android", я получил очень хорошие результаты, самостоятельно обрабатывая изменения ориентации и просто перемещая виджеты в пределах вида, чтобы принять во внимание измененную ориентацию. Это быстрее, чем любой другой подход, потому что ваши представления не должны быть сохранены и восстановлены. Это также обеспечивает более удобное взаимодействие с пользователем, поскольку видоизмененные виджеты - это точно такие же виджеты, только что перемещенные и / или измененные. Таким образом можно сохранить не только состояние модели, но и состояние просмотра.
RelativeLayout
иногда может быть хорошим выбором для представления, которое время от времени должно переориентироваться. Вы просто предоставляете набор параметров портретного макета и набор параметров ландшафтного макета с различными правилами относительного позиционирования для каждого дочернего виджета. Тогда в вашем onConfigurationChanged()
метод, вы передаете соответствующий setLayoutParams()
позвони каждому ребенку. Если какой-либо дочерний элемент управления должен быть внутренне переориентирован, вы просто вызываете метод для этого дочернего элемента, чтобы выполнить переориентацию. Этот дочерний процесс аналогичным образом вызывает методы для любого из своих дочерних элементов управления, которые нуждаются во внутренней переориентации, и так далее.
Я нашел способ сделать это, используя onRestoreInstanceState
и onSaveInstanceState
события, чтобы сохранить что-то в Bundle
(даже если вам не нужны сохраненные переменные, просто поместите что-нибудь туда, чтобы Bundle
не пусто). Затем на onCreate
метод, проверьте, если Bundle
пусто, и если это так, то сделайте инициализацию, если нет, то сделайте это.
Every time when screen is rotated, opened activity is finished and onCreate() is called again.
1 You can do one thing save the state of activity when screen is rotated so that, You can recover all old stuff when activity's onCreate() is called again. Refer this link
2 If you want to prevent restarting of the activity just place following lines in your manifest.xml file.
<activity android:name=".Youractivity"
android:configChanges="orientation|screenSize"/>
Вам нужно использовать метод onSavedInstanceState, чтобы сохранить все значения для его параметра, который является связкой
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outPersistentState.putBoolean("key",value);
}
и использовать
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
savedInstanceState.getBoolean("key");
}
чтобы получить и установить значение для просмотра объектов, он будет обрабатывать поворот экрана
Примечание: я публикую этот ответ, если в будущем кто-то столкнется с той же проблемой, что и я. Для меня не было достаточно следующей строки:
android:configChanges="orientation"
Когда я повернул экран, метод `onConfigurationChanged(Configuration newConfig) не был вызван.
Решение: мне также пришлось добавить "screenSize", даже если проблема была связана с ориентацией. Итак, в файле AndroidManifest.xml добавьте следующее:
android:configChanges="keyboardHidden|orientation|screenSize"
Затем реализовать метод onConfigurationChanged(Configuration newConfig)
В разделе активности manifest
, добавлять:
android:configChanges="keyboardHidden|orientation"
Добавьте эту строку в манифест: android:configChanges="orientation|screenSize"
Люди говорят, что вы должны использовать
android:configChanges="keyboardHidden|orientation"
Но лучший и самый профессиональный способ справиться с вращением в Android - это использовать класс Loader. Это не известный класс (я не знаю почему), но он намного лучше, чем AsyncTask. Для получения дополнительной информации вы можете прочитать учебники по Android, которые можно найти в курсах по Android от Udacity.
Конечно, в качестве другого способа вы можете сохранить значения или представления с помощью onSaveInstanceState и прочитать их с помощью onRestoreInstanceState. Это зависит от вас на самом деле.
Один из лучших компонентов архитектуры Android, представленный Google, удовлетворит все ваши требования, а именно ViewModel.
Это предназначено для хранения и управления данными, относящимися к пользовательскому интерфейсу, в режиме жизненного цикла, а также позволит выживать данным при повороте экрана.
class MyViewModel : ViewModel() {
Пожалуйста, обратитесь сюда: https://developer.android.com/topic/libraries/architecture/viewmodel
Использование orientation
Слушатель для выполнения разных задач по разной направленности.
@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);
}
}
Исправьте ориентацию экрана (альбомную или книжную) в AndroidManifest.xml
android:screenOrientation="portrait"
или же android:screenOrientation="landscape"
для этого ваш onResume()
метод не вызывается.
Поместите этот код ниже в свой Activity
в Android Manifest
,
android:configChanges="orientation"
Это не возобновит вашу деятельность, когда вы измените ориентацию.
Вы можете использовать объект ViewModel в своей деятельности.
Объекты ViewModel автоматически сохраняются во время изменений конфигурации, поэтому данные, которые они хранят, сразу же становятся доступны для следующего действия или экземпляра фрагмента. Прочитайте больше:
https://developer.android.com/topic/libraries/architecture/viewmodel
После некоторого времени проб и ошибок я нашел решение, которое соответствует моим потребностям в большинстве ситуаций. Вот код:
Конфигурация манифеста:
<?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>
Основная деятельность:
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();
}
}
И образец фрагмента:
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);
}
}
Можно найти на GitHub.