Перезапуск активности при вращении Android
в моем приложении для Android, когда я поворачиваю устройство (выдвигаю клавиатуру), то мой Activity
перезапускается (onCreate
называется). Теперь, вероятно, так и должно быть, но я делаю много начальной настройки в onCreate
метод, поэтому мне нужно либо:
- поместите всю начальную настройку в другую функцию, чтобы она не потерялась при вращении устройства или
- сделать так
onCreate
не вызывается снова и макет просто корректирует или - ограничение приложение просто портрет, так что
onCreate
не называется.
30 ответов
использование класса Application
в зависимости от того, что вы делаете в своей инициализации, вы можете рассмотреть возможность создания нового класса, который расширяет 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"
реагирование на изменения конфигурации [UPDATE: это устарело с API 13;см. Рекомендуемый альтернатива]
в качестве дополнительной альтернативы вы можете заставить приложение прослушивать события, которые могут вызвать перезапуск – например, изменения ориентации и видимости клавиатуры – и обрабатывать их в своей деятельности.
начните с добавления android:configChanges
узел манифеста узла вашей деятельности
android:configChanges="keyboardHidden|orientation"
или Android 3.2 (уровень API 13) и новее:
android:configChanges="keyboardHidden|orientation|screenSize"
затем в пределах действия переопределить onConfigurationChanged
метод и вызов setContentView
чтобы заставить макет GUI быть повторно сделано в новой ориентации.
@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
передается в событие, чтобы увидеть, если он имеет значение null или нет.
например, если у меня есть логика, которая должна быть запущена, когда Activity
действительно создан, а не на каждом изменении ориентации, я только запускаю эту логику в onCreate()
только если savedInstanceState
равно null.
в противном случае я все еще хочу, чтобы макет перерисовывался правильно для ориентация.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_list);
if(savedInstanceState == null){
setupCloudMessaging();
}
}
Не уверен, что это окончательный ответ, но это работает для меня.
что я сделал...
в манифесте, в раздел activity, добавлено:
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
, документация и пример кода выше предложите это в файле манифеста:
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#setRetainInstance(boolean)
Я просто добавил
android:configChanges="keyboard|keyboardHidden|orientation"
в файле манифеста и не добавлять любой onConfigurationChanged
метод в своей деятельности.
поэтому каждый раз, когда клавиатура выскальзывает или ничего не происходит.
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
(кроме как в крайнем случае).... Но тогда альтернативные методы они предлагают все DO использовать android:configChanges
.
по моему опыту, эмулятор всегда вызывает onCreate()
при вращении.
Но 1-2 устройства, на которых я запускаю тот же код... Не.
(Не знаю, почему это так важно.)
добавить эту строку в манифест :-
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);
}
изменения, которые будут внесены в манифест 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();
}
}
на onCreate
метод по-прежнему вызывается даже при изменении orientation
android. Поэтому перемещение всей тяжелой функциональности в этот метод не поможет вам
поместите код ниже внутри вашего <activity>
tag in Manifest.xml
:
android:configChanges="screenLayout|screenSize|orientation"
есть несколько способов сделать это:
Сохранить Состояние Активности
вы можете сохранить состояние активности в 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);
}
Это очень просто, просто выполните следующие действия:
<activity
android:name=".Test"
android:configChanges="orientation|screenSize"
android:screenOrientation="landscape" >
</activity>
это работает для меня :
Примечание: ориентация зависит от вашего requitement
способ, которым я нашел это сделать, - использовать onRestoreInstanceState
и onSaveInstanceState
событий, чтобы сохранить что-то в Bundle
(даже если вам не нужны сохраненные переменные, просто поместите что-то туда, чтобы Bundle
не пуста). Затем, на onCreate
метод, проверьте, чтобы увидеть, если Bundle
пусто, и если это так, то сделайте инициализацию, если нет, то сделайте это.
несмотря на то, что это не "путь Android", я получил очень хорошие результаты, обрабатывая изменения ориентации сам и просто перемещая виджеты в представлении, чтобы учесть измененную ориентацию. Это быстрее, чем любой другой подход, потому что ваши взгляды не должны быть сохранены и восстановлены. Это также обеспечивает более плавный опыт для пользователя, потому что respositioned виджеты точно такие же виджеты, просто перемещены и / или изменены. Не только модель, но и состояние просмотра, может быть сохранено таким образом.
RelativeLayout
иногда может быть хорошим выбором для представления, которое должно время от времени переориентироваться. Вы просто предоставляете набор параметров портретной компоновки и набор параметров ландшафтной компоновки с различными правилами относительного позиционирования для каждого дочернего виджета. Затем, в onConfigurationChanged()
метод, вы передаете соответствующий метод в setLayoutParams()
звонок на каждого ребенка. Если какой-либо дочерний контроль должен быть внутри переориентируется, вы просто вызываете метод на этом ребенке, чтобы выполнить переориентацию. Этот ребенок аналогичным образом вызывает методы на любом из его дочерние элементы управления, которые нуждаются в внутренней переориентации, и так далее.
Примечание: я отправляю этот ответ, если кто в будущем столкнется с той же проблемой как меня. Мне не хватило следующей строчки:--6-->
android:configChanges="orientation"
когда я повернул экран, метод onConfigurationChanged (Configuration newConfig) не был вызван.
устранение: мне также пришлось добавить "screenSize", даже если проблема была связана с ориентацией. Так в AndroidManifest.xml-файл, добавьте это:
android:configChanges="keyboardHidden|orientation|screenSize"
затем реализовать метод onConfigurationChanged(Configuration newConfig)
вам нужно использовать метод onSavedInstanceState для хранения всего значения его параметра имеет, что является bundle
@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");
}
заправшивать и выберите значение для просмотра объектов он будет обрабатывать повороты экрана
каждый раз, когда экран поворачивается, открытое действие завершено и onCreate() вызывается снова.
1 . Вы можете сделать одну вещь, чтобы сохранить состояние активности при повороте экрана, чтобы вы могли восстановить все старые вещи при повторном вызове activity onCreate (). См.этой ссылке
2 . Если вы хотите предотвратить перезапуск действия, просто поместите следующие строки в манифест.XML-файл.
<activity android:name=".Youractivity"
android:configChanges="orientation|screenSize"/>
через некоторое время проб и ошибок, я нашел решение, которое соответствует моим потребностям в большинстве ситуаций. Вот код:
Манифест конфигурации:
<?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>
MainActivity:
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.
использовать 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);
}
}
поместите это ниже кода в свой Activity
на Android Manifest
.
android:configChanges="orientation"
Это не перезапустит вашу деятельность, когда вы измените ориентацию.
исправить ориентацию экрана (пейзаж или портрет) в AndroidManifest.xml
android:screenOrientation="portrait"
или android:screenOrientation="landscape"
этого вашего onResume()
метод не вызывается.
люди говорят, что вы должны использовать
android:configChanges="keyboardHidden|orientation"
но лучший и самый профессиональный способ обработки вращения в Android-использовать класс загрузчика. Это не знаменитый класс (я не знаю почему), но он намного лучше, чем AsyncTask. Для получения дополнительной информации вы можете прочитать учебники Android, найденные в курсах Android Udacity.
конечно, как другой способ, вы можете хранить значения или представления с помощью onSaveInstanceState и читать их с помощью onRestoreInstanceState. Все зависит от тебя.
вы можете заблокировать текущую ориентацию экрана с помощью этого кода...
int currentOrientation =context.getResources().getConfiguration().orientation;
if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else {
((Activity) context). setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
вы можете использовать объект ViewModel в своей деятельности.
объекты ViewModel автоматически сохраняются во время изменений конфигурации, так что данные, которые они содержат, немедленно доступны для следующего экземпляра действия или фрагмента. Подробнее:
https://developer.android.com/topic/libraries/architecture/viewmodel