Коротко — нельзя позиционировать элементы «жёстко» по пикселям или привязываться к конкретному экрану. Надёжный способ — использовать адаптивные контейнеры и относительные привязки, единицы dp/sp, ресурсы под разные экраны и тестирование на разных устройствах. Ниже — подробное руководство с примерами и практическими советами.
1) Основные правила
- Используйте ConstraintLayout (рекомендуется) или комбинацию LinearLayout / RelativeLayout только при необходимости. Никогда не используйте AbsoluteLayout.
- Размеры в интерфейсе задавайте в dp (плотностно-независимые пиксели) для размеров и в sp — для текста.
- Не жёстко задавайте координаты/пиксели. Применяйте относительные привязки, веса, направляющие и цепочки.
- Для изображений используйте vector drawables или предоставляйте несколько размеров в drawable- папках (mdpi, hdpi, xhdpi...).
- Для длинного содержимого используйте ScrollView / NestedScrollView, для списков — RecyclerView.
2) ConstraintLayout — что и почему
ConstraintLayout даёт «плоскую» и гибкую систему связей: привязываете стороны элементов друг к другу или к родителю, можно ставить bias (смещение), создать цепочки и направляющие, проценты и барьеры. Это позволяет интерфейсу адаптироваться к разной ширине/высоте экрана без вложенных layout'ов.
Пример простого макета:
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Заголовок"
android:layout_margin="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Кнопка"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Ключевой момент: android:layout_width="0dp" в ConstraintLayout означает «match constraints» — ширина заполняется между ограничениями. Это часто правильнее, чем match_parent.
Полезные фичи ConstraintLayout:
- Guideline (app:layout_constraintGuide_percent) — позиционирование по проценту от размера родителя.
- Barrier — динамическая «граница» по размеру нескольких элементов.
- Chains — распределяют элементы в строке/столбце равномерно или с приоритетом.
- app:layout_constraintHorizontal_bias / Vertical — смещение внутри двух привязок (0..1).
3) LinearLayout + веса
Если нужен простой горизонтальный (или вертикальный) раздел на части, можно использовать веса:
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"/>
</LinearLayout>
Здесь 0dp + weight означает распределение пространства по весам.
4) Размеры, отступы, текст
- Используйте dp для отступов/размеров (например, 8dp, 16dp).
- Для текста — sp (scale-independent pixels), чтобы учитывать настройки размера шрифта у пользователя.
- Не увеличивайте шрифты до фиксированных больших значений для маленьких экранов — лучше использовать autosize TextView или разные текстовые размеры в ресурсах для размеров экранов.
5) Ресурсы для разных экранов и ориентаций
- Если интерфейс сильно меняется на планшете/широких экранах, создавайте отдельные layout-ресурсы:
res/layout/ — телефоны
res/layout-sw600dp/ — экраны с min width 600dp (обычно 7"+ планшеты)
res/layout-large/, res/layout-land/ — для ландшафтной ориентации
- Для картинок используйте drawable-mdpi/hdpi/xhdpi/xxhdpi или векторные drawable (recommended).
- Для разных размеров текста/отступов можно иметь несколько values файлов: values/, values-sw600dp/, где в dimens.xml задать другие величины.
6) Тестирование и инструменты
- В Android Studio в Layout Editor используйте Preview -> "Select Device" и «Preview all screen sizes» для быстрого просмотра.
- Запускайте приложение на эмуляторах с разными разрешениями/соотношениями сторон и на реальных устройствах при возможности.
- Используйте Layout Inspector и "Show Layout Bounds" в настройках разработчика на устройстве.
- Для времени разработки используйте tools: атрибуты (tools:text, tools:visibility) — они видны только в редакторе.
7) Работа с системными панелями и безопасными зонами
- Учитывайте статус-бар и навигационную панель. Лучше использовать CoordinatorLayout / fitsSystemWindows / WindowInsets, если нужен контент под панелями.
- Для современных устройств с вырезами (notches) используйте WindowInsets / DisplayCutout API.
8) Другие практические советы
- Не задавайте фиксированную ширину/высоту в px. Избегайте magic numbers.
- Старайтесь минимизировать глубину вложенности layout'ов — производительность важна. ConstraintLayout помогает держать структуру плоской.
- Для динамически создаваемых интерфейсов используйте RecyclerView.
- Если в одном экране много элементов, подумайте о ScrollView.
- Для картинок с растягиванием используйте 9-patch, когда нужно сохранять углы/отступы.
9) Пример: кнопки внизу экрана, заголовок сверху, контент между — на всех экранах
<androidx.constraintlayout.widget.ConstraintLayout ...>
<TextView
android:id="@+id/header"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/footerBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintBottom_toTopOf="@id/footerBtn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- контент -->
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
10) Контрольный список (чтобы всё корректно отображалось)
- Использую dp/sp, не px.
- Элементы привязаны относительными constraints / weight / guideline, а не фикс. координатами.
- Тесты на разных размерах и плотностях (Preview + эмуляторы).
- Для планшетов/широких экранов есть отдельные макеты (layout-sw600dp и т. п.), если нужно.
- Изображения корректно масштабируются (vector или несколько плотностей / 9-patch).
- Пользовательский текст обрезается/переносится или размер текста авто-подстраивается.
Если хотите, пришлите XML вашего проблемного макета — посмотрю и скажу, что именно исправить (переписать на ConstraintLayout, исправить ширины, использовать 0dp и т.д.).