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

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

  1. поместите всю начальную настройку в другую функцию, чтобы она не потерялась при вращении устройства или
  2. сделать так onCreate не вызывается снова и макет просто корректирует или
  3. ограничение приложение просто портрет, так что 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"/>

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

android:configChanges="keyboardHidden|orientation"

добавить эту строку android: configChanges= " ориентация / 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