Макеты в представлениях
Макет определяет структуру пользовательского интерфейса в вашем приложении, например, в активности . Все элементы в макете создаются с использованием иерархии объектов View и ViewGroup . View обычно отображает то, что пользователь может видеть и с чем может взаимодействовать. ViewGroup — это невидимый контейнер, который определяет структуру макета для View и других объектов ViewGroup , как показано на рисунке 1.

Объекты View часто называют виджетами , и они могут быть одним из многих подклассов, таких как Button или TextView . Объекты ViewGroup обычно называют макетами , и они могут быть одного из многих типов, предоставляющих различную структуру макета, например, LinearLayout или ConstraintLayout .
Макет можно задать двумя способами:
- Объявляйте элементы пользовательского интерфейса в XML. Android предоставляет простой XML-словарь, соответствующий классам и подклассам
View, например, виджетам и макетам. Вы также можете использовать редактор макетов Android Studio для создания XML-макета с помощью интерфейса перетаскивания. - Создавайте экземпляры элементов макета во время выполнения. Ваше приложение может создавать объекты
ViewиViewGroupи программно управлять их свойствами.
Объявление пользовательского интерфейса в XML позволяет отделить представление приложения от кода, управляющего его поведением. Использование XML-файлов также упрощает предоставление различных макетов для разных размеров и ориентаций экрана. Подробнее об этом рассказывается в разделе «Поддержка разных размеров экрана» .
Фреймворк Android предоставляет вам гибкость в использовании одного или обоих этих методов для создания пользовательского интерфейса вашего приложения. Например, вы можете объявить макеты вашего приложения по умолчанию в XML, а затем изменять макет во время выполнения.
Напишите XML-код
Используя XML-словарь Android, вы можете быстро создавать макеты пользовательского интерфейса и содержащиеся в них элементы экрана, подобно тому, как вы создаете веб-страницы в HTML с помощью ряда вложенных элементов.
Каждый файл разметки должен содержать ровно один корневой элемент, который должен быть объектом View или ViewGroup . После определения корневого элемента вы можете добавлять дополнительные объекты разметки или виджеты в качестве дочерних элементов, чтобы постепенно создавать иерархию View , определяющую вашу разметку. Например, вот XML-разметка, которая использует вертикальный LinearLayout для размещения TextView и Button :
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello, I am a Button" /> </LinearLayout>
После того, как вы объявите макет в XML, сохраните файл с расширением .xml в каталоге res/layout/ вашего Android-проекта, чтобы он корректно скомпилировался.
Для получения дополнительной информации о синтаксисе XML-файла макета см. раздел «Ресурсы макета» .
Загрузите XML-ресурс
При компиляции вашего приложения каждый XML-файл разметки компилируется в ресурс View . Загрузите ресурс разметки в реализации колбэка Activity.onCreate() вашего приложения. Для этого вызовите setContentView() , передав ему ссылку на ваш ресурс разметки в формате: R.layout. layout_file_name . Например, если ваша XML-разметка сохранена как main_layout.xml , загрузите её для вашей Activity следующим образом:
Котлин
fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.main_layout) }
Java
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_layout); }
В Android-фреймворке метод обратного вызова onCreate() вызывается в вашем Activity при Activity запуске. Дополнительную информацию о жизненном цикле Activity см. в разделе «Введение в Activity» .
Атрибуты
Каждый объект View и ViewGroup поддерживает свой собственный набор XML-атрибутов. Некоторые атрибуты специфичны для объекта View . Например, TextView поддерживает атрибут textSize . Однако эти атрибуты также наследуются любыми объектами View , которые расширяют этот класс. Некоторые атрибуты являются общими для всех объектов View , поскольку они наследуются от корневого класса View , например, атрибут id . Другие атрибуты считаются параметрами макета , которые описывают определенные ориентации макета объекта View , как это определено родительским объектом ViewGroup .
ИДЕНТИФИКАТОР
Любой объект View может иметь связанный с ним целочисленный идентификатор (ID), который однозначно идентифицирует View в дереве. При компиляции приложения этот ID указывается как целое число, но обычно в XML-файле макета он присваивается в виде строки в атрибуте id . Это XML-атрибут, общий для всех объектов View , и он определяется классом View . Вы используете его очень часто. Синтаксис для ID внутри XML-тега выглядит следующим образом:
android:id="@+id/my_button"
Символ @ в начале строки указывает на то, что XML-парсер анализирует и расширяет остальную часть строки ID и идентифицирует её как ресурс ID. Символ плюс (+) означает, что это новое имя ресурса, которое необходимо создать и добавить в список ресурсов в файле R.java .
Фреймворк Android предлагает множество других ресурсов с идентификаторами. При ссылке на идентификатор ресурса Android символ плюса не требуется, но необходимо добавить пространство имен пакета android следующим образом:
android:id="@android:id/empty"
Пространство имен пакета android указывает на то, что вы ссылаетесь на идентификатор из класса ресурсов android.R , а не из класса локальных ресурсов.
Для создания представлений и ссылки на них из вашего приложения можно использовать следующий распространенный шаблон:
- Определите представление в файле макета и присвойте ему уникальный идентификатор, как показано в следующем примере:
<Button android:id="@+id/my_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/my_button_text"/>
- Создайте экземпляр объекта представления и получите его из макета, как правило, в методе
onCreate(), как показано в следующем примере:Котлин
val myButton: Button = findViewById(R.id.my_button)
Java
Button myButton = (Button) findViewById(R.id.my_button);
Определение идентификаторов для объектов представления важно при создании RelativeLayout . В RelativeLayout соседние представления могут определять свою компоновку относительно другого соседнего представления, на которое ссылается уникальный идентификатор.
Идентификатор не обязательно должен быть уникальным во всем дереве, но он должен быть уникальным в той части дерева, где вы выполняете поиск. Часто это может быть все дерево целиком, поэтому лучше, по возможности, сделать его уникальным.
Параметры компоновки
Атрибуты XML-разметки с именем layout_ something определяют параметры разметки для View , которые соответствуют ViewGroup в которой оно находится.
Каждый класс ViewGroup реализует вложенный класс, который наследует ViewGroup.LayoutParams . Этот подкласс содержит типы свойств, определяющие размер и положение каждого дочернего представления в соответствии с группой представлений. Как показано на рисунке 2, родительская группа представлений определяет параметры компоновки для каждого дочернего представления, включая дочернюю группу представлений.

