Макеты в представлениях
Макет определяет структуру пользовательского интерфейса в вашем приложении, например в действии . Все элементы макета построены с использованием иерархии объектов 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) }
Ява
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_layout); }
Платформа Android вызывает метод обратного вызова onCreate()
в вашем Activity
при запуске Activity
. Дополнительные сведения о жизненных циклах действий см. в разделе Введение в действия .
Атрибуты
Каждый объект View
и ViewGroup
поддерживает собственный набор атрибутов XML. Некоторые атрибуты специфичны для объекта View
. Например, TextView
поддерживает атрибут textSize
. Однако эти атрибуты также наследуются любыми объектами View
, расширяющими этот класс. Некоторые из них являются общими для всех объектов View
, поскольку они наследуются от корневого класса View
, например атрибут id
. Другие атрибуты считаются параметрами макета , которые являются атрибутами, описывающими определенные ориентации макета объекта View
, как определено родительским объектом ViewGroup
этого объекта.
ИДЕНТИФИКАТОР
Любой объект 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()
, как показано в следующем примере:Котлин
val myButton: Button = findViewById(R.id.my_button)
Ява
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
, поскольку это помогает вашему приложению правильно отображаться на экранах различных размеров. Принятые типы измерений определены в ресурсе макета .
Положение макета
Вид имеет прямоугольную геометрию. Он имеет местоположение, выраженное как пара левой и верхней координат, и два измерения, выраженные как ширина и высота. Единицей местоположения и размеров является пиксель.
Вы можете получить местоположение представления, вызвав методы 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
, используя конструктор, чтобы указать макет для каждой строки и массива строк:Котлин
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
Ява
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
Ява
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)
Ява
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
Ява
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. }
Ява
// 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.