Основные компоненты приложения.
Диалоговые окна
- Dialog - базовый класс для всех типов диалоговых окон;
- AlertDialog — диалоговое окно с кнопками, списком, флажками или переключателями;
- CharacterPickerDialog - диалоговое окно, позволяющее выбрать символ с ударением, связанный с базовым символом;
- ProgressDiaiog — диалоговое окно с индикатором прогресса при помощи компонента ProgressBar. В API 26 признан устаревшим.
- DatePickerDialog — диалоговое окно выбора даты с элементом DatePicker
- TimePickerDialog — диалоговое окно выбора времени с элементом TimePicker
Класс Dialog
- Класс Dialog является базовым для всех классов диалоговых окон.
- ProgressDialog, TimePickerDialog И DatePickerDialog — расширение класса AlertDialog, они также могут иметь командные кнопки.
- Каждое диалоговое окно должно быть определено внутри активности или фрагмента
- Диалоговое окно можно открыть один раз или несколько раз.
- Для отображения диалогового окна необходимо вызвать метод showDialog()
Класс Dialog
- Метод dismissDialog() прячет диалоговое окно (но не удаляет), не отображая его на экране.
- Окно остаётся в пуле диалоговых окон данной активности.
- При повторном отображении при помощи метода showDialog() будет использована кэшированная версия окна.
- Метод removeDialog() удаляет диалоговое окно из пула окон данной активности.
- При повторном вызове метода showDialog() диалоговое окно придётся создавать снова.
Dialog: пример
Dialog dialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dialog = new Dialog(MainActivity.this);
// Установите заголовок
dialog.setTitle("Заголовок диалога");
// Передайте ссылку на разметку
dialog.setContentView(R.layout.dialog_view);
// Найдите элемент TextView внутри вашей разметки
// и установите ему соответствующий текст
TextView text = (TextView) dialog.findViewById(R.id.dialogTextView);
text.setText("Текст в диалоговом окне. Вы любите котов?");
}
public void onClick(View v)
{
// Выводим диалоговое окно на экран
dialog.show();
}
Методы onCreateDialog() и onPrepareDialog()
- Метод onCreateDialog() вызывается один раз при создании окна.
- После начального создания при каждом вызове метода showDialog() будет срабатывать обработчик onPrepareDialog().
- Переопределив этот метод, вы можете изменять диалоговое окно при каждом его выводе на экран.
- Это позволит привнести контекст в любое из отображаемых значений.
- Если требуется перед каждым вызовом диалогового окна изменять его свойства (например, текстовое сообщение или количество кнопок), то можно реализовать внутри этого метода.
Методы onCreateDialog() и onPrepareDialog()
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case (TIME_DIALOG) :
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
Date currentTime = new Date(java.lang.System.currentTimeMillis());
String dateString = sdf.format(currentTime);
AlertDialog timeDialog = (AlertDialog)dialog;
timeDialog.setMessage(dateString);
break;
}
}
Фрагменты
- Два способа использования фрагментов
- Первый способ основан на замещении родительского контейнера.
- Создаётся стандартная разметка и в том месте, где будут использоваться фрагменты, размещается контейнер, например, FrameLayout.
- В коде контейнер замещается фрагментом.
- При использовании подобного сценария в разметке не используется тег fragment, так как его нельзя менять динамически.
- Также придётся обновлять ActionBar, если он зависит от фрагмента.
Фрагменты
- Второй вариант - используются отдельные разметки для телефонов и планшетов, которые можно разместить в разных папках ресурсов.
- Например, если в планшете используется двухпанельная разметка с двумя фрагментами на одной активности
- используем эту же активность для телефона, но подключаем другую разметку, которая содержит один фрагмент.
- Когда нам нужно переключиться на второй фрагмент, то запускаем вторую активность.
- Второй подход является наиболее гибким и в целом предпочтительным способом использования фрагментов.
Фрагменты: классы
- Сами фрагменты наследуются от androidx.fragment.app.Fragment.
- Существует подклассы фрагментов: ListFragment, DialogFragment, PreferenceFragment, WebViewFragment и др.
- Для взаимодействия между фрагментами используется класс android.app.FragmentManager - специальный менеджер по фрагментам.
- Для транзакций (добавление, удаление, замена) используется класс-помощник android.app.FragmentTransaction.
- В 2018 году Гугл объявила фрагменты из пакета androd.app устаревшими.
- Заменяйте везде на версию из библиотеки совместимости. В 2020 году уже используют пакет androidx.fragment.app.
Фрагменты
- У каждого фрагмента должен быть свой класс.
- Класс наследуется от класса Fragment или схожих классов
- Создаются различные методы типа onCreate() и др.
- Если фрагмент имеет разметку, то используется метод onCreateView() - считайте его аналогом метода setContentView()
- При этом метод onCreateView() возвращает объект View, который является корневым элементом разметки фрагмента.
Фрагменты
public class FirstFragment extends Fragment implements OnClickListener {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.first_fragment,
container, false);
Button nextButton = (Button) view.findViewById(R.id.button_first);
nextButton.setOnClickListener(this);
return view;
}
// ...
}
Фрагменты
- Перед методом findViewById() используется view
- Этот метод относится к компоненту, а не к активности, как обычно делается в программах, когда просто опускали имя активности.
- ищем ссылку на кнопку не среди разметки активности, а внутри разметки самого фрагмента.
- в методе inflate() последний параметр должен иметь значение false в большинстве случаев.
FragmentManager
- Класс FragmentManager имеет два метода, позволяющих найти фрагмент, который связан с активностью:
- findFragmentById(int id) - Находит фрагмент по идентификатору
- findFragmentByTag(String tag) - Находит фрагмент по заданному тегу
Методы транзакции (FragmentTransaction)
- add() - Добавляет фрагмент к активности
- remove() - Удаляет фрагмент из активности
- replace() - Заменяет один фрагмент на другой
- hide() - Прячет фрагмент (делает невидимым на экране)
- show() - Выводит скрытый фрагмент на экран
- detach() (API 13) - Отсоединяет фрагмент от графического интерфейса, но экземпляр класса сохраняется
- attach() (API 13) - Присоединяет фрагмент, который был отсоединён методом detach()
Методы транзакции (FragmentTransaction)
- Методы remove(), replace(), detach(), attach() не применимы к статичным фрагментам.
- Перед началом транзакции нужно получить экземпляр FragmentTransaction через метод FragmentManager.beginTransaction().
- В конце любой транзакции, которая может состоять из цепочки вышеперечисленных методов, следует вызвать метод commit().
FragmentManager fragmentManager = getFragmentManager()
fragmentManager.beginTransaction()
.remove(fragment1)
.add(R.id.fragment_container, fragment2)
.show(fragment3)
.hide(fragment4)
.commit();
Адаптеры
- Адаптеры упрощают связывание данных с элементом управления.
- Адаптеры используются при работе с виджетами, которые дополняют android.widget.AdapterView: ListView, ExpandableListView, GridView, Spinner, Gallery
- AdapterView дополняет android.widget.ViewGroup
- Назначение адаптера заключается в том, чтобы предоставлять дочерние view для контейнера.
- Адаптер берет данные и метаданные определенного контейнера и строит каждый дочерний вид.
- Например, мы формируем пункты списка (массив строк) и передаём его списку ListView.
Готовые адаптеры
- ArrayAdapter - предназначен для работы с ListView. Данные представлены в виде массива, которые размещаются в отдельных элементах TextView
- ListAdapter - адаптер между ListView и данными. Строго говоря, это класс-интерфейс, который можно использовать и в ArrayAdapter и в SimpleAdapter и т.д.
- SpinnerAdapter - адаптер для связки данных с элементом Spinner. Это тоже интерфейс, как ListAdapter и работает по схожему принципу
- SimpleAdapter - адаптер, позволяющий заполнить данными список более сложной структуры, например, два текста в одной строке списка.
Готовые адаптеры
- SimpleCursorAdapter - дополняет ResourceCursorAdapter и создаёт компоненты TextView/ImageView из столбцов, содержащихся в курсоре. Компоненты определяются в ресурсах
- CursorAdapter - предназначен для работы с ListView, предоставляет данные для списка через курсор, который должен иметь колонку с именем "_id"
- ResourceCursorAdapter - этот адаптер дополняет CursorAdapter и может создавать виды из ресурсов
- HeaderViewListAdapter - расширенный вариант ListAdapter, когда ListView имеет заголовки.
- WrapperListAdapter - еще один адаптер для списков.
BaseAdapter
- Если вам нужен свой собственный адаптер, то в Android есть абстрактный класс BaseAdapter, который можно расширить.
- Собственный адаптер необходим в тех случаях, когда требуется специальное управление данными или дополнительный контроль над отображением дочерних представлений.
- Кроме того, вы можете предусмотреть в своём адаптере элементы кэширования для повышения производительности работы.
- У BaseAdapter есть несколько методов, которые следует переопределить. Например, метод getCount() позволяет узнать количество выводимых объектов.
Разметка
- Компоновка (также используются термины разметка или макет) хранится в виде XML-файла в папке /res/layout.
- компоновка – это некий визуальный шаблон для пользовательского интерфейса вашего приложения, который позволяет управлять элементами управления, их свойствами и расположением.
- Каждый файл разметки должен содержать только один корневой элемент компоновки, который должен быть объектом View или ViewGroup.
- Внутри корневого элемента вы можете добавлять дополнительные объекты разметки или виджеты как дочерние элементы.
Виды разметок
- FrameLayout
- LinearLayout
- TableLayout
- RelativeLayout
- GridLayout
- SwipeRefreshLayout
- ConstraintLayout
- CoordinatorLayout
FrameLayout
- является самым простым типом разметки.
- Обычно это пустое пространство на экране, которое можно заполнить только дочерними объектами View или ViewGroup.
- Все дочерние элементы FrameLayout прикрепляются к верхнему левому углу экрана.
- В разметке FrameLayout нельзя определить различное местоположение для дочернего объекта.
- Последующие дочерние объекты View будут просто рисоваться поверх предыдущих компонентов
- поэтому единственный дочерний элемент для FrameLayout обычно растянут до размеров родительского контейнера
FrameLayout
Пример разметки, когда на экран динамически выводится только одно изображение.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/FrameLayout1"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView
android:id="@+id/imageView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/cat" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitCenter"
android:src="@drawable/ic_launcher"
android:visibility="gone" />
</FrameLayout>
LinearLayout
- макет LinearLayout представлен двумя вариантами - Horizontal и Vertical.
- Макет LinearLayout выравнивает все дочерние объекты в одном направлении — вертикально или горизонтально.
- Направление задается при помощи атрибута ориентации android:orientation:
- android:orientation="horizontal"
- android:orientation="vertical"
- Все дочерние элементы помещаются в стек один за другим, так что вертикальный список компонентов будет иметь только один дочерний элемент в ряду независимо от того, насколько широким он является.
- Горизонтальное расположение списка будет размещать элементы в одну строку с высотой, равной высоте самого высокого дочернего элемента списка.
LinearLayout, пример
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
< TextView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#aa0000"
android:gravity="center_horizontal"
android:text="red"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#00aa00"
android:gravity="center_horizontal"
android:text="green"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#0000aa"
android:gravity="center_horizontal"
android:text="blue"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#aaaa00"
android:gravity="center_horizontal"
android:text="yellow"/>
</LinearLayout>
LinearLayout пример
LinearLayout, layout_weight
- атрибут android:layout_weight назначает индивидуальный вес для дочернего элемента
- атрибут определяет "важность" представления и позволяет этому элементу расширяться, чтобы заполнить любое оставшееся пространство в родительском представлении.
- Заданный по умолчанию вес является нулевым.
- Например, если есть три текстовых поля, и двум из них объявлен вес со значением 1, в то время как другому не даётся никакого веса (0), третье текстовое поле без веса не будет расширяться и займёт область, определяемую размером текста, отображаемого этим полем.
- Другие два расширятся одинаково, чтобы заполнить остаток пространства, не занятого третьим полем.
- Если третьему полю присвоить вес 2 (вместо 0), это поле будет объявлено как "более важное", чем два других, так что третье поле получит 50% общего пространства, в то время как первые два получат по 25% общего пространства.
- Также можно указать атрибут android:weightSum. Если атрибуту присвоить значение 100, то можно указывать вес дочерних элементов в удобном виде, как в процентах.
LinearLayout, layout_weight
<?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:background="#0000FF"
android:orientation="vertical"
android:weightSum="100">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="50"
android:background="#FFFF00"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="и треснул мир напополам"
android:textColor="#000000"
android:textSize="20sp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
TableLayout и TableRow
- Разметка TableLayout (Табличная разметка) позиционирует свои дочерние элементы в строки и столбцы
- TableLayout не отображает линии обрамления для их строк, столбцов или ячеек.
- TableLayout может иметь строки с разным количеством ячеек.
- При создании разметки для строк используются объекты TableRow, которые являются дочерними классами TableLayout (каждый TableRow определяет единственную строку в таблице).
- Строка может не иметь ячеек или иметь одну и более ячеек, которые являются контейнерами для других объектов.
- В ячейку допускается вкладывать другой TableLayout или LinearLayout.
RelativeLayout
- ReiativeLayout (относительная разметка) находится в разделе Layouts и позволяет дочерним компонентам определять свою позицию относительно родительского компонента или относительно соседних дочерних элементов (по идентификатору элемента).
- В RelativeLayout дочерние элементы расположены так, что если первый элемент расположен по центру экрана, другие элементы, выровненные относительно первого элемента, будут выровнены относительно центра экрана.
- При таком расположении, при объявлении разметки в XML-файле, элемент, на который будут ссылаться для позиционирования другие объекты представления, должен быть объявлен раньше, чем другие элементы, которые обращаются к нему по его идентификатору.
RelativeLayout
- android:layout_alignParentBottom - выравнивание относительно нижнего края родителя
- android:layout_alignParentLeft - выравнивание относительно левого края родителя
- android:layout_alignParentRight - выравнивание относительно правого края родителя
- android:layout_alignParentTop - выравнивание относительно верхнего края родителя
- android:layout_centerInParent - выравнивание по центру родителя по вертикали и горизонтали
- android:layout_centerHorizontal - выравнивание по центру родителя по горизонтали
- android:layout_centerVertical - выравнивание по центру родителя по вертикали
RelativeLayout
RelativeLayout
- Компонент можно размещать не только относительно родителя, но и относительно других компонентов.
- Для этого все компоненты должны иметь свой идентификатор, по которому их можно будет отличать друг от друга.
- android:layout_above - размещается над указанным компонентом
- android:layout_below - размещается под указанным компонентом
- android:layout_alignLeft - выравнивается по левому краю указанного компонента
- android:layout_alignRight - выравнивается по правому краю указанного компонента
- android:layout_alignTop - выравнивается по верхнему краю указанного компонента
- android:layout_alignBottom - выравнивается по нижнему краю указанного компонента
- android:layout_toLeftOf - правый край компонента размещается слева от указанного компонента
- android:layout_toRightOf - левый край компонент размещается справа от указанного компонента
RelativeLayout
RelativeLayout
- Чтобы компоненты "не прилипали" друг к другу, используются атрибуты, добавляющие пространство между ними.
- android:layout_marginTop
- android:layout_marginBottom
- android:layout_marginLeft
- android:layout_marginRight
GridLayout
- В GridLayout для любого компонента можно указать строку и колонку, и в этом месте таблицы он будет размещён.
- Указывать элемент для каждой ячейки не понадобится, это нужно делать только для тех ячеек, где действительно должны быть компоненты.
- Компоненты могут растягиваться на несколько ячеек таблицы. Более того, в одну ячейку можно поместить несколько компонентов.
- Обратите внимание на атрибуты layout_column и layout_columnSpan, используемые для указания самой левой колонки и количества занимаемых компонентом колонок.
- Также доступны атрибуты layout_row и layout_rowSpan.
- Для дочерних элементов не нужно указывать атрибуты layout_height и layout_width, по умолчанию они устанавливаются в значение wrap_content.
- Количество колонок и рядов используются атрибуты android:columnCount="number" и android:rowCount="number".
GridLayout
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" >
<Button android:text="1" />
<Button android:text="2" />
<Button android:text="3" />
<Button android:text="4" />
<Button android:text="5" />
<Button android:text="6" />
<Button android:text="7" />
<Button android:text="8" />
<Button android:text="9" />
<Button android:text="10" />
<Button android:text="11" />
<Button android:text="12" />
<Button android:text="13" />
<Button android:text="14" />
<Button android:text="15" />
<Button android:text="16" />
</GridLayout>
GridLayout
GridLayout
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:columnCount="4"
android:orientation="horizontal" >
<Button
android:layout_column="3"
android:text="/" />
<Button android:text="1" />
<Button android:text="2" />
<Button android:text="3" />
<Button android:text="*" />
<Button android:text="4" />
<Button android:text="5" />
<Button android:text="6" />
<Button android:text="-" />
<Button android:text="7" />
<Button android:text="8" />
<Button android:text="9" />
<Button
android:layout_rowSpan="3"
android:text="+" />
<Button
android:layout_columnSpan="2"
android:text="0" />
<Button android:text="00" />
<Button
android:layout_columnSpan="3"
android:text="=" />
</GridLayout>
GridLayout
GridLayout
<Button
android:layout_gravity="fill"
android:layout_rowSpan="3"
android:text="+" />
<Button
android:layout_gravity="fill"
android:layout_columnSpan="2"
android:text="0" />
<Button
android:layout_gravity="fill"
android:layout_columnSpan="3"
android:text="=" />
GridLayout