Перезагрузка активности при ротации Android

В моем приложении для Android, когда я поворачиваю устройство (выдвигаю клавиатуру), мой Activity перезапускается (вызывается onCreate). Теперь это, вероятно, так, как должно быть, но я много начинаю в методе onCreate, поэтому мне нужно:

  • Поместите всю начальную настройку в другую функцию, чтобы она не потерялась при вращении устройства или
  • Сделать так, чтобы onCreate не вызывался снова и макет просто настраивался или
  • Ограничьте приложение только портретом, чтобы onCreate не вызывался.

Ответ 1

Использование класса приложения

В зависимости от того, что вы делаете при инициализации, вы можете рассмотреть возможность создания нового класса, расширяющего 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 в ваш узел манифеста Activity

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

или для Android 3.2 (уровень API 13) и новее:

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

Затем в Activity переопределите метод onConfigurationChanged и вызовите setContentView чтобы принудительно сделать макет графического интерфейса в новой ориентации.

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  setContentView(R.layout.myLayout);
}

Ответ 2

Обновление для Android 3.2 и выше:

Предостережение. Начиная с Android 3.2 (уровень API 13), размер экрана также изменяется, когда устройство переключается между портретной и альбомной ориентацией. Таким образом, если вы хотите предотвратить перезапуск во время изменения ориентации при разработке для уровня API 13 или выше (как указано атрибутами minSdkVersion и targetSdkVersion), вы должны добавить значение "screenSize" в дополнение к значению "orientation". То есть, вы должны объявить android:configChanges="orientation|screenSize". Однако, если ваше приложение нацелено на уровень API 12 или ниже, ваша деятельность всегда обрабатывает это изменение самой конфигурации (это изменение конфигурации не перезапускает вашу активность даже при работе на устройстве Android 3.2 или более поздней версии).

Ответ 3

Вместо того, чтобы пытаться полностью отключить onCreate(), возможно, попробуйте передать Bundle savedInstanceState в событие, чтобы узнать, является ли оно нулевым или нет.

Например, если у меня есть некоторая логика, которая должна выполняться, когда Activity действительно создан, а не при каждом изменении ориентации, я запускаю эту логику только в onCreate(), только если savedInstanceState имеет значение null.

В противном случае, я все еще хочу, чтобы макет правильно перерисовывал ориентацию.

public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game_list);

        if(savedInstanceState == null){
            setupCloudMessaging();
        }
}

не уверен, что это окончательный ответ, но он работает для меня.

Ответ 4

что я сделал...

в манифесте, в раздел активности, добавлено:

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();
}

Ответ 5

То, что вы описываете, - это поведение по умолчанию. Вы должны сами определять и обрабатывать эти события, добавляя:

android:configChanges

в ваш манифест, а затем изменения, которые вы хотите обработать. Поэтому для ориентации вы будете использовать:

android:configChanges="orientation"

а для открытой или закрытой клавиатуры вы будете использовать:

android:configChanges="keyboardHidden"

Если вы хотите обрабатывать оба, вы можете просто отделить их командой pipe, например:

android:configChanges="keyboardHidden|orientation"

Это вызовет метод onConfigurationChanged во всех действиях, которые вы вызываете. Если вы переопределите метод, который вы можете передать в новых значениях.

Надеюсь, что это поможет.

Ответ 6

Я только что открыл эти знания:

Чтобы сохранить активность через изменение ориентации и обработать ее через onConfigurationChanged, документацию и пример кода выше, предположим это в файле манифеста:

android:configChanges="keyboardHidden|orientation"

который имеет дополнительное преимущество, которое он всегда работает.

Знание бонуса заключается в том, что опускание keyboardHidden может показаться логичным, но оно вызывает сбои в эмуляторе (по крайней мере, для Android 2.1): указание только orientation сделает вызов эмулятора как OnCreate, так и onConfigurationChanged иногда и только OnCreate в другое время.

Я не видел сбоя на устройстве, но я слышал об отсутствии эмулятора для других. Поэтому стоит документировать.

Ответ 7

Вы также можете рассмотреть возможность использования платформы Android для хранения данных в разных изменениях ориентации: onRetainNonConfigurationInstance() и getLastNonConfigurationInstance().

Это позволяет вам сохранять данные в конфигурационных изменениях, таких как информация, которую вы, возможно, получили из выборки сервера, или что-то еще, которое было вычислено в onCreate, или с тех пор, а также позволяет Android повторно развернуть ваш Activity, используя xml файл для используемой ориентации.

См. здесь или здесь.

Следует отметить, что эти методы теперь устаревают (хотя и более гибкие, чем обработка ориентации, меняют сами, как предлагают большинство из вышеперечисленных решений) с рекомендацией, что все переключаются на Fragments и вместо этого используют setRetainInstance(true) для каждого Fragment, который вы хотите сохранить.

Ответ 8

Подход полезен, но является неполным при использовании фрагментов.

Фрагменты обычно воссоздаются при изменении конфигурации. Если вы этого не хотите, используйте

setRetainInstance(true); в конструкторе (-ях) фрагмента

Это приведет к сохранению фрагментов во время изменения конфигурации.

http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance (boolean)

Ответ 10

Метод onCreate по-прежнему вызывается даже при изменении orientation андроида. Поэтому перемещение всех тяжелых функций этого метода не поможет вам

Ответ 11

Поместите код ниже в свой тег <activity> в Manifest.xml:

android:configChanges="screenLayout|screenSize|orientation"

Ответ 12

 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 устройствах, на которых я запускаю один и тот же код, нет. (Не знаю, почему будет какая-то разница.)

Ответ 13

