Классы размеров окон — это набор точек останова области просмотра, которые помогают проектировать, разрабатывать и тестировать адаптивные макеты. Точки останова сочетают простоту макета с гибкостью оптимизации вашего приложения для уникальных случаев.
Классы размеров окон классифицируют область отображения, доступную вашему приложению, на компактную , среднюю или расширенную . Доступная ширина и высота классифицируются отдельно, поэтому в любой момент времени ваше приложение имеет два класса размеров окон — один для ширины, другой для высоты. Доступная ширина обычно более важна, чем доступная высота из-за повсеместного распространения вертикальной прокрутки, поэтому класс размера окна ширины, вероятно, более актуален для пользовательского интерфейса вашего приложения.
Как показано на рисунках, точки останова позволяют вам продолжать думать о макетах с точки зрения устройств и конфигураций. Каждая точка останова класса размера представляет собой большинство случаев для типичных сценариев устройства, что может быть полезным ориентиром при проектировании макетов на основе точек останова.
Класс размера | Точка останова | Представление устройства |
---|---|---|
Компактная ширина | ширина <600 дп | 99,96% телефонов в портретной ориентации |
Средняя ширина | 600 dp ≤ ширина < 840 dp | 93,73% планшетов в портретной ориентации, самые большие внутренние дисплеи в развернутом виде в портретном режиме |
Расширенная ширина | ширина ≥ 840 дп | 97,22% планшетов в альбомной ориентации, самые большие развернутые внутренние дисплеи в альбомной ориентации |
Компактная высота | высота < 480 дп | 99,78% телефонов в альбомной ориентации |
Средний рост | 480dp ≤ высота < 900dp | 96,56% планшетов в альбомной ориентации, 97,59% телефонов в портретной ориентации |
Расширенная высота | высота ≥ 900 дп | 94,25% планшетов в портретной ориентации |
Хотя визуализация классов размеров как физических устройств может быть полезной, классы размеров окон явно не определяются размером экрана устройства. Классы размеров окон не предназначены для логики типа isTablet . Скорее, классы размеров окон определяются размером окна, доступным вашему приложению, независимо от типа устройства, на котором приложение работает, что имеет два важных последствия:
Физические устройства не гарантируют определенный класс размера окна. Пространство экрана, доступное вашему приложению, может отличаться от размера экрана устройства по многим причинам. На мобильных устройствах режим разделенного экрана позволяет разделить экран между двумя приложениями. В ChromeOS приложения Android могут отображаться в окнах настольного типа, размер которых можно произвольно изменять. Складные устройства могут иметь два экрана разного размера, к которым можно получить индивидуальный доступ, складывая или раскладывая устройство.
Класс размера окна может меняться на протяжении всего времени существования вашего приложения. Во время работы вашего приложения изменение ориентации устройства, многозадачность, а также складывание/раскладывание могут изменить объем доступного места на экране. В результате класс размера окна является динамическим, и пользовательский интерфейс вашего приложения должен адаптироваться соответствующим образом.
Классы размеров окон сопоставляются с компактными, средними и расширенными точками останова в руководстве по макету Material Design . Используйте классы размеров окон для принятия решений по макету приложения на высоком уровне, например, при принятии решения о том, следует ли использовать конкретный канонический макет, чтобы использовать дополнительное пространство экрана.
Вы можете вычислить текущий WindowSizeClass
с помощью функции WindowSizeClass#compute()
предоставляемой библиотекой Jetpack WindowManager . В следующем примере показано, как вычислить класс размера окна и получать обновления при каждом изменении класса размера окна:
Котлин
class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged(). This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged(), // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged() is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) computeWindowSizeClasses() } }) computeWindowSizeClasses() } private fun computeWindowSizeClasses() { val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) // COMPACT, MEDIUM, or EXPANDED val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass // COMPACT, MEDIUM, or EXPANDED val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass // Use widthWindowSizeClass and heightWindowSizeClass. } }
Ява
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged(). This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged(), // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged() is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); computeWindowSizeClasses(); } }); computeWindowSizeClasses(); } private void computeWindowSizeClasses() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(this); int width = metrics.getBounds().width int height = metrics.getBounds().height() float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density) // COMPACT, MEDIUM, or EXPANDED WindowWidthSizeClass widthWindowSizeClass = windowSizeClass.getWindowWidthSizeClass() // COMPACT, MEDIUM, or EXPANDED WindowHeightSizeClass heightWindowSizeClass = windowSizeClass.getWindowHeightSizeClass() // Use widthWindowSizeClass and heightWindowSizeClass. } }
Классы размеров тестовых окон
При внесении изменений в макет проверяйте поведение макета во всех размерах окон, особенно при компактной, средней и расширенной ширине точки останова.
Если у вас есть макет для компактных экранов, сначала оптимизируйте его для класса размеров с расширенной шириной, поскольку этот класс размеров предоставляет больше места для дополнительного контента и изменений пользовательского интерфейса. Затем решите, какой макет имеет смысл для класса размеров средней ширины; рассмотрите возможность добавления специализированного макета.
Следующие шаги
Чтобы узнать больше о том, как использовать классы размеров окон для создания адаптивных макетов, см. следующее:
Для макетов на основе Compose: поддержка разных размеров экрана.
Для макетов на основе представлений: адаптивный дизайн с представлениями.
Чтобы узнать больше о том, что делает приложение отличным на всех устройствах и размерах экрана, см.:
- Перенесите свой пользовательский интерфейс на адаптивные макеты
- Качество приложения на большом экране
Классы размеров окон — это набор точек останова области просмотра, которые помогают проектировать, разрабатывать и тестировать адаптивные макеты. Точки останова сочетают простоту макета с гибкостью оптимизации вашего приложения для уникальных случаев.
Классы размеров окон классифицируют область отображения, доступную вашему приложению, на компактную , среднюю или расширенную . Доступная ширина и высота классифицируются отдельно, поэтому в любой момент времени ваше приложение имеет два класса размеров окон — один для ширины, другой для высоты. Доступная ширина обычно более важна, чем доступная высота из-за повсеместного распространения вертикальной прокрутки, поэтому класс размера окна ширины, вероятно, более актуален для пользовательского интерфейса вашего приложения.
Как показано на рисунках, точки останова позволяют вам продолжать думать о макетах с точки зрения устройств и конфигураций. Каждая точка останова класса размера представляет собой большинство случаев для типичных сценариев устройства, что может быть полезным ориентиром при проектировании макетов на основе точек останова.
Класс размера | Точка останова | Представление устройства |
---|---|---|
Компактная ширина | ширина < 600 дп | 99,96% телефонов в портретной ориентации |
Средняя ширина | 600dp ≤ ширина < 840dp | 93,73% планшетов в портретной ориентации, самые большие внутренние дисплеи в развернутом виде в портретном режиме |
Расширенная ширина | ширина ≥ 840 дп | 97,22% планшетов в альбомной ориентации, самые большие развернутые внутренние дисплеи в альбомной ориентации |
Компактная высота | высота < 480 дп | 99,78% телефонов в альбомной ориентации |
Средний рост | 480dp ≤ высота < 900dp | 96,56% планшетов в альбомной ориентации, 97,59% телефонов в портретной ориентации |
Расширенная высота | высота ≥ 900 дп | 94,25% планшетов в портретной ориентации |
Хотя визуализация классов размеров как физических устройств может быть полезной, классы размеров окон явно не определяются размером экрана устройства. Классы размеров окон не предназначены для логики типа isTablet . Скорее, классы размеров окон определяются размером окна, доступным вашему приложению, независимо от типа устройства, на котором приложение работает, что имеет два важных последствия:
Физические устройства не гарантируют определенный класс размера окна. Пространство экрана, доступное вашему приложению, может отличаться от размера экрана устройства по многим причинам. На мобильных устройствах режим разделенного экрана позволяет разделить экран между двумя приложениями. В ChromeOS приложения Android могут отображаться в окнах настольного типа, размер которых можно произвольно изменять. Складные устройства могут иметь два экрана разного размера, к которым можно получить индивидуальный доступ, складывая или раскладывая устройство.
Класс размера окна может меняться на протяжении всего времени существования вашего приложения. Во время работы вашего приложения изменение ориентации устройства, многозадачность, а также складывание/раскладывание могут изменить объем доступного места на экране. В результате класс размера окна является динамическим, и пользовательский интерфейс вашего приложения должен адаптироваться соответствующим образом.
Классы размеров окон сопоставляются с компактными, средними и расширенными точками останова в руководстве по макету Material Design . Используйте классы размеров окон для принятия решений по макету приложения на высоком уровне, например, при принятии решения о том, следует ли использовать конкретный канонический макет, чтобы использовать дополнительное пространство экрана.
Вы можете вычислить текущий WindowSizeClass
с помощью функции WindowSizeClass#compute()
предоставляемой библиотекой Jetpack WindowManager . В следующем примере показано, как вычислить класс размера окна и получать обновления при каждом изменении класса размера окна:
Котлин
class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged(). This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged(), // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged() is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) computeWindowSizeClasses() } }) computeWindowSizeClasses() } private fun computeWindowSizeClasses() { val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) // COMPACT, MEDIUM, or EXPANDED val widthWindowSizeClass = windowSizeClass.windowWidthSizeClass // COMPACT, MEDIUM, or EXPANDED val heightWindowSizeClass = windowSizeClass.windowHeightSizeClass // Use widthWindowSizeClass and heightWindowSizeClass. } }
Ява
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged(). This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged(), // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged() is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); computeWindowSizeClasses(); } }); computeWindowSizeClasses(); } private void computeWindowSizeClasses() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate() .computeCurrentWindowMetrics(this); int width = metrics.getBounds().width int height = metrics.getBounds().height() float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density) // COMPACT, MEDIUM, or EXPANDED WindowWidthSizeClass widthWindowSizeClass = windowSizeClass.getWindowWidthSizeClass() // COMPACT, MEDIUM, or EXPANDED WindowHeightSizeClass heightWindowSizeClass = windowSizeClass.getWindowHeightSizeClass() // Use widthWindowSizeClass and heightWindowSizeClass. } }
Классы размеров тестовых окон
При внесении изменений в макет проверяйте поведение макета во всех размерах окон, особенно при компактной, средней и расширенной ширине точки останова.
Если у вас есть макет для компактных экранов, сначала оптимизируйте его для класса размеров с расширенной шириной, поскольку этот класс размеров предоставляет больше места для дополнительного контента и изменений пользовательского интерфейса. Затем решите, какой макет имеет смысл для класса размеров средней ширины; рассмотрите возможность добавления специализированного макета.
Следующие шаги
Чтобы узнать больше о том, как использовать классы размеров окон для создания адаптивных макетов, см. следующее:
Для макетов на основе Compose: поддержка разных размеров экрана.
Для макетов на основе представлений: адаптивный дизайн с представлениями.
Чтобы узнать больше о том, что делает приложение отличным на всех устройствах и размерах экрана, см.:
- Перенесите свой пользовательский интерфейс на адаптивные макеты
- Качество приложения на большом экране