Макеты в представлениях
Макет определяет структуру пользовательского интерфейса в приложении, например в действии. Все элементы макета создаются с помощью иерархии объектов 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 следующим образом:
Kotlin
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); }
При запуске Activity фреймворк Android вызывает метод обратного вызова onCreate() в Activity. Дополнительную информацию о жизненных циклах объектов activity вы можете найти в статье Введение в объекты activity.
Атрибуты
Каждый объект View и ViewGroup поддерживает свой набор атрибутов XML. Некоторые атрибуты относятся к объекту View. Например, TextView поддерживает атрибут textSize. Однако эти атрибуты также наследуются любыми объектами View, которые расширяют этот класс. Некоторые из них являются общими для всех объектов View, поскольку они унаследованы от корневого класса View, например атрибут id. Другие атрибуты считаются параметрами макета, которые описывают определенные ориентации макета объекта View, как определено родительским объектом ViewGroup этого объекта.
ID
С каждым объектом View можно связать целочисленный идентификатор, который будет
уникальным View в пределах дерева. При компиляции приложения этот идентификатор указывается как целое число, но обычно он назначается в XML-файле макета как строка в атрибуте id. Это атрибут XML, общий для всех объектов View и определенный классом View. Вы используете его очень часто. Синтаксис для идентификатора внутри
XML-тега выглядит следующим образом:
android:id="@+id/my_button"
Символ @ в начале строки указывает, что синтаксический анализатор XML должен обработать и расширить остальную часть строки идентификатора и распознать ее как ресурс идентификатора. Символ плюса (+) означает, что это новое название ресурса, которое нужно создать и добавить в ресурсы в файле 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(), как показано в следующем примере:Kotlin
val myButton: Button = findViewById(R.id.my_button)
Java
Button myButton = (Button) findViewById(R.id.my_button);
При создании 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. Это поможет обеспечить правильное отображение приложения на экранах разных размеров. Допустимые типы измерений определены в ресурсе разметки.
Позиция макета
Область просмотра имеет прямоугольную форму. У него есть местоположение, выраженное в виде пары координат left и top, и два размера, выраженные в виде ширины и высоты. Единица измерения для местоположения и размеров – пиксель.
Вы можете получить местоположение представления, вызвав методы getLeft() и getTop().
Первый метод возвращает левую координату (x) прямоугольника, представляющего область просмотра. Последний возвращает верхнюю координату (y) прямоугольника, представляющего представление. Эти методы возвращают местоположение представления относительно его родительского элемента. Например, если getLeft() возвращает 20, это означает, что представление находится на 20 пикселей правее левого края своего непосредственного родительского элемента.
Кроме того, есть удобные методы, позволяющие избежать ненужных вычислений: getRight() и getBottom().
Эти методы возвращают координаты правого и нижнего краев прямоугольника, представляющего представление. Например, вызов getRight() аналогичен следующему вычислению: getLeft() + getWidth().
Размер, поля и отступы
Размер представления определяется его шириной и высотой. У представления есть две пары значений ширины и высоты.
Первая пара – это измеренная ширина и измеренная высота. Эти размеры определяют, насколько большим должно быть представление в родительском элементе. Получить измеренные размеры можно с помощью методов getMeasuredWidth() и getMeasuredHeight().
Вторая пара – это ширина и высота (иногда ширина отрисовки и высота отрисовки). Эти размеры определяют фактический размер представления на экране во время рисования и после макета. Эти значения могут отличаться от измеренных ширины и высоты. Ширину и высоту можно получить, вызвав методы getWidth() и getHeight().
Чтобы измерить размеры, представление учитывает его отступы. Отступы указываются в пикселях для левой, верхней, правой и нижней частей представления.
Вы можете использовать отступы, чтобы сместить контент на определенное количество пикселей. Например, если задать отступ слева, равный двум, то контент будет сдвинут на два пикселя вправо от левого края. Вы можете задать отступы с помощью метода setPadding(int, int, int, int) и запросить их, вызвав getPaddingLeft(), getPaddingTop(), getPaddingRight() и getPaddingBottom().
Хотя представление может определять отступы, оно не поддерживает поля. Однако группы представлений поддерживают поля. Дополнительную информацию можно найти в статьях 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с помощью конструктора, чтобы указать макет для каждой строки и массива строк:Kotlin
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:Kotlin
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вы хотите включить в макет для каждого результата, и целочисленный массив, указывающий соответствующие представления, в которых должен быть размещен каждый столбец:Kotlin
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с результатами и два следующих массива:Kotlin
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. Пример:
Kotlin
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.


