Ориентация активности меняется на Android

Я разрабатываю мобильное приложение на базе Android с minSdkVersion=15. Я хотел бы поддержать обе ориентации для планшетов и только портрет для смартфонов. Все работает как шарм, но я испытываю небольшую ошибку, которая сводит меня с ума. Когда смартфон находится в ландшафтном режиме, и я пытаюсь запустить новую активность, она открывается в ландшафтном режиме на некоторое время, а затем авторотируется на портрет. Каждый из моих действий расширяет класс GeneralActivity:

public class GeneralActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // If smartphone lock orientation to portrait
        if (!Helper.isTablet(this.getApplicationContext())){
            this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
}

Я обнаруживаю таблетки с помощью этой функции:

public class Helper {
    public static boolean isTablet(Context context){
        Configuration config = context.getResources().getConfiguration()
        return config.smallestScreenWidthDp >= 600;
    }
}

Я не хочу указывать android:screenOrientation внутри Manifest.xml, потому что таким образом я могу поддерживать всю ориентацию интерфейса для планшетов. Я что-то пропустил?

ИЗМЕНИТЬ

Я решил применить наилучшую практику, предложенную в ответе Джонатана, но проблема, которую я описал, все еще здесь. Здесь мое репо на github: https://github.com/giacmarangoni/Android-Orientation-Test

Ответ 1

У меня была та же проблема на Android N и O и нашел решение.

Я все еще использую setRequestedOrientation в методе onCreate, но я добавил это для каждого действия в манифесте:

android:screenOrientation="behind"

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

Ответ 2

Я нашел правильный способ решить эту проблему с помощью:

android:screenOrientation="locked"

на каждую деятельность, заявленную в манифесте с этой проблемой.

И сохраняйте его с помощью setRequestedOrientation() программно, чтобы определить, является ли альбомная или книжная ориентация в методе onCreate(),

Кстати, в вашем проекте GitHub я просто изменил манифест, добавив locked ориентацию, вот так, и это наконец работает:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.giacomomarangoni.orientationtest">

    <application
        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:screenOrientation="locked">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".OtherActivity"
            android:screenOrientation="locked">
        </activity>
    </application>
</manifest>

Надеюсь, это поможет вам тоже :)

Ответ 3

Здесь хороший способ использования ресурсов и определения размера.

Поместите этот ресурс bool в res/values ​​как bools.xml или что-то еще (имена файлов здесь неважны):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">true</bool>
</resources>

Поместите это в res/values-sw600dp и res/values-xlarge:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="portrait_only">false</bool>
</resources>

См. этот дополнительный ответ для добавления этих каталогов и файлов в Android Studio.

Затем в методе onCreate вашей деятельности вы можете сделать это:

if(getResources().getBoolean(R.bool.portrait_only)){
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

Устройства размером более 600 д.п. в наименьшем направлении ширины или x-large на устройствах до Android 3.2 (планшеты в основном) будут вести себя как обычно, на основе датчика и поворота с помощью пользователя и т.д. Все остальное ( телефоны, в значительной степени) будет только портретом.

Источник: Оригинальный ответ

Ответ 4

У меня есть это решение для меня. В вашем манифесте указывается портрет ориентации экрана

<activity android:name=".activities.MainActivity"
        android:screenOrientation="portrait">

        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>

Затем в вашем базовом действии в onCreate перед вызовом super.onCreate, если устройство представляет собой планшет, установите ориентацию на неуказанную.

@Override
protected void onCreate(Bundle savedInstanceState) {
    if (!getResources().getBoolean(R.bool.portrait_only)) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
    }
    super.onCreate(savedInstanceState);
}

Из документов:

Не выбрано: Не указано предпочтение: пусть система решит лучшую ориентацию. Это будет либо ориентация, выбранная нижеуказанной деятельностью, либо предпочтительная ориентация пользователя, если эта деятельность является нижней частью задачи. Если пользователь явно отключил ориентацию на основе сенсора через настройки, основанные на настройке устройства, будет проигнорирован. Если по умолчанию не будет учитываться ориентация датчика, ориентация будет изменяться в зависимости от того, как пользователь поворачивает устройство.