Добавьте эту строку в свой манифест: -

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);
    }

Ответ 14

Очень просто выполнить следующие действия:

<activity
    android:name=".Test"
    android:configChanges="orientation|screenSize"
    android:screenOrientation="landscape" >
</activity>

Это работает для меня:

Примечание: ориентация зависит от вашего удовлетворения

Ответ 15

Изменения в манифесте 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();
    }
}

Ответ 16

Существует несколько способов сделать это:

Сохранить состояние активности

Состояние активности можно сохранить в 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);
}

Ответ 17

Несмотря на то, что это не "способ Android", я получил очень хорошие результаты, самостоятельно обработав изменения ориентации и просто переставив виджеты в представление, чтобы учитывать измененную ориентацию. Это быстрее, чем любой другой подход, потому что ваши взгляды не нужно сохранять и восстанавливать. Он также предоставляет пользователям более бесшовные впечатления, потому что смещенные виджеты - это точно такие же виджеты, просто перемещенные и/или измененные. Таким образом можно сохранить не только состояние модели, но и состояние отображения.

RelativeLayout иногда может быть хорошим выбором для представления, которое время от времени должно переориентироваться. Вы просто предоставляете набор параметров макета портрета и набор параметров ландшафтного макета с разными правилами относительного позиционирования для каждого из них для каждого дочернего виджета. Затем в вашем методе onConfigurationChanged() вы передаете соответствующий вызов setLayoutParams() для каждого дочернего элемента. Если какой-либо дочерний элемент управления сам должен переориентироваться на внутреннем уровне, вы просто вызываете метод для этого ребенка для выполнения переориентации. Этот ребенок аналогичным образом называет методы любого из своих дочерних элементов управления, которые нуждаются в внутренней переориентации и т.д.

Ответ 18

Как я нашел это, используйте события onRestoreInstanceState и onSaveInstanceState, чтобы сохранить что-то в Bundle (даже если вам не нужны какие-либо переменные, просто поместите что-то там, чтобы Bundle не пусто). Затем, по методу onCreate, проверьте, является ли Bundle пустым, а если это так, выполните инициализацию, если нет, тогда сделайте это.

Ответ 19

Примечание: Я отправляю этот ответ, если кто-то в будущем сталкивается с той же проблемой, что и я. Для меня следующая строка не была достаточной:

android:configChanges="orientation"

Когда я поворачивал экран, метод `onConfigurationChanged (Configuration newConfig) не вызывался.

Решение: Мне также пришлось добавить "screenSize", даже если проблема была связана с ориентацией. Поэтому в файле AndroidManifest.xml добавьте следующее:

android:configChanges="keyboardHidden|orientation|screenSize"

Затем реализуем метод onConfigurationChanged(Configuration newConfig)

Ответ 20

вам нужно использовать метод 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");
    } 

для извлечения и установки значения для просмотра объектов он будет обрабатывать вращения экрана

Ответ 21

Каждый раз, когда экран поворачивается, открытая активность завершается, а функция onCreate() вызывается снова.

1. Вы можете сделать одну вещь, чтобы сохранить состояние активности, когда экран повернут так, что вы можете восстановить все старые вещи, когда снова будет вызвана операция onCreate().   Обратитесь к этой ссылке

2. Если вы хотите предотвратить перезапуск активности, просто поместите следующие строки в файл manifest.xml.

  <activity android:name=".Youractivity"
  android:configChanges="orientation|screenSize"/>

Ответ 22

В разделе активности manifest добавьте:

android:configChanges="keyboardHidden|orientation"

Ответ 23

Добавьте эту строку в манифест: android:configChanges="orientation|screenSize"

Ответ 24

Люди говорят, что вы должны использовать

android:configChanges="keyboardHidden|orientation"

Но лучший и самый профессиональный способ справиться с вращением в Android - это использовать класс Loader. Это не известный класс (я не знаю почему), но он намного лучше, чем AsyncTask. Для получения дополнительной информации вы можете прочитать учебники по Android, которые можно найти в курсах по Udacity.

Конечно, в качестве другого способа вы можете сохранить значения или представления с помощью onSaveInstanceState и прочитать их с помощью onRestoreInstanceState. Это до вас действительно.

Ответ 25

Исправить ориентацию экрана (пейзаж или портрет) в AndroidManifest.xml

android:screenOrientation="portrait" или android:screenOrientation="landscape"

для этого ваш метод onResume() не вызывается.

Ответ 26

Поместите этот ниже код в Activity в Android Manifest.

android:configChanges="orientation"

Это не приведет к перезапуску вашей активности, когда вы измените ориентацию.

Ответ 27

Используйте 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);
    }
}

Ответ 28

Через некоторое время проб и ошибок я нашел решение, которое соответствует моим потребностям в большинстве ситуаций. Вот код:

Конфигурация манифеста:

<?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 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.

Ответ 29

Вы можете использовать объект ViewModel в своей деятельности.

Объекты ViewModel автоматически сохраняются во время изменений конфигурации, поэтому данные, которые они хранят, сразу же становятся доступны для следующего действия или экземпляра фрагмента. Прочитайте больше:

https://developer.android.com/topic/libraries/architecture/viewmodel

Ответ 30

Один из лучших компонентов архитектуры Android, представленный Google, удовлетворит все ваши требования, а именно ViewModel.

Это предназначено для хранения и управления данными, относящимися к пользовательскому интерфейсу, в режиме жизненного цикла, а также позволит выживать данным при повороте экрана

class MyViewModel : ViewModel() {

Пожалуйста, обратитесь сюда: https://developer.android.com/topic/libraries/architecture/viewmodel