Advertisement
  1. Code
  2. Android SDK

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

Scroll to top
Read Time: 10 min
This post is part of a series called Android User Interface Design.
Android User Interface Design: Building a ListView Application
Android User Interface Design: Building Application Preference Screens

() translation by (you can also view the original English article)

Новый API-интерфейс Fragment для Android, представленный в Android 3.0, позволяет упростить создание динамичного интерфейса. В этом учебном пособии вы узнаете, как преобразовать двухэкранный ListView и WebView в одноэранный вид, предназначенный для больших экранов, например, для планшетов.

Изменения в техниках и программном обеспечении

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

Шаги этого учебника будут более краткие, чем некоторые из наших предыдущих учебников; вам, возможно, придется просмотреть некоторые другие обучающие материалы для Android на этом сайте или даже в справочнике Android SDK, если вы не знакомы с какими-либо базовыми концепциями и классами Android, обсуждаемыми в этом руководстве. Окончательный образец кода, который идет вместе с этим уроком, доступен для загрузки в качестве открытого кода на Google.

Представляем фрагменты

Прежде чем мы начнем, давайте определим, что такое фрагмент. Фрагмент - это, как правило, часть пользовательского интерфейса с его собственным жизненным циклом. Если это звучит очень похоже на Activity, это потому, что оно похоже на него. Однако фрагмент отличается от Activity, поскольку он существует внутри него. Фрагмент не должен быть сопряжен с одним и тем же Activity каждый раз, когда он создается, что дает ему некоторую гибкость. Также как Activity, фрагмент не содержит элементов пользовательского интерфейса.

Шаг 0: Начало работы

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

Шаг 1: Редизайн экранов

На следующем рисунке показано наше приложение Mobiletuts+ для чтения статей (урок ListView) до того, как был рассмотрен и применен дизайн фрагмента:

Android SDK Fragments - Figure 1Android SDK Fragments - Figure 1Android SDK Fragments - Figure 1

Эта разметка отлично работает на относительно небольшом экране телефона. Однако на большом экране, например, на 10-дюймовом экране Motorola Xoom на экране ListView очень много свободного места. Экран WebView выглядит хорошо, но немного пусто.

Здесь и появляются фрагменты: на больших экранах мы могли бы предоставить более эффективный пользовательский интерфейс, если бы мы могли отображать ListView на том же экране, что и WebView. Когда пользователь нажимает на определенный элемент ListView на панели слева, WebView с правой стороны обновляется, чтобы отобразить соответствующий контент. Этот тип разметки часто используется в программах для чтения электронной почты или документов. На следующем рисунке показан редизайн:

Android SDK Fragments - Figure 2Android SDK Fragments - Figure 2Android SDK Fragments - Figure 2

Шаг 2: Преобразование в дизайн на основе фрагментов

Теперь, мы знаем, как будет работать новая разметка экрана, мы также знаем, что два текущих действия необходимо будет преобразовать в фрагменты. Мы выполним преобразование в несколько этапов. Первый шаг заключается в том, чтобы оставить экраны визуально без изменений, но модифицировать каждый экран для использования фрагмента. Один фрагмент будет содержать текущий ListView, а другой будет содержать WebView. Затем мы перейдем к единственной реализации на экране, которая включает в себя обмен данными между элементами ListView и WebView, превращенными в фрагменты.

Во-первых, измените Project Build Target вашего приложения на Android 3.0. Чтобы сделать это в Eclipse, щелкните правой кнопкой мыши проект и выберите «Свойства». Перейдите в раздел Android и установите флажок рядом с Android 3.0. Мы не используем какие-либо API Google, поэтому достаточно версии Android Open Source Project. Затем нажмите кнопку «ОК».

Теперь у вас будет доступ к новым API, включая API фрагментов.

Примечание: В следующем уроке мы поговорим об использовании нового уровня совместимости, чтобы технологии, такие как API-интерфейс Fragment, работали в более ранних версиях Android. Пока же им потребуется устройство с Android 3.0, Honeycomb.