Это сработало для меня, и до сих пор я не нашел недостатков. Я тестировал это только в эмуляторах, и, похоже, приложение не начинается в портрете, а затем вращается, как это случилось с другими решениями на телефонах. Во всяком случае, я бы предпочел, чтобы телефоны работали лучше, чем таблетки, потому что они используются больше.

Если вы обнаружите, что что-то, что не работает с этим решением, поразило меня!

Ответ 5

Я также столкнулся с такой же проблемой для устройств Android N. Поэтому, чтобы поддерживать как ориентацию для планшетов, так и только портрет для телефонов, я сделал следующий трюк:

  • Установите портрет для каждой активности в манифесте. Выглядит плохо, но в этом случае экран не будет авторотировать: android:screenOrientation="portrait"

  • В вашем наборе BaseActivity:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (isTablet(){  
           setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
        }
    }
    
    protected boolean isTablet() {
       return getResources().getBoolean(R.bool.tablet);
    }
    

Ответ 6

В вашей базовой деятельности вы можете добавить что-то вроде этого:

/*  Device types. */
static int MOBILE_DEVICE = 0;
static int SEVEN_INCH_TABLET = 1;
static int TEN_INCH_TABLET = 2;

private static int deviceType = MOBILE_DEVICE;
private boolean deviceTypeDetermined = false;


@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
protected void onCreate(Bundle savedInstanceState) {

    if ( ! deviceTypeDetermined) setDeviceType();

    /* Screen rotation only for tablets. */
    if (getDeviceType() < SEVEN_INCH_TABLET) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        lockScreenOrientation();

    ......
}



// ---------------------------------------------------------------------------------------------
static int getDeviceType() {
    return deviceType;
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
private void setDeviceTypeDetermined() {
    this.deviceTypeDetermined = true;
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
private void setDeviceType() {

    /*
        Let invest on what kind of screen our APP is invited...

        We only make a difference in 10", 7" tablets and the rest...
    */

    Display mDisplay = getWindowManager().getDefaultDisplay();
    Point mScreenResolution = new Point();

    mDisplay.getRealSize(mScreenResolution);

    int mWidthPixels = mScreenResolution.x;
    int mHeightPixels = mScreenResolution.y;

    DisplayMetrics mMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(mMetrics);

    float mWidthDpi = mMetrics.xdpi;
    float mHeightDpi = mMetrics.ydpi;

    float mWidthInches = mWidthPixels / mWidthDpi;
    float mHeightInches = mHeightPixels / mHeightDpi;



    double mDiagonalInches = Math.sqrt(
            (mWidthInches * mWidthInches)
                    + (mHeightInches * mHeightInches));


    if (mDiagonalInches >= 9) {

        /*
            A tablet with 8" x 5" is called a 10", but it is in fact
            9.43398". Interesting that this kind of things happens in
            the world of informatics.... ;)
        */

        MyBaseAppCompatActivity.deviceType = TEN_INCH_TABLET;
    }

    else if (mDiagonalInches >= 7) {
        MyBaseAppCompatActivity.deviceType = SEVEN_INCH_TABLET;
    }

    else
    {
        MyBaseAppCompatActivity.deviceType = MOBILE_DEVICE;
    }


    setDeviceTypeDetermined();
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
void lockScreenOrientation() {

    /*  Screen rotation only for tablets. */
    if (deviceType < SEVEN_INCH_TABLET ) return;

    setRequestedOrientation(getResources().getConfiguration().orientation);
}
// ---------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------
void unlockScreenOrientation() {

    /*  Screen rotation only for tablets. */
    if (deviceType < SEVEN_INCH_TABLET ) return;

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
// ---------------------------------------------------------------------------------------------

И в ваших действиях в onCreate:

// ---------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
protected void onCreate(Bundle savedInstanceState) {
    // -----------------------------------------------------------------------------------------
    super.onCreate(savedInstanceState);

    if (getDeviceType() >= SEVEN_INCH_TABLET) unlockScreenOrientation();

    setContentView(R.layout.main_activity);

    .........

// ---------------------------------------------------------------------------------------------

Ответ 7

Добавьте эту строку во все теги активности в файл манифеста...

 android:screenOrientation="portrait"