Поддерживает трехсекционные и горизонтальные складные издания.

Складная книга горизонтального формата в закрытом и полностью открытом положениях рядом с трехсекционной книгой в закрытом и полностью открытом положениях.

Разработчики часто сталкиваются с уникальными трудностями при создании приложений для складных устройств, особенно таких, как Samsung Trifold или оригинальный Pixel Fold, которые открываются в альбомной ориентации (rotation_0 = landscape). К ошибкам разработчиков относятся:

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

К числу конкретных проблем, связанных с устройством, относятся:

  • Несоответствие естественной ориентации устройства между внешним и внутренним дисплеями (предположения, основанные на rotation_0 = портретный режим), приводящее к сбоям приложений при складывании и раскладывании.
  • Различная плотность экрана и некорректная обработка изменений конфигурации плотности
  • Проблемы с предварительным просмотром изображения вызваны зависимостью сенсора камеры от естественной ориентации.

Для обеспечения высокого качества пользовательского опыта на складных устройствах необходимо сосредоточиться на следующих ключевых областях:

  • Ориентацию приложения следует определять, исходя из фактической области экрана, которую занимает приложение, а не из физической ориентации устройства.
  • Обновите предварительный просмотр камеры, чтобы он корректно обрабатывал ориентацию устройства и соотношение сторон, избегал просмотра изображений в горизонтальном положении и предотвращал растягивание или обрезку изображений.
  • Для обеспечения непрерывности работы приложения при складывании или раскладывании устройства либо сохраняя состояние с помощью ViewModel или аналогичных подходов, либо обрабатывая изменения плотности экрана и ориентации вручную, что позволяет избежать перезапуска приложения или потери состояния.
  • Для приложений, использующих датчики движения, настройте систему координат так, чтобы она соответствовала текущей ориентации экрана, и избегайте предположений, основанных на rotation_0 = portrait, гарантируя точное взаимодействие с пользователем.

Создавайте адаптивные

Если ваше приложение уже адаптивное и соответствует оптимизированному уровню (уровень 2), описанному в рекомендациях по качеству приложений для больших экранов , оно должно хорошо работать на складных устройствах. В противном случае, прежде чем проверять особенности трехсекционных и горизонтальных складных устройств, ознакомьтесь со следующими основными концепциями адаптивной разработки Android.

Адаптивные макеты

Ваш пользовательский интерфейс должен обрабатывать не только разные размеры экрана, но и изменения соотношения сторон в реальном времени, например, при развертывании и переходе в многооконный или оконный режимы рабочего стола. Дополнительные рекомендации по этому вопросу см. в разделе «О адаптивных макетах» .

  • Разработка и внедрение адаптивных макетов.
  • Настройте основную навигацию вашего приложения в зависимости от размера окна.
  • Используйте классы размера окна для адаптации пользовательского интерфейса вашего приложения.
  • Упростите реализацию канонических макетов, таких как список-подробности, используя API Jetpack.
Приложение отображается в режиме "letterbox" на развернутом складном экране, а на другом развернутом складном экране то же приложение отображается в полноэкранном режиме с адаптивной компоновкой.
Рисунок 1. Разница между неадаптивной (с черными полосами сверху и снизу) и адаптивной компоновкой.

Классы размеров окон

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

Изображение приложения на устройствах с размерами окна, соответствующими компактному, среднему и расширенному форматам.
Рисунок 2. Классы размеров окон.

В следующем примере используется адаптивная библиотека Material 3 для определения доступного приложению пространства путем сначала вызова функции currentWindowAdaptiveInfo() , а затем использования соответствующих макетов для трех классов размеров окон:

val adaptiveInfo = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)
val windowSizeClass = adaptiveInfo.windowSizeClass

when {
  windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> // Large
  windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND) -> // Medium
  else -> // Compact
}

Для получения дополнительной информации см. раздел «Использование классов размеров окон» .

Качество приложения для большого экрана

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

Android 16 и выше

Для приложений, ориентированных на Android 16 (уровень API 36) и выше, система игнорирует ограничения по ориентации, изменению размера и соотношению сторон на дисплеях с минимальной шириной >= 600dp. Приложения заполняют все окно дисплея независимо от соотношения сторон или предпочтительной ориентации пользователя, и режим совместимости с черными полосами сверху и снизу больше не используется.

Особые соображения

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

Предварительный просмотр камеры

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

Несоответствие предположений

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

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

Решение 1: Jetpack CameraX (лучшее)

Самое простое и надежное решение — использовать библиотеку Jetpack CameraX. Ее элемент пользовательского интерфейса PreviewView разработан для автоматической обработки всех сложностей предварительного просмотра:

  • PreviewView корректно учитывает ориентацию датчика, поворот устройства и масштабирование.
  • Она сохраняет соотношение сторон изображения с камеры, как правило, путем центрирования и обрезки (FILL_CENTER).
  • При необходимости можно установить тип масштабирования FIT_CENTER , чтобы отобразить предварительный просмотр с черными полосами по бокам.

Для получения более подробной информации см. раздел «Реализация предварительного просмотра» в документации CameraX.

Решение 2: Видоискатель камеры

Если вы используете существующий код Camera2, библиотека CameraViewfinder (обратно совместимая с API уровня 21) — это еще одно современное решение. Она упрощает отображение видеопотока с камеры, используя TextureView или SurfaceView и применяя все необходимые преобразования (соотношение сторон, масштаб и вращение).

Для получения более подробной информации см. статью в блоге «Представляем Camera Viewfinder» и руководство разработчика по предварительному просмотру Camera .

