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
}
Единственное, чего я не могу понять, это как заставить приложение изменять файлы макетов при переключении с альбомной ориентации на книжную или наоборот. Я предполагаю, что ответ делает нечто похожее на то, что делает приведенная выше ссылка, но я не смог заставить это работать на меня - он удалил все мои данные. Но если у вас достаточно простое приложение с одинаковым файлом макета для портретной и альбомной ориентации, это должно сработать.
У меня были проблемы с предоставленными решениями, наконец, это то, что сработало для меня:
- В
AndroidManifest.xml
установите ориентацию Activity на «заблокировано»:
<activity
android:name="com.whatever.YourActivity"
android:screenOrientation="locked"
... />
- В
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
делает, если должен изменить ориентацию:
После принятого ответа я добавляю файл 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);
}