Статьи

Пользовательский интерфейс Android: макеты с группами просмотра и фрагментами

Эта статья является частью нашего Академического курса под названием Android UI Design — Основы .

В этом курсе вы познакомитесь с основами дизайна пользовательского интерфейса Android. Вы поймете пользовательский ввод, представления и макеты, а также адаптеры и фрагменты. Проверьте это здесь !

1. Обзор

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

В этой статье мы хотим изучить, как мы можем организовать такие представления и как эти представления могут быть размещены на экране. Другими словами, мы будем детально анализировать менеджеры макетов или просто макеты.

2. Обзор макета

Когда мы создаем интерфейс нашего приложения, мы используем специальное представление, которое действует как контейнер. Эти специальные виды управляют размещением других видов на экране смартфона / планшета. Android предоставляет набор менеджеров по расположению, и каждый из них реализует свою стратегию для хранения, управления и размещения своих дочерних элементов. С точки зрения API все менеджеры по ViewGroup происходят от класса ViewGroup . Есть некоторые макеты, которые размещают своих детей горизонтально или вертикально, а другие реализуют другую стратегию. Мы проанализируем их подробно позже в этой статье.

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

  • Использование XML: в этом случае, используя файл XML, мы «описываем», как выглядит наш пользовательский интерфейс. Мы определяем элементы (представления и вложенные макеты), которые появляются в пользовательском интерфейсе. В то же время мы определяем их атрибуты, как мы видели в предыдущей статье.
  • Во время выполнения: в этом случае мы ViewGroup наш макет, ViewGroup экземпляр нашей ViewGroup и присоединяем к нему Views . Мы можем манипулировать их свойствами, программно устанавливая их атрибуты.

Мы можем использовать оба подхода в нашем приложении. Мы могли бы, например, использовать XML для создания пользовательского интерфейса и присвоить его Views некоторые свойства. Во время выполнения мы можем найти (или выполнить поиск) эти Views и ViewGroup (layout) и изменить их свойства программно. Например, у нас может быть представление с красным фоном, а во время выполнения мы меняем его на зеленый. Android очень мощный и гибкий с этой точки зрения.

Используя XML, мы каким-то образом отделяем представление от кода, который обрабатывает его поведение. В XML описание пользовательского интерфейса является внешним по отношению к исходному коду, поэтому теоретически мы можем изменить представление, то есть просто файл XML, не затрагивая наш исходный код. Это тот случай, когда, например, мы хотим адаптировать наш пользовательский интерфейс для нескольких размеров экрана. В этом случае мы определяем разные макеты с одинаковыми именами, но в разных каталогах, и система выбирает ту, которая лучше всего соответствует размерам экрана. Это стандартный подход, принятый Android для обработки экрана нескольких размеров. Более того, мы увидим позже, что мы можем использовать другую технику, основанную на фрагментах. Если мы используем XML, можно использовать Draw Layout и легко отлаживать его.

С точки зрения API, каждая ViewGroup определяет вложенный класс LayoutParameter который содержит некоторые параметры, которые определяют размер и положение для каждого представления, принадлежащего ViewGroup . Все ViewGroup имеют два общих параметра, называемых width и height (или layout_width и layout_height ), которые должны быть определены каждым View . Эти два параметра представляют ширину и высоту View . Мы можем указать числовое значение или чаще мы можем использовать две константы:

  • wrap_content , означающее, что размер представления будет зависеть от фактического содержимого
  • fill_parent (или match_parent ), означающий, что представление должно стать таким же большим, как его родитель, удерживающий его

Представление в Android — это прямоугольник, а местоположение представления выражается в виде пары координат слева и сверху. Эти два значения определяют положение View внутри его ViewGroup .
Другим важным свойством View внутри Layout является отступ, выражаемый четырьмя значениями (let, top, right, bottom). Используя padding, мы можем перемещать содержимое View .

Android предоставляет несколько стандартных менеджеров раскладки:

  • Линейный макет
  • Макет таблицы
  • Относительная компоновка
  • Структура кадра
  • Макет сетки

2.1. LinearLayout

Это самый простой менеджер раскладок. Этот макет располагает дочерние элементы вертикально или горизонтально в зависимости от параметра ориентации. Чтобы определить этот макет в XML, мы можем использовать:

1
2
3
4
5
6
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
</LinearLayout>