Каждый подкласс LayoutParams имеет свой собственный синтаксис для установки значений. Каждый дочерний элемент должен определить LayoutParams , подходящий для его родительского элемента, хотя он также может определить другой LayoutParams для своих собственных дочерних элементов.
Все группы представлений включают ширину и высоту, заданные с помощью layout_width и layout_height , и каждое представление должно определять их самостоятельно. Многие LayoutParams содержат необязательные поля и границы.
Вы можете указать ширину и высоту с помощью точных измерений, но это может быть нечасто необходимо. Чаще всего для задания ширины или высоты используется одна из следующих констант:
-
wrap_content: указывает вашему представлению изменять свой размер в соответствии с размерами, необходимыми для его содержимого. -
match_parent: указывает вашему представлению стать настолько большим, насколько это позволяет родительская группа представлений.
В целом, мы не рекомендуем указывать ширину и высоту макета в абсолютных единицах, таких как пиксели. Лучше использовать относительные измерения, такие как независимые от плотности пиксельные единицы (dp), wrap_content или match_parent , поскольку это помогает вашему приложению корректно отображаться на экранах устройств различных размеров. Допустимые типы измерений определены в ресурсе Layout .
Положение компоновки
Объект представления имеет прямоугольную геометрию. Он имеет местоположение, выраженное парой координат «слева» и «сверху» , и два измерения, выраженные как ширина и высота. Единицей измерения местоположения и размеров является пиксель.
Вы можете получить местоположение представления, вызвав методы getLeft() и getTop() . Первый возвращает левую ( x ) координату прямоугольника, представляющего представление. Второй возвращает верхнюю ( y ) координату прямоугольника, представляющего представление. Эти методы возвращают местоположение представления относительно его родительского элемента. Например, когда getLeft() возвращает 20, это означает, что представление расположено на 20 пикселей правее левого края своего непосредственного родительского элемента.
Кроме того, существуют удобные методы, позволяющие избежать ненужных вычислений: а именно getRight() и getBottom() . Эти методы возвращают координаты правого и нижнего краев прямоугольника, представляющего собой представление. Например, вызов getRight() аналогичен следующему вычислению: getLeft() + getWidth() .
Размер, отступы и поля
Размер представления выражается шириной и высотой. Представление имеет две пары значений ширины и высоты.
Первая пара параметров называется измеренной шириной и измеренной высотой . Эти размеры определяют, насколько большим может быть представление внутри родительского элемента. Измеренные размеры можно получить, вызвав методы getMeasuredWidth() и getMeasuredHeight() .
Вторая пара параметров называется шириной и высотой , или иногда шириной и высотой отрисовки. Эти размеры определяют фактический размер представления на экране во время отрисовки и после компоновки. Эти значения могут, но не обязательно, отличаться от измеренных ширины и высоты. Ширину и высоту можно получить, вызвав методы getWidth() и getHeight() .
Для измерения своих размеров представление учитывает свой отступ (padding). Отступ выражается в пикселях для левой, верхней, правой и нижней частей представления. Вы можете использовать отступ для смещения содержимого представления на определенное количество пикселей. Например, левый отступ, равный двум, сдвигает содержимое представления на два пикселя вправо от левого края. Вы можете установить отступ с помощью метода setPadding(int, int, int, int) и запросить его, вызвав методы getPaddingLeft() , getPaddingTop() , getPaddingRight() и getPaddingBottom() .
Хотя представление может задавать отступы (padding), оно не поддерживает поля (margins). Однако группы представлений поддерживают поля. Дополнительную информацию см. в описаниях ViewGroup и ViewGroup.MarginLayoutParams .
Для получения более подробной информации о размерах см. раздел «Размеры» .
Помимо программной установки полей и отступов, вы также можете задать их в XML-макетах, как показано в следующем примере:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:padding="8dp" android:text="Hello, I am a TextView" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:paddingBottom="4dp" android:paddingEnd="8dp" android:paddingStart="8dp" android:paddingTop="4dp" android:text="Hello, I am a Button" /> </LinearLayout>
В приведенном выше примере показано применение полей и отступов. Для TextView поля и отступы применены равномерно по всему периметру, а для Button показано, как их можно применять независимо к разным краям.
Стандартные схемы расположения элементов
Каждый подкласс класса ViewGroup предоставляет уникальный способ отображения вложенных в него представлений. Наиболее гибким типом компоновки, предоставляющим лучшие инструменты для поддержания неглубокой иерархии компоновки, является ConstraintLayout .
Ниже перечислены некоторые распространенные типы макетов, встроенные в платформу Android.

Организует дочерние элементы в один горизонтальный или вертикальный ряд и создает полосу прокрутки, если длина окна превышает длину экрана.
Создавайте динамические списки
Если содержимое вашего макета динамическое или не предопределено, вы можете использовать RecyclerView или подкласс AdapterView . RecyclerView как правило, является лучшим вариантом, поскольку он использует память более эффективно, чем AdapterView .
К числу распространенных вариантов компоновки, возможных при использовании RecyclerView и AdapterView , относятся следующие:
RecyclerView предоставляет больше возможностей и позволяет создать собственный менеджер компоновки .
Заполните представление адаптера данными.
Вы можете заполнить AdapterView например ListView или GridView привязав экземпляр AdapterView к Adapter , который получает данные из внешнего источника и создает View , представляющий каждую запись данных.
Android предоставляет несколько подклассов Adapter , которые полезны для получения различных типов данных и построения представлений для AdapterView . Два наиболее распространенных адаптера:
-
ArrayAdapter - Используйте этот адаптер, если ваш источник данных — массив. По умолчанию
ArrayAdapterсоздает представление для каждого элемента массива, вызываяtoString()для каждого элемента и помещая его содержимое вTextView.Например, если у вас есть массив строк, которые вы хотите отобразить в
ListView, инициализируйте новыйArrayAdapterиспользуя конструктор, чтобы указать макет для каждой строки и массива строк:Котлин
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
Java
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray);
Аргументами для этого конструктора являются следующие:
-
Contextвашего приложения - Макет, содержащий
TextViewдля каждой строки в массиве. - Массив строк
Затем вызовите
setAdapter()для вашегоListView:Котлин
val listView: ListView = findViewById(R.id.listview) listView.adapter = adapter
Java
ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(adapter);
Чтобы настроить внешний вид каждого элемента, вы можете переопределить метод
toString()для объектов в вашем массиве. Или, чтобы создать для каждого элемента представление, отличное отTextView— например, если вы хотитеImageViewдля каждого элемента массива — расширьте классArrayAdapterи переопределитеgetView(), чтобы он возвращал тип представления, который вы хотите для каждого элемента. -
-
SimpleCursorAdapter - Используйте этот адаптер, если ваши данные поступают из
Cursor. При использованииSimpleCursorAdapterукажите макет для каждой строкиCursorи столбцыCursor, которые вы хотите вставить в представления нужного макета. Например, если вы хотите создать список имен и номеров телефонов людей, вы можете выполнить запрос, который вернетCursorсодержащий строку для каждого человека и столбцы для имен и номеров. Затем вы создаете строковый массив, указывающий, какие столбцы изCursorвы хотите включить в макет для каждого результата, и целочисленный массив, указывающий соответствующие представления, в которые должен быть помещен каждый столбец:Котлин
val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER) val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
Java
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; int[] toViews = {R.id.display_name, R.id.phone_number};
При создании экземпляра
SimpleCursorAdapterпередайте макет, используемый для каждого результата,Cursorсодержащий результаты, и эти два массива:Котлин
val adapter = SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0) val listView = getListView() listView.adapter = adapter
Java
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0); ListView listView = getListView(); listView.setAdapter(adapter);
Затем
SimpleCursorAdapterсоздает представление для каждой строки вCursor, используя предоставленную разметку, путем вставки каждого элементаfromColumnsв соответствующее представлениеtoViews.
Если в процессе работы вашего приложения вы изменяете базовые данные, считываемые адаптером, вызовите notifyDataSetChanged() . Это уведомит подключенное представление об изменении данных, и оно обновится.
Обработка событий клика
Вы можете реагировать на события клика по каждому элементу в AdapterView , реализовав интерфейс AdapterView.OnItemClickListener . Например:
Котлин
listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> // Do something in response to the click. }
Java
// Create a message handling object as an anonymous class. private OnItemClickListener messageClickedHandler = new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { // Do something in response to the click. } }; listView.setOnItemClickListener(messageClickedHandler);
Дополнительные ресурсы
Посмотрите, как используются элементы макета в демонстрационном приложении Sunflower на GitHub.



