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


191

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


Один из способов - НЕ проектировать альбомные макеты для телефонов, как при использовании layout-landвнутренней resпапки.
Призрак

12
Это приведет к тому, что макет портрета будет отображаться в ландшафте Это на самом деле не будет препятствовать повороту телефона в ландшафт.
Рэдли

Ответы:


446

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

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


1
Мне xlargeтоже нужно использовать или я могу просто использовать sw600dp? Вероятно, сейчас нет планшетов, работающих под <3.2.
Theblang

7
Ну, это потенциально перезапускает активность при запуске в ландшафтном см developer.android.com/reference/android/app/...
Bondax

1
@Bondax Этот комментарий применяется только «если действие в настоящее время находится на переднем плане или иным образом влияет на ориентацию экрана», что не может быть так, поскольку мы находимся в onCreate на данный момент.
Брайан Кристенсен

1
@BrianChristensen Я наблюдал перезапуск действия при установке запрашиваемой ориентации в onCreate(). Когда я переместил вызов в setRequestedOrientation()другую позицию по времени и контексту, перезапуск больше не происходил.
Бондакс

8
если я запускаю приложение в ландшафтном режиме, оно все равно показывает ландшафтный режим на короткое время.
目 白 目

29

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

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

4
Какой метод я должен поставить метод setRequestedOrientation ()? После запуска onCreate он уже выбрал желаемую ориентацию.
Кенни Уайлэнд

Кенни, проверьте это, я думаю, что это может помочь вам stackoverflow.com/questions/2833474/…
Ави Кумар Манку

Я вижу, что за этот ответ проголосовали, но я не уверен, что он решает проблему. Configurationпредоставляет screenLayoutинформацию - DPI. Тогда как вопрос касается размера экрана физического устройства - телефона против планшета. Это означает, что вы можете иметь Configuration.SCREENLAYOUT_SIZE_NORMALпланшет MDPI.
фиксированной ночью

я сталкиваюсь с проблемой stackoverflow.com/questions/42172864/…
Адитья Вьяс-

10

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

Вы можете сделать следующие шаги в 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файл.


2
Дайте мне знать, если это не сработает, и я все исправлю. Это сработало для меня. Я получил одно отрицание, но без комментариев, поэтому я не знаю, что исправить.
Сурагч

9

Вот как я это сделал (вдохновлено 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 
}

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


2
поместите ландшафтные макеты в папку layout-land
radley

Вы получите исключение, если не вызовете супер onConfigurationChanged
RominaV

8

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

Как описано здесь , поместите логическое значение в ресурсы 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>

5

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

https://stackoverflow.com/a/27015879/1281930


1
Не работает, планшеты всегда принимают ориентацию телефонов. Очевидно, что android: screenOrientation устанавливается ровно один раз для всех действий в манифесте, прежде чем будут применены какие-либо модификаторы ресурса.
0101100101

2

После принято ответа я добавляю файл 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)
    }
}

1

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

Пример:

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

0

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

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

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


0

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.