Атрибут ориентации является наиболее важным атрибутом, поскольку он определяет, как размещаются представления. Может принимать два значения: горизонтальное или вертикальное. В первом случае виды располагаются горизонтально, а во втором — вертикально. Есть два других параметра, которые влияют на положение и размер видов. Это гравитация и вес.

Параметр гравитации ведет себя как выравнивание. Поэтому, если мы хотим выровнять вид по левой стороне, мы можем установить гравитацию влево или вправо, если мы хотим выровнять его по правому краю. Соответствующим атрибутом в XML является layout_gravity . Если мы создаем приложение, используя макет, показанный ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:text="Left" />
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right"
        android:text="Right" />
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Center" />
 
</LinearLayout>

и запустить его, мы имеем:

фигура 1

фигура 1

Другим важным параметром является вес (или layout_weight в XML). Используя параметр веса, мы можем присвоить значение важности View отношению к другим. View с более высоким значением важности является более важным, чем Views с более низкими значениями. Другими словами, Views с более высоким значением веса занимают больше места, чем другие Views . Например, посмотрите этот макет:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0"
        android:gravity="left"
        android:text="Left" />
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="right"
        android:text="Right" />
 
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0"
        android:gravity="center"
        android:text="Center" />
 
</LinearLayout>

В этом случае мы использовали layout_weight и придали больше значения TextView с правильным текстом. Запустив приложение, мы имеем:

фигура 2

фигура 2

Другим важным аспектом, который мы должны рассмотреть, является различие между android:gravity и layout_gravity . Даже если они выглядят очень похожими, они имеют другое значение. android:gravity — это атрибут, используемый View , а layout_gravity — это параметр, используемый контейнером.

2.2. TableLayout

Это менеджер компоновки, который размещает своих дочерних элементов в таблице, группируя их по строкам и столбцам. Например, используя макет, показанный ниже, мы создаем две разные строки, содержащие две ячейки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TableRow>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 1" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 2" >
        </TextView>
    </TableRow>
 
    <TableRow>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 3" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 4" >
        </TextView>
    </TableRow>
 
</TableLayout>

В качестве TableLayout мы использовали дочерний TableRow , который представляет строку внутри таблицы. Запустив приложение с таким макетом, мы имеем:

Рисунок 3

Рисунок 3

Мы даже можем использовать разные номера ячеек для разных строк, как показано в примере ниже, где во второй строке у нас есть три ячейки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TableRow>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 1" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 2" >
        </TextView>
    </TableRow>
 
    <TableRow>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 3" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 4" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 5" >
        </TextView>
    </TableRow>
 
</TableLayout>

В этом случае первая строка имеет пустую ячейку. Запустив пример, который мы имеем:

Рисунок 4

Рисунок 4

Не обязательно использовать TableRow , потому что мы можем использовать все компоненты, которые расширяют класс View . В этом случае этот компонент будет иметь ту же ширину, что и таблица. Например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is a row!" >
    </TextView>
 
    <TableRow>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 1" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 2" >
        </TextView>
    </TableRow>
 
    <TableRow>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 3" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 4" >
        </TextView>
 
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Cell 5" >
        </TextView>
    </TableRow>
 
</TableLayout>

В этом случае мы не использовали TableRow но мы использовали TextView в качестве первого TableLayout и указали, что ширина TextView должна быть такой же большой, как и содержимое. Если мы запустим приложение с этим макетом, у нас будет:

Рисунок 5

Рисунок 5

Вы можете заметить, что даже если мы указали, что TextView должен быть таким же большим, как и контент, он все равно занимает всю строку.

2,3. RelativeLayout

Это самая гибкая раскладка в Android. Этот менеджер компоновки использует политику, в которой контейнер размещает свои представления относительно других Views . Используя этот менеджер компоновки, мы можем реализовать очень сложные структуры пользовательского интерфейса.

RelativeLayout реализует некоторые атрибуты вида, которые мы можем использовать для размещения View . Существуют атрибуты, которые управляют положением View относительно других Views :

  • layout_toLeftof : позиция правого края этого вида слева от определенного вида
  • layout_toRightof : позиция левого края этого вида находится справа от определенного вида
  • layout_below : верхний край этого вида ниже определенного вида
  • layout_above : нижний край этого вида выше определенного вида

