Android: разрешить портрет и пейзаж для планшетов, но заставить портрет на телефоне?

Я хотел бы, чтобы планшеты могли отображаться в портретной и альбомной ориентации (sw600dp или выше), но телефоны должны быть только в портретной ориентации. Я не могу найти способ условно выбрать ориентацию. Какие-либо предложения?

17 ответов

Решение

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

Поместите этот ресурс 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 dp в направлении наименьшей ширины или x-large на устройствах до Android 3.2 (планшеты, в основном), будут вести себя как обычно, основываясь на повороте датчика и блокировки пользователя и т. Д. Все остальное (телефоны, в значительной степени) будет только портретным.

Вы можете попробовать этот способ сначала получить размер экрана устройства

if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {     
    Toast.makeText(this, "Large screen",Toast.LENGTH_LONG).show();

}
else if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL) {     
    Toast.makeText(this, "Normal sized screen" , Toast.LENGTH_LONG).show();

} 
else if ((getResources().getConfiguration().screenLayout &      Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL) {     
    Toast.makeText(this, "Small sized screen" , Toast.LENGTH_LONG).show();
}
else {
    Toast.makeText(this, "Screen size is neither large, normal or small" , Toast.LENGTH_LONG).show();
}

а затем установить ориентацию в соответствии с этим

setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

Дополнение к принятому ответу

Вы можете сделать следующие шаги в Android Studio, чтобы добавить res/values-sw600dp а также res/values-large каталоги с их bools.xml файлы.

Значения-sw600dp

Прежде всего, на вкладке Project выберите фильтр Project (а не Android) в навигаторе.

введите описание изображения здесь

Затем щелкните правой кнопкой мыши app/src/main/res каталог. Выберите " Создать" > " Каталог ресурсов Android".

Выберите наименьшая ширина экрана, а затем нажмите кнопку >>.

введите описание изображения здесь

Введите 600 для наименьшей ширины экрана. Имя каталога будет создано автоматически. Скажи "ОК.

введите описание изображения здесь

Затем щелкните правой кнопкой мыши по вновь созданному values-sw600dp файл. Выберите " Создать" > " Файл значений". Тип bools для имени.

Значения-большие

Добавление values-large Каталог необходим только в том случае, если вы поддерживаете предварительно Android 3.2 (уровень API 13). В противном случае вы можете пропустить этот шаг. values-large каталог соответствует values-sw600dp, (values-xlarge соответствует values-sw720dp.)

Чтобы создать values-large каталога, выполните те же действия, что и выше, но в этом случае выберите Размер, а не Наименьшую ширину экрана. Выберите Большой. Имя каталога будет создано автоматически.

введите описание изображения здесь

Щелкните правой кнопкой мыши каталог, как и прежде, чтобы создать bools.xml файл.

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

Как описано здесь, поместите логическое значение в ресурсы sw600dp. У него должен быть префикс sw, иначе он не будет работать должным образом:

в res/values-sw600dp/dimensions.xml

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

в res / values ​​/ dimensions.xml

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

Затем создайте метод для получения этого логического значения:

public class ViewUtils {
    public static boolean isTablet(Context context){
        return context.getResources().getBoolean(R.bool.isTablet);
    }
}

И базовое действие, расширяемое от действий, для которых вы хотите следующее поведение:

public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!ViewUtils.isTablet(this)) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
    }
}

Таким образом, каждое действие будет расширять BaseActivity:

public class LoginActivity extends BaseActivity //....

Важно: даже если вы продлили BaseActivity, вы должны добавить строку android:configChanges="orientation|screenSize" для каждого Activity в вашем AndroidManifest.xml:

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

