Основные компоненты приложения.

Диалоговые окна

  • 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