Есть другие параметры, которые используются для размещения представления относительно его контейнера:

  • layout_alignParentLeft : левый край этого представления совпадает с левым краем его контейнера
  • layout_alignParentRight : правый край этого представления совпадает с правым краем его контейнера
  • layout_alignParentTop : верхний край этого представления совпадает с верхним краем его контейнера
  • layout_alignParentBottom : нижний край этого представления соответствует нижнему краю его контейнера

Есть некоторые другие параметры, которые можно использовать, и вам рекомендуется ознакомиться с документацией. Например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:id="@+id/t1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text1" />
 
    <TextView
        android:id="@+id/t2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/t1"
        android:text="Text2" />
 
    <TextView
        android:id="@+id/t3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/t1"
        android:layout_toRightOf="@id/t2"
        android:text="Text3" />
 
</RelativeLayout>

В приведенном выше примере мы поместили TextView с идентификатором t2 ниже TextView с идентификатором t1 , TextView с идентификатором t3 находится слева от t2 и ниже t1 . Запустив пример, который мы имеем:

Рисунок 6

Рисунок 6

Предположим, мы хотим добавить еще один TextView в макет выше, и этот TextView должен быть размещен в правом нижнем углу, поэтому мы имеем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:id="@+id/t1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Text1" />
 
    <TextView
        android:id="@+id/t2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/t1"
        android:text="Text2" />
 
    <TextView
        android:id="@+id/t3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/t1"
        android:layout_toRightOf="@id/t2"
        android:text="Text3" />
 
    <TextView
        android:id="@+id/t4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:text="Text4" />
 
</RelativeLayout>

Запустив пример, мы имеем:

Рисунок 7

Рисунок 7

2,4. FrameLayout

FrameLayout — это специальный макет, который мы рассмотрим более подробно позже. Мы видели другой менеджер компоновки, который реализует некоторые конкретные стратегии для размещения представлений в пользовательском интерфейсе. FrameLayout используется, когда мы хотим динамически отображать представление. Это очень полезно, когда мы используем Fragments .

2.5. Макет сетки

Это последний менеджер макетов, который мы рассмотрим в этой статье. Он очень похож на TableLayout и появился с Android 4.0. Этот менеджер компоновки располагал свои представления в виде сетки, но относительно TableLayout его проще использовать. GridLayout делит область экрана на ячейки. Его дети занимают одну или несколько клеток.

2.6. Макет и поддержка нескольких экранов

Особое внимание необходимо уделять, когда мы хотим поддерживать несколько размеров экрана. Когда мы хотим создать приложение для нескольких устройств, мы должны реализовать несколько макетов. В Android у нас есть разные каталоги, и там мы можем реализовать макеты. Макет по умолчанию — тот, который находится под res/layout . Мы можем предоставить определитель размера, который добавляем к макету:

  • небольшой
  • большой
  • XLarge

Более того, мы можем даже обеспечить ориентацию экрана:

  • земельные участки
  • портрет

Так, например, если мы хотим создать макет для очень большого экрана, мы создаем каталог под res именем layout-xlarge и здесь мы реализуем наш макет.

Если мы хотим предоставить макет для ландшафтного режима, мы создаем другой каталог под res , называемый layout-land land и так далее. В Android 3.2 были введены классификаторы других размеров, чтобы лучше адаптировать макет к размеру экрана. Если вы хотите получить больше информации, вы можете посмотреть здесь .

3. Фрагменты

К настоящему времени мы увидели, как мы можем создавать пользовательские интерфейсы, используя макеты и компоненты. В этой ситуации у нас есть Activity с ее макетом и компонентами. Мы знаем, как мы можем адаптировать наш макет для нескольких экранов, но такой техники иногда недостаточно, особенно когда мы хотим поддерживать планшеты и смартфоны. Мы уже говорили об этом, и мы знаем, что размер экрана смартфона сильно отличается от экрана планшета. Более того, необходимо сделать интерфейс более динамичным и гибким в планшетах с большим экраном. По этим причинам в Android 3.0 (уровень API 11) введена концепция фрагментов.

Что такое фрагмент? Фрагмент — это часть пользовательского интерфейса в Activity . Мы можем объединить несколько фрагментов для создания сложных пользовательских интерфейсов. Каждый фрагмент имеет свой жизненный цикл, и мы можем управлять жизненным циклом каждого фрагмента независимо. Фрагмент существует только внутри Activity которая действует как его контейнер. Когда мы добавляем фрагмент внутри макета, он живет внутри ViewGroup, которая представляет макет. Фрагмент — это очень мощный компонент, который помогает разработчикам создавать динамический пользовательский интерфейс и одновременно поддерживать несколько размеров экрана.