Шаг 3: Создание классов фрагментов

Создайте два новых класса Java для представления двух фрагментов: экранов ListView и WebView. Назовите их TutListFragment и TutViewerFragment. TutListFragment расширит класс ListFragment, а TutViewerFragment просто расширит класс Fragment.

В классе TutListFragment нам необходимо переопределить два метода: onListItemClick() и onCreate(). Содержание этих методов должно выглядеть вам знакомым; они похожи на те, что мы видели ранее в классе TutListActivity. Мы их скоро изменим, но пока не все. Вот список класса TutListFragment:

1
@Override
2
public void onListItemClick(ListView l, View v, int position, long id) {
3
    String[] links = getResources().getStringArray(R.array.tut_links);
4
5
    String content = links[position];
6
    Intent showContent = new Intent(getActivity().getApplicationContext(),
7
            TutViewerActivity.class);
8
    showContent.setData(Uri.parse(content));
9
    startActivity(showContent);
10
}
11
12
@Override
13
public void onCreate(Bundle savedInstanceState) {
14
    super.onCreate(savedInstanceState);
15
    setListAdapter(ArrayAdapter.createFromResource(getActivity()
16
            .getApplicationContext(), R.array.tut_titles,
17
            R.layout.list_item));
18
}

Класс TutViewerFragment немного проще. Мы используем тот факт, что фрагмент работает под тем же activity, которым он пользовался, и получает данные непосредственно из класса Fragment. Добавьте метод переопределения для метода onCreateView(). Теперь эти методы должны выглядеть так:

1
@Override
2
public View onCreateView(LayoutInflater inflater, ViewGroup container,
3
        Bundle savedInstanceState) {
4
    Intent launchingIntent = getActivity().getIntent();
5
    String content = launchingIntent.getData().toString();
6
7
    WebView viewer = (WebView) inflater.inflate(R.layout.tut_view, container, false);
8
    viewer.loadUrl(content);
9
    
10
    return viewer;
11
}

Возможность непосредственного доступа к экземпляру активности полезна, но позже создаст проблему. Что делать, если этот фрагмент находится на экране с фрагментом списка? В этом случае не произойдёт получения URL-адреса. Аналогично, в TutListFragment мы запускаем новое действие непосредственно, когда пользователь щелкает элемент в списке. Что делать, если TutViewFragment существовал в рамках одного activity? Если это так, запуск нового не имеет смысла. Мы вернемся и решим эти проблемы чуть позже в этом уроке.

Шаг 4: Добавление ресурсов макета фрагмента

Теперь создайте новый файл макета, называемый tutlist_fragment.xml, для фрагмента, содержащего список статей. Ресурс макета фрагмента использует тег и ссылается на созданный вами класс фрагментов.

1
<?xml version="1.0" encoding="utf-8"?>
2
<fragment
3
    xmlns:android="http://schemas.android.com/apk/res/android"
4
    android:name="com.mamlambo.tutorial.tutlist.TutListFragment"
5
    android:layout_width="match_parent"
6
    android:layout_height="match_parent"
7
    android:id="@+id/tutlist_fragment">
8
</fragment>

Затем создайте аналогичный файл макета с именем tutview_fragment.xml:

1
<?xml version="1.0" encoding="utf-8"?>
2
<fragment
3
    xmlns:android="http://schemas.android.com/apk/res/android"
4
    android:name="com.mamlambo.tutorial.tutlist.TutViewerFragment"
5
    android:layout_width="match_parent"
6
    android:layout_height="match_parent"
7
    android:id="@+id/tutview_fragment">
8
</fragment>

Шаг 5: Обновление классов Activity

Теперь должны обновляться классы TutListActivity и TutViewerActivity. Класс TutListActivity имеет единственный метод onCreate(), который теперь должен быть обновлен для загрузки соответствующего ресурса макета фрагмента, созданного на предыдущем шаге, например:

1
@Override
2
public void onCreate(Bundle savedInstanceState) {
3
    super.onCreate(savedInstanceState);
4
5
    setContentView(R.layout.tutlist_fragment);
6
}

Кроме того, следует отметить, что TutListActivity должен наследовать класс Activity, а не ListActivity.

Класс TutViewerActivity требует аналогичного изменения. Его метод onCreate() теперь должен выглядеть следующим образом:

1
@Override
2
public void onCreate(Bundle savedInstanceState) {
3
    super.onCreate(savedInstanceState);
4
    setContentView(R.layout.tutview_fragment);
5
}

Шаг 6: Проверка вашего прогресса

Попробуйте запустить приложение сейчас. Вы заметите, что оно делает именно то, что и раньше делало. Не очень интересно, не так ли? Однако весь пользовательский интерфейс теперь выполняется с использованием фрагментов. Это позволит сделать следующие изменения, которые нужно сделать, чтобы плавно перестроить всё, поскольку мы добавляем новый макет, который объединит два фрагмента на больших дисплеях. Однако, как вы могли заметить, связь между фрагментами обрабатывается идентично тому, как мы взаимодействуем между activity. Фактически, мы использовали знание о том, что активность каждого фрагмента в паре оставалась неизменной. Этого не будет, если у нас есть одно действие, которое содержит и управляет обоими фрагментами. Давайте исправим это в первую очередь.

Шаг 7: Изменение связи для TutListFragment

Как вы поняли на шаге 3, запуск активности непосредственно из объекта TutListFragment больше не имеет смысла. Пользовательский интерфейс WebView может, по сути, быть частью того же самого действия, что и List - это наше решение для больших экранов. В этом случае мы просто хотим обновить URL-адрес WebView во втором фрагменте.

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

1
public interface OnTutSelectedListener {
2
    public void onTutSelected(Uri tutUri);
3
}

А затем активируйте его, обновив метод onListItemClickListener() следующим образом:

1
@Override
2
public void onListItemClick(ListView l, View v, int position, long id) {
3
    String[] links = getResources().getStringArray(R.array.tut_links);
4
5
    String content = links[position];
6
    tutSelectedListener.onTutSelected(Uri.parse(content));
7
}

Затем, класс TutListActivity реализует интерфейс OnTutSelectedListener, например:

1
public class TutListActivity extends Activity implements
2
        TutListFragment.OnTutSelectedListener {
3
...
4
@Override
5
public void onTutSelected(Uri tutUri) {
6
    Intent showContent = new Intent(getApplicationContext(),
7
            TutViewerActivity.class);
8
    showContent.setData(tutUri);
9
    startActivity(showContent);
10
}

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

Шаг 8: Изменение связи для TutViewerFragment

Теперь перейдем к классу TutViewerFragment, который также необходимо обновить. Вместо того, чтобы узнать, какой URL для загрузки, фрагмент будет ожидать, нужно узнать, какой URL-адрес загружается. Таким образом, мы можем обновлять WebView напрямую и не воссоздавать фрагмент с каждой загрузкой.

Во-первых, измените класс TutViewerFragment на новый метод под названием updateUrl():

1
public void updateUrl(String newUrl) {
2
    if (viewer != null) {
3
        viewer.loadUrl(newUrl);
4
    }
5
}

Затем удалите все функции из метода onCreateView(), за исключением вызова inflate(). Над классом TutViewerActivity, добавьте функциональные возможности для извлечения Intent и затем вызовите метод updateUrl(), например:

1
@Override
2
public void onCreate(Bundle savedInstanceState) {
3
    super.onCreate(savedInstanceState);
4
    setContentView(R.layout.tutview_fragment);
5
6
    Intent launchingIntent = getIntent();
7
    String content = launchingIntent.getData().toString();
8
9
    TutViewerFragment viewer = (TutViewerFragment) getFragmentManager()
10
            .findFragmentById(R.id.tutview_fragment);
11
    
12
    viewer.updateUrl(content);
13
}

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

Шаг 9: Добавление макета с двумя фрагментами

Теперь давайте создадим макет с обоими фрагментами, для использования в определенных ситуациях. В папке layout-land (которую вам может понадобиться создать) добавьте копию файла tutlist_fragment.xml. Это обеспечит разную ориентацию на любом экране. Портретный режим останется неизменным. Отредактируйте файл, чтобы выглядеть следующим образом с обоими фрагментами:

1
<?xml version="1.0" encoding="utf-8"?>
2
<LinearLayout
3
    xmlns:android="http://schemas.android.com/apk/res/android"
4
    android:layout_width="match_parent"
5
    android:layout_height="match_parent"
6
    android:orientation="horizontal">
7
    <fragment
8
        android:name="com.mamlambo.tutorial.tutlist.TutListFragment"
9
        android:layout_width="0dp"
10
        android:layout_height="match_parent"
11
        android:id="@+id/tutlist_fragment"
12
        android:layout_weight="45">
13
    </fragment>
14
    <fragment
15
        android:name="com.mamlambo.tutorial.tutlist.TutViewerFragment"
16
        android:layout_width="0dp"
17
        android:layout_height="match_parent"
18
        android:id="@+id/tutview_fragment"
19
        android:layout_weight="55">
20
    </fragment>
21
</LinearLayout>

Это будет разделять экран по горизонтали между обоими фрагментами.

Шаг 10: Добавление динамического выбора

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

Для этого обновите метод onTutSelected() в классе TutListActivity следующим образом:

1
@Override
2
public void onTutSelected(String tutUrl) {
3
    TutViewerFragment viewer = (TutViewerFragment) getFragmentManager()
4
            .findFragmentById(R.id.tutview_fragment);
5
6
    if (viewer == null || !viewer.isInLayout()) {
7
        Intent showContent = new Intent(getApplicationContext(),
8
                TutViewerActivity.class);
9
        showContent.setData(Uri.parse(tutUrl));
10
        startActivity(showContent);
11
    } else {
12
        viewer.updateUrl(tutUrl);
13
    }
14
}

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

Шаг 11: Запуск приложения с добавлением новых фрагментов

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

Android SDK Fragments - Tutorial 3Android SDK Fragments - Tutorial 3Android SDK Fragments - Tutorial 3

Заключение

API-интерфейс фрагмента помогает организовать компоненты интерфейса, так чтобы их можно было повторно использовать в разных activity. Таким образом, приложение может динамически корректировать свой макет и пользовательский интерфейсы с относительно небольшими усилиями в плане кодирования. Вы также видели, что приложение, которое создано с помощью фрагментов, легче реорганизовать. Еще лучше, чтобы любое приложение смогло использовать фрагменты через библиотеку совместимости, предоставляемую Google, которая совместима еще в Android 1.6.
Фрагментируйте пользовательский интерфейс приложений и создавайте удивительные пользовательские интерфейсы для каждого размера и формы экрана!

Об авторах

Разработчики мобильных устройств Лорен Дарси и Шейн Кондер совместно со своими коллегами разработали несколько книг по разработке Android: углубленную книгу по программированию под названием Разработка приложений для Android Wireless и самоучитель Сэма для разработки приложений Android за 24 часа. Пока они не начали писать, они разрабатывали мобильное ПО в своей компании и предоставляли консультационные услуги. Их можно найти написав по электронной почте androidwirelessdev+mt@gmail.com через свой блог на androidbook.blogspot.com и на Twitter @androidwireless.

Нужна дополнительная помощь? Написание приложений для Android? Ознакомьтесь с самыми новыми книгами и ресурсами!

Buy Android Wireless Application Development, 2nd Edition Buy Sam's Teach Yourself Android Application Development in 24 Hours Mamlambo code at Code Canyon

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.