Вот как я это сделал (вдохновлено http://androidblogger.blogspot.com/2011/08/orientation-for-both-phones-and-tablets.html):

В AndroidManifest.xml для каждого действия вы хотите иметь возможность переключаться между портретным и ландшафтным режимом (убедитесь, что вы добавили screenSize - вам это раньше не нужно!) Вам не нужно устанавливать здесь ориентацию экрана.:

android:configChanges="keyboardHidden|orientation|screenSize"

Методы, которые нужно добавить в каждое действие:

public static boolean isXLargeScreen(Context context) {
    return (context.getResources().getConfiguration().screenLayout
    & Configuration.SCREENLAYOUT_SIZE_MASK)
    >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
} 

и: (если вы не переопределите этот метод, приложение вызовет onCreate() при изменении ориентации)

@Override
public void onConfigurationChanged (Configuration newConfig)
{       
    super.onConfigurationChanged(newConfig);

    if (!isXLargeScreen(getApplicationContext()) ) {            
        return; //keep in portrait mode if a phone      
    }

    //I set background images for landscape and portrait here
}

В onCreate() каждого действия:

if (!isXLargeScreen(getApplicationContext())) { //set phones to portrait; 
   setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);            
}
else {
  //I set background images here depending on portrait or landscape orientation 
}

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

У меня были проблемы с предоставленными решениями, наконец, это то, что сработало для меня:

  1. В AndroidManifest.xmlустановите ориентацию Activity на «заблокировано»:
      <activity
    android:name="com.whatever.YourActivity"
    android:screenOrientation="locked"
    ... />
  1. В OnCreate(...)метод в своей деятельности, используйте следующее:
      @SuppressLint("SourceLockedOrientationActivity")
override fun onCreate(savedInstanceState: Bundle?) {
    if (isTablet(resources)) {
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR;
    } else if (resources.configuration.orientation != Configuration.ORIENTATION_PORTRAIT) {
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
    }
    super.onCreate(savedInstanceState)
}

Обратите внимание, что вам не нужно использовать хакерские решения с логическими значениями в ресурсах, вы можете использовать isTablet(resources)метод из DevicePropertiesкласс, который помимо всего прочего проверяет, smallestScreenWidthDp >= 600.

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

Заключение НЕОБХОДИМО установить правильную ориентацию в манифесте, чтобы система знала ориентацию без запуска вашего приложения.

Вот решение, как это сделать (это немного усилий, но вы будете лучше спать)

Сначала установите ориентацию в манифесте как заполнитель

<activity
      android:screenOrientation="${screenOrientation}"
     ...
</activity>

Затем нам нужно добавить обычный / планшетный вариант, чтобы установить значение в app / build.gradle. (Может быть достигнуто с новым типом сборки, если вы уже используете ароматы)

android {
    ...
    productFlavors {
        normal {
            manifestPlaceholders = [screenOrientation: "portrait"]
        }
        tablet {
            manifestPlaceholders = [screenOrientation: "unspecified"]
        }
    }
}

Теперь нам нужно сообщить сборке планшета, что она предназначена только для больших устройств. Это можно сделать, добавив манифест только для планшета для слияния с манифестом по умолчанию. Добавьте новый файл манифеста в->

app
 src
  tablet
   AndroidManifest.xml

Ниже приведены все, что нужно этому манифесту, потому что он объединен с манифестом по умолчанию ->

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

    <supports-screens
        android:smallScreens="false"
        android:normalScreens="false"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:requiresSmallestWidthDp="600"
        />

</manifest>

Последний трюк - нам нужно различать сборки по коду версии, потому что в Playstore не может быть двух загрузок с совпадающими кодами. Мы хотим убедиться, что планшет (более строгая сборка) имеет более высокий код. Легче всего взять базовый код, потом для планшетаbase * 2 и нормально base * 2 - 1. Я использую для этого CI с базовым кодом, который является номером сборки, но достаточно легким для жесткого кодирования в ваших вариантах.

Теперь создайте оба аромата, чтобы создать

 app-normal.apk/aab (v1.0.0, version code 1)
 app-tablet.apk/aab (v1.0.0, version code 2)

Загрузите их в Playstore как несколько загрузок apk/aab, а затем, если они загружены на планшет, Playstore предоставит им apk, который вращается, а если загружен на телефон, портрет только один.

Примечание: работает только при распространении через Google Playstore/Amazon Kindle.

Дальнейшее чтение https://developer.android.com/google/play/publishing/multiple-apkshttps://developer.amazon.com/docs/fire-tablets/ft-screen-layout-and-resolution.html

Ну, это немного поздно, но вот XML-Only, но решение для взлома, которое не воссоздает действие как setRequestedOrientation делает, если должен изменить ориентацию:

/questions/25188351/kak-izmenit-orientatsiyu-ekrana-ne-sozdavaya-novuyu-aktivnost-na-android/25188372#25188372

После принятого ответа я добавляю файл kotlin с решением, надеюсь, что это поможет кому-то

Поместите этот ресурс 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-sw600dp-land:

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

Затем добавьте его в строки ниже в своей активности или фрагментации

class MyActivity : Activity() {

    @SuppressLint("SourceLockedOrientationActivity")
    override fun onCreate(savedInstanceState: Bundle?) {
        if (resources.getBoolean(R.bool.portrait_only)) {
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        super.onCreate(savedInstanceState)
    }

    @SuppressLint("SourceLockedOrientationActivity")
    override fun onConfigurationChanged(newConfig: Configuration) {
        if (resources.getBoolean(R.bool.portrait_only)) {
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
        super.onConfigurationChanged(newConfig)
    }
}

Другие решения не сработали для меня. У меня все еще были странные проблемы с ориентацией на диалоги и вопросы отдыха. Мое решение состояло в том, чтобы продлить действие, сделав его портретом в манифесте.

Пример:

public class MainActivityPhone extends MainActivity {}

manifest.xml:

        <activity
        android:screenOrientation="portrait"
        android:name=".MainActivityPhone"
        android:theme="@style/AppTheme.NoActionBar" />

в заставке деятельности:

    Intent i = null;
    boolean isTablet = getResources().getBoolean(R.bool.is_tablet);
    if (!isTablet)
        i = new Intent(this, MainActivityPhone.class);
    else
        i = new Intent(this, MainActivity.class);
    startActivity(i);

На сайте developer.android.com вы должны объявить ориентацию экрана в своем манифесте со значением «nosensor»:

Ориентация определяется без привязки к датчику физической ориентации. Датчик игнорируется, поэтому дисплей не будет вращаться в зависимости от того, как пользователь перемещает устройство.

В этом случае вращение будет «естественным» поведением устройства (для телефонов: книжная ориентация / для планшетов: альбомная ориентация).

Преимущество заключается в том, что вы можете смешивать разные вращения для каждого действия, например, если у вас есть действие, показывающее записи базы данных, вы можете установить для этого действия ориентацию на «fullUser», в то время как другие используют «nosensor». Активность «fullUser» будет чередоваться в зависимости от выбора пользователя (поворот включен: использует датчик, в противном случае использует предпочтения пользователя).

      <activity android:name=".scanner.ScannerPropertiesActivity" 
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/title_activity_scanner_properties"
android:screenOrientation="${screenOrientation}"/>

${screenOrientation} - это ManifestPlaceHolder, поэтому я могу легко переключать все действия, которые я хочу протестировать, между всеми режимами ориентации, используя это:

      //module's build.gradle
defaultConfig {
//...
    manifestPlaceholders = [applicationName:appName,screenOrientation:'nosensor']
//...
}

Если вам нужно что-то более сложное (например, разрешить поворот экрана только на планшете и исправить ориентацию телефона на книжную), вам следует использовать locked :

android:screenOrientation="locked"Я заметил, что если вы установите портретную ориентацию , в то время как датчик отправляет, что телефон повернут в альбомной ориентации, ваша активность начнется в альбомной ориентации, а затем повернется в портретную после выполнения вашего кода практически с любой другой ориентацией. lock сохраняет предыдущее вращение, поэтому вы можете уже заблокироваться в портретной ориентации из вашей предыдущей активности. Затем вы должны знать, когда устройство является планшетом или телефоном: для этого я рекомендую вам использовать это:

      //normal tablet_detection file content:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="isTablet" type="bool">false</item>
</resources>
      //sw600dp tablet_detection file content:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="isTablet" type="bool">true</item>
</resources>
      //Now in your code you can detect if it's a tablet or not by calling following function in your Activity's **onCreate** and apply screen orientation by code:
protected  void updateScreenOrientation(){
boolean isTablet =context.getResources().getBoolean(R.bool.isTablet);
//And select the orientation you wish to set:
int defaultOrientation=isTablet? ActivityInfo.SCREEN_ORIENTATION_FULL_USER:ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        if(getRequestedOrientation()== ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
            //Apply this only on Activities where you set the orientation to 'locked' in the manifest
            this.setRequestedOrientation(defaultOrientation);
        }
}

К сожалению, использование метода setRequestedOrientation(...) приведет к перезапуску действия, поэтому даже если вы вызовете его в методе onCreate, он пройдет жизненный цикл действия и затем заново создаст то же действие в запрошенной ориентации. Поэтому при ответе @Brian Christensen вы должны учитывать, что код активности может быть вызван дважды, что может иметь плохие последствия (не только визуальные, но и при сетевых запросах, аналитике и т. Д.).

Более того, установка атрибута configChanges в манифесте, по моему мнению, является большим компромиссом, который может потребовать огромных затрат на рефакторинг. Разработчики Android не рекомендуют менять этот атрибут.

Наконец, попытка установить screenOrientation как-то иначе (чтобы избежать проблемы перезапуска) невозможна, статически невозможна из-за статического манифеста, который нельзя изменить, программно можно вызвать этот метод только в уже запущенном действии.

Резюме: На мой взгляд, предложение @Brian Christensen - лучший компромисс, но имейте в виду проблему возобновления активности.

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

По шагам первое, что нужно сделать, это разблокировать ориентацию в манифесте.

      <activity
    android:name=".MyActivity"
    android:screenOrientation="fullUser">

Затем вы можете использовать Jetpack Window Manager , чтобы определить, сколько места приложение занимает на экране:

      /** Determines whether the device has a compact screen. **/
fun compactScreen(): Boolean {
    val screenMetrics = WindowMetricsCalculator
                        .getOrCreate()
                        .computeMaximumWindowMetrics(this)
    val shortSide = min(screenMetrics.bounds.width(),
                        screenMetrics.bounds.height())
    return shortSide / resources.displayMetrics.density < 600
}

и, наконец, вы блокируете ориентацию, когда пространство меньше, обычно на телефонах:

      override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    requestedOrientation = if (compactScreen())
        ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else
        ActivityInfo.SCREEN_ORIENTATION_FULL_USER
    ...
}

В файл манифеста вы добавляете следующий код:

android:screenOrientation="fullSensor"

и файл XML создать в layout-land папка

Я добавил эту часть кода внутрьMainActivity.javaфайл. Это сработало для моего случая.

       @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            handleOrientationConfiguration();
        }

        private void handleOrientationConfiguration() {

            if (isTablet()) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            }
        }

        private boolean isTablet() {
            Configuration configuration = this.getContext().getResources()
                    .getConfiguration();/*from   www .j ava  2  s. c  o  m*/
            return (configuration.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
        }

Старый вопрос я знаю. Чтобы приложение всегда работало в портретном режиме, даже если ориентация может быть или поменяется местами и т. Д. (Например, на планшетах), я разработал эту функцию, которая используется для установки устройства в правильной ориентации без необходимости знать, как портрет и пейзаж функции организованы на устройстве.

   private void initActivityScreenOrientPortrait()
    {
        // Avoid screen rotations (use the manifests android:screenOrientation setting)
        // Set this to nosensor or potrait

        // Set window fullscreen
        this.activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        DisplayMetrics metrics = new DisplayMetrics();
        this.activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);

         // Test if it is VISUAL in portrait mode by simply checking it's size
        boolean bIsVisualPortrait = ( metrics.heightPixels >= metrics.widthPixels ); 

        if( !bIsVisualPortrait )
        { 
            // Swap the orientation to match the VISUAL portrait mode
            if( this.activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT )
             { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); }
            else { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ); }
        }
        else { this.activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); }

    }

Работает как шарм!

ВНИМАНИЕ: Изменить this.activity по вашей активности или добавьте его в основную активность и удалите this.activity;-)

Если вы хотите сделать обратное, вы должны изменить код на ландшафт (но я думаю, что это понятно).

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

Шаг 1: Установитеandroid:screenOrientation="locked"к действию в AndroidManifest.xml

Шаг 2: Создайте планшет Qualifer.

Шаг 3:

      val configuration = resources.configuration
val smallestScreenWidthDp = configuration.smallestScreenWidthDp

if (smallestScreenWidthDp>=600){
    //   for tablet 
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
    //   for phone   
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
Другие вопросы по тегам