Фрагмент можно рассматривать как фрагмент кода многократного использования с собственным интерфейсом. Очень важно различать, когда нам нужно использовать фрагменты и когда мы можем использовать только «простые» макеты. Как уже было сказано, в некоторых ситуациях простых макетов, даже если они адаптированы к размеру экрана, недостаточно.

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

Например, в смартфоне мы бы имели:

Рисунок 8

Рисунок 8

в то время как в таблетке у нас будет:

Рисунок 9

Рисунок 9

В этом случае фрагменты помогают нам. Мы можем создать два фрагмента: один, который обрабатывает список контактов, а другой — контактные данные. Итак, мы имеем:

Рисунок 10

Рисунок 10

пока в планшете:

Рисунок 11

Рисунок 11

3.1. Фрагмент жизненного цикла

Теперь, когда мы знаем, когда нам следует использовать фрагменты, нам нужно знать, как они работают, прежде чем их использовать. Фрагмент имеет свой жизненный цикл, как и Activity , но он более сложный по сравнению с жизненным циклом деятельности. Кроме того, фрагмент живет только внутри Activity которое действует как его контейнер. Ниже показан жизненный цикл фрагмента:

Рисунок 12

Рисунок 12

Как мы видим, этот жизненный цикл имеет больше состояний по сравнению с жизненным циклом активности. Двигаясь сверху вниз, мы имеем:

  • onInflate : этот метод вызывается, только если мы определяем фрагмент непосредственно в нашем макете, используя тег. В этом методе мы можем сохранить некоторые параметры конфигурации и некоторые атрибуты, определенные в файле макета XML.
  • onAttach : этот метод вызывается, как только фрагмент «присоединяется» к активности «папа», и мы можем использовать этот метод для хранения ссылки на операцию.
  • onCreate : это один из самых важных шагов, наш фрагмент находится в процессе создания. Этот метод может использоваться для запуска некоторого потока для получения информации о данных, возможно, с удаленного сервера.
  • onCreateView : это метод, вызываемый, когда фрагмент должен создать свою иерархию представления. Во время этого метода мы будем раздувать наш макет внутри фрагмента. На этом этапе мы не можем быть уверены, что наша деятельность все еще создается, поэтому мы не можем рассчитывать на нее в какой-либо операции.
  • OnActivityCreated : в этом методе мы уведомляемся, когда действие «отца» создано и готово к использованию. Отныне наша деятельность активна и создана, и мы можем использовать ее, когда нам нужно.
  • onStart : здесь мы делаем обычные вещи, как в onStart , на этом этапе наш фрагмент видим, но он по-прежнему не взаимодействует с пользователем.
  • onResume : этот метод вызывается, когда фрагмент готов взаимодействовать с пользователем. В конце этого этапа наш фрагмент запущен!

Затем, возможно, что действие может быть приостановлено, и поэтому onPause . Ну, в этом случае также вызывается метод фрагмента onPause. После этого, возможно, что ОС решит уничтожить наш фрагментный вид, и будет вызван onDestroyView . После этого, если система решает отклонить наш фрагмент, она вызывает метод onDestroy . Здесь мы должны освободить все активные соединения, потому что наш фрагмент близок к закрытию. Даже на этапе уничтожения он все еще привязан к активности отца. Последний шаг — отсоединить фрагмент от действия, и это происходит при onDetach .

3.2. Как использовать фрагменты

Как только мы узнаем жизненный цикл фрагмента, нам нужно знать, как мы можем создать фрагмент и как мы прикрепляем его к Activity . Создать фрагмент очень просто, нам нужно просто расширить класс Android под названием android.app.Fragment . Мы можем предположить, что мы хотим создать фрагмент с именем Fragment1 . В этом случае мы имеем:

1
2
public class Fragment1 extends Fragment {
}

Таким образом, мы создали фрагмент. В этом классе мы можем переопределить методы обратного вызова, чтобы обрабатывать различные состояния в жизненном цикле фрагмента. К настоящему времени мы можем предположить, что этот фрагмент имеет очень простое расположение:

01
02
03
04
05
06
07
08
09
10
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Test Fragment1" />
 
</LinearLayout>

В этом случае этот макет просто имеет компонент TextView который пишет текст на экране. Если мы хотим «прикрепить» этот макет к нашему фрагменту, мы можем сделать это в методе onCreateView , поскольку в соответствии с жизненным циклом фрагмента этот метод вызывается, когда фрагмент создает свою иерархию представления. Таким образом, в нашем классе Fragment1 мы можем переопределить метод onCreateView и реализовать нашу логику:

1
2
3
4
5
6
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {   
    View v = inflater.inflate(R.layout.text_frag, container, false);
    return v;
}

В этом методе мы раздуваем наш фрагментный фрагмент text_frag (тот, что показан выше). Теперь мы создали фрагмент, и нам нужно присоединить его к Activity , в котором он содержится, потому что мы знаем, что фрагмент существует только внутри Activity . Мы можем сделать эту операцию двумя способами:

  • объявление фрагмента внутри макета активности
  • объявление заполнителя внутри макета действия и прикрепление фрагмента внутри действия

В первом случае эта операция является статической, то есть мы постоянно прикрепляем фрагмент к операции. Во втором случае мы можем управлять фрагментами во время выполнения, поэтому мы можем заменить фрагмент другим, например.

Если мы объявляем фрагмент внутри макета, мы должны использовать тег фрагмента.

Кроме того, мы должны указать полное имя класса. Например, если класс Fragment1 находится в пакете com.swa, для макета действия мы будем иметь:

01
02
03
04
05
06
07
08
09
10
11
12
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <fragment
        android:id="@+id/f1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.sswa.Fragment1" />
 
</LinearLayout>

Запустив приведенный выше пример, мы получим:

Рисунок 13

Рисунок 13

Немного сложнее второй вариант, когда мы хотим динамически управлять фрагментами.

3.3. FrameLayout и FragmentManager

Когда мы хотим динамически обрабатывать фрагменты внутри нашей Activity мы не можем использовать тег фрагмента в макете активности, но мы должны использовать заполнитель, который мы «заполним» пользовательским интерфейсом фрагмента. В этом случае макет действия становится:

01
02
03
04
05
06
07
08
09
10
11
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <FrameLayout
        android:id="@+id/fl1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
</LinearLayout>

В Activity мы должны использовать компонент, который помогает нам обрабатывать фрагменты. Этот компонент называется FragmentManager . Используя этот компонент, мы можем добавлять, удалять, заменять фрагменты во время выполнения, а также находить фрагменты.

Обратите внимание, что перед выполнением каких-либо операций с фрагментом нам необходимо активировать транзакцию. Используя FragmentManager мы можем создать транзакцию, запустить ее и зафиксировать в конце, когда операция фрагмента завершена. Итак, в Activity мы имеем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class MainActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_din);
 
        // Create fragment
        Fragment1 f1 = new Fragment1();
        FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.add(R.id.fl1, f1);
        ft.commit();
    }
 
}

3.4. Фрагмент активности общения

Часто нам нужно обмениваться информацией между фрагментом и деятельностью, которая его содержит. Нам нужно учитывать, что мы можем повторно использовать фрагменты с различными действиями, поэтому мы не можем связать фрагмент с определенным действием.

Внутри фрагмента мы можем получить экземпляр действия, используя метод getActivity() . Если мы хотим обмениваться данными, рекомендуется создать интерфейс обратного вызова внутри фрагмента и потребовать, чтобы действие его реализовало.

Например, мы можем предположить добавить кнопку в пользовательский интерфейс фрагмента1, и когда пользователь нажимает кнопку, мы хотим передать эту информацию в действие. Первое, что нам нужно сделать, это изменить наш фрагментный макет:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Test Fragment Dynamic" />
 
    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click me" />
 
</LinearLayout>

Второй шаг — создать такой интерфейс:

1
2
3
4
5
public class Fragment1 extends Fragment {
    public static interface FragmentListener {
        public void onClickButton() ;
    }
}

Теперь, когда у нас есть интерфейс, наша деятельность должна его реализовать:

1
2
3
4
5
6
7
public class MainActivity extends Activity implements
        Fragment1.FragmentListener {
    @Override
    public void onClickButton() {
        // Handle here the event
    }
}

Теперь мы должны проверить в классе фрагмента, реализует ли действие интерфейс, чтобы мы могли уведомить событие, когда оно происходит. Лучшее место для этого, помня жизненный цикл фрагмента, — метод onAttach , поэтому мы имеем:

01
02
03
04
05
06
07
08
09
10
public class Fragment1 extends Fragment {
 
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (!(activity instanceof FragmentListener))
            throw new ClassCastException();
 
    }
}

Наконец, мы готовы уведомить событие, когда пользователь нажимает кнопку, и мы можем сделать это в методе onCreateView :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class Fragment1 extends Fragment {
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.text_frag, container, false);
        Button b = (Button) v.findViewById(R.id.btn1);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((FragmentListener) getActivity()).onClickButton();
            }
        });
        return v;
    }
}

3.5. Межфрагментное общение

При разработке приложения для Android с использованием фрагментов нам нужно не только обмениваться данными между фрагментом и активностью, которая его содержит, но и обмениваться информацией между двумя фрагментами. В этом случае рекомендуется избегать связи фрагмента с фрагментом, чтобы мы могли гарантировать, что фрагменты отделены.

Если мы хотим обмениваться данными таким образом, мы можем передать их из первого фрагмента в действие, используя метод, описанный ранее, а затем из действия в целевой фрагмент. Таким образом, мы можем быть уверены, что наши фрагменты могут быть повторно использованы в различных ситуациях.

3,6. Поддержка нескольких экранов с использованием фрагментов

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

Настало время изучить, как создать пользовательский интерфейс, работающий на планшете и смартфоне. Чтобы достичь этого, мы можем вернуться к описанному выше примеру. В этом случае мы можем предположить, что когда пользователь нажимает кнопку, приложение отображает сообщение. Если у нас есть смартфон, когда пользователь нажимает кнопку, мы переходим к другой операции, которая показывает сообщение, а если мы используем планшет, мы хотим, чтобы это сообщение отображалось на том же экране.

Прежде всего, мы кодируем второй фрагмент с очень простой компоновкой, которая просто показывает сообщение, и мы называем этот фрагмент Fragment2 :

1
2
3
4
5
6
7
8
public class Fragment2 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {   
        View v = inflater.inflate(R.layout.frag_message, container, false);
        return v;
    }
}

Макет:

01
02
03
04
05
06
07
08
09
10
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello world" />
 
</LinearLayout>

Если приложение работает на смартфоне, то макет аналогичен описанному ранее, в то время как в случае планшета необходимо уделить особое внимание. Для простоты мы можем предположить, что планшет имеет альбомную компоновку, поэтому мы можем создать в каталоге res новый каталог с именем layout-land :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
 
    <FrameLayout
        android:id="@+id/fl1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
    <FrameLayout
        android:id="@+id/fl2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
</LinearLayout>

Обратите внимание, что в этом случае у нас есть два FrameLayout . «Тяжелая» работа выполняется Activity . Если в прикрепленном к нему текущем макете нет FrameLayout с идентификатором, равным f2 , то у нас есть макет с одним фрагментом, то есть мы используем смартфон, в противном случае мы используем фрагмент.

В методе обратного вызова в действии имеем:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import wkkwc.com;
 
@Override
    public void onClickButton() {
        // Handle here the event
        if (findViewById(R.id.fl2) != null) {
            // We have dual fragment layout
             
            Fragment2 f2 = (Fragment2) getFragmentManager().findFragmentById(R.id.fl2);
             
            if (f2 == null) {
                // Fragment2 is not inizialized
                f2 = new Fragment2();
                FragmentTransaction ft = getFragmentManager().beginTransaction();
                ft.replace(R.id.fl2, f2);
                ft.commit();
            }
             
        }
        else {
            // Single fragment layout
            Fragment2 f2 = new Fragment2();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.replace(R.id.fl1, f2);
            ft.commit();
        }
    }

Запустив приложение у нас в смартфоне:

Рисунок 14

Рисунок 14

Рисунок 15

Рисунок 15

в то время как, если мы запустим приложение на планшете, у нас будет:

Рисунок 16

Рисунок 16

Рисунок 17

Рисунок 17

4. Загрузите исходный код

Это был урок о том, как создавать макеты Android с помощью ViewGroups и фрагментов. Вы можете скачать исходный код здесь: AndroidLayout.zip