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


191

Я хотів би, щоб планшети могли відображатись у портреті та пейзажі (sw600dp або більше), але телефони мають бути обмежені лише для портретних. Я не можу знайти жодного способу умовно вибрати орієнтацію. Будь-які пропозиції?


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

12
Це призвело б лише до того, щоб макет портрета відображався в пейзажі. Це фактично не завадить обертатися телефону до пейзажу.
radley

Відповіді:


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 на пристроях, що перебувають під керуванням 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 почався, він вже обрав, яку орієнтацію хоче.
Кенні Віланд

kenny перевіри це, я думаю, що це може допомогти тобі stackoverflow.com/questions/2833474/…
Аві Кумар Манку

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


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 (рівень 13 API). Інакше цей крок можна пропустити. 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

Ви отримаєте виняток, якщо не зателефонуєте на Super onConfigurationChanged
RominaV

8

Після відповіді Джінні , я думаю, що найнадійніший спосіб зробити це:

Як описано тут , введіть булевий ресурс sw600dp. Він повинен мати префікс sw інакше він не працює належним чином:

у res / values-sw600dp / dimens.xml

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

в res / values ​​/ dimens.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" />

в активності splashcreen:

    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 Devs не рекомендує змінювати цей атрибут .

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

Короткий зміст: На мій погляд, пропозиція @Brian Christensen є найкращим компромісом, але будьте в курсі питання про перезапуск діяльності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.