Решение 3: Ручная реализация Camera2

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

  • Определите ориентацию сенсора камеры (например, 0, 90, 180, 270 градусов) с помощью CameraCharacteristics .
  • Получите текущий угол поворота дисплея устройства (например, 0, 90, 180, 270 градусов).
  • Используйте эти два значения, чтобы определить необходимые преобразования для вашего SurfaceView или TextureView .
  • Убедитесь, что соотношение сторон выходного изображения Surface соответствует соотношению сторон изображения, полученного с камеры, чтобы избежать искажений.
  • Приложение камеры может работать в части экрана, либо в многооконном режиме, либо в оконном режиме рабочего стола, либо на подключенном дисплее. По этой причине размер экрана не следует использовать для определения размеров видоискателя камеры; вместо этого используйте метрики окна .

Для получения дополнительной информации см. руководство разработчика по предварительной версии приложения «Камера» и видеоролик «Ваше приложение «Камера» на различных форм-факторах» .

Решение 4: Выполнение основных действий с камерой с помощью намерения.

Если вам не требуется множество функций камеры, простое и понятное решение — выполнять основные действия с камерой, такие как фото- или видеосъемка, используя стандартное приложение камеры устройства. Вам не нужно интегрироваться с библиотекой камер; вместо этого используйте Intent .

Для получения дополнительной информации см. раздел «Намерения камеры» .

Конфигурация и непрерывность

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

  • Сохранение состояния приложения без сбоев или внесения изменений, нарушающих работу приложения (например, при переключении экранов или переводе приложения в фоновый режим).
  • Положение прокрутки прокручиваемых полей
  • Введенный текст в текстовые поля и состояние клавиатуры.
  • Воспроизведение медиафайлов возобновляется с того места, где оно было прервано при изменении конфигурации.

К числу часто используемых параметров конфигурации относятся screenSize , smallestScreenSize , screenLayout , orientation , density , fontScale , touchscreen и keyboard .

См. android:configChanges и Handle configuration changes . Дополнительную информацию об управлении состоянием приложения см. в разделе Save UI states .

Изменения конфигурации плотности

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

Конфигурация AndroidManifest.xml

  • density : Указывает, что приложение будет обрабатывать изменение плотности экрана.
  • Другие изменения конфигурации: Также полезно указать другие часто встречающиеся изменения конфигурации, например, screenSize , orientation , keyboardHidden , fontScale и так далее.

Указание плотности (и других изменений конфигурации) предотвращает перезапуск активности системой и вместо этого вызывает метод onConfigurationChanged().

реализация метода onConfigurationChanged()

При изменении плотности необходимо обновить ресурсы (например, перезагрузить растровые изображения или пересчитать размеры макета) в функции обратного вызова:

  • Убедитесь, что значение DPI изменилось на newConfig.densityDpi
  • Сбросьте пользовательские представления, пользовательские элементы отображения и т.д. до новой плотности.

Ресурсные элементы для обработки

  • Ресурс изображения : Замените растровые изображения и графические ресурсы ресурсами, специфичными для плотности, или отрегулируйте масштаб напрямую.
  • Единица компоновки (преобразование dp в px) : пересчитать размер представления, поля и отступы.
  • Размер шрифта и текста : Повторно применить размер текста в единицах sp.
  • Пользовательский View / Рисование Canvas : Обновите значения в пикселях, используемые для рисования Canvas

Определение ориентации приложения

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

Решение 1: Используйте Configuration.orientation

Это свойство определяет ориентацию, в которой в данный момент отображается ваше приложение.

Решение 2: Используйте WindowMetrics#getBounds()

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

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

Позы и режимы отображения

Как для складных моделей в вертикальном, так и в горизонтальном положении поддерживаются различные положения и состояния в сложенном виде, такие как «стол» и HALF_OPENED . Однако складные модели в трехсекционном формате не поддерживают положение «стол» и не могут использоваться HALF_OPENED . В полностью разложенном виде складные модели в трехсекционном формате предлагают больший экран для уникального пользовательского опыта.

Чтобы выделить ваше приложение на складных устройствах, поддерживающих режим HALF_OPENED , используйте API Jetpack WindowManager, такие как FoldingFeature .

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

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

Фиксация ориентации в соответствии с естественной ориентацией датчика.

Для очень специфических случаев использования — в частности, для приложений, которым необходимо занимать весь экран независимо от состояния устройства в сложенном виде — флаг nosensor позволяет заблокировать приложение в соответствии с естественной ориентацией устройства. Например, на Pixel Fold естественная ориентация устройства в сложенном состоянии — портретная, а в разложенном — альбомная. Добавление флага nosensor заставляет приложение блокироваться в портретном режиме при работе на внешнем дисплее и в альбомном режиме при работе на внутреннем дисплее.

<activity
  android:name=".MainActivity"
  android:screenOrientation="nosensor">

Игры и переназначение XR-датчиков

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

Для решения этой проблемы проверьте текущий вызов Display.getRotation() и соответствующим образом переназначьте оси:

  • Вращение 0 : x=x, y=y
  • Поворот на 90 градусов : x=-y, y=x
  • Поворот на 180 градусов : x=-x, y=-y
  • Вращение 270° : x=y, y=-x

Для векторов вращения (используемых в компасах или приложениях XR) используйте SensorManager.remapCoordinateSystem() , чтобы сопоставить направление объектива камеры или верхнюю часть экрана с новыми осями на основе текущего вращения.

Совместимость приложений

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

Для получения дополнительной информации ознакомьтесь с полным списком обходных решений проблем совместимости, предоставленных платформой, в частности, тех, которые связаны с предварительным просмотром камеры , переопределениями и изменениями API Android 16 , которые могут повлиять на поведение вашего приложения.

Чтобы узнать больше о создании адаптивных приложений, см. раздел «Качество приложений для больших экранов» .