Построение гибкого пользовательского интерфейса

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

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

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

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

Добавление фрагмента к деятельности во время выполнения

Вместо определения фрагментов деятельности в файле макета, как показано в предыдущем уроке с <fragment> элементами — вы можете добавить фрагмент в деятельности во время выполнения деятельности. Это необходимо, если вы планируете изменять фрагменты в течение жизни вашей деятельности.

Для выполнения транзакции, такой как добавление или удаление фрагмента, необходимо использовать FragmentManager для создания FragmentTransaction, которая обеспечивает API для добавления, удаления, замены и выполнения других операций с фрагментами.

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

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

Следующий макет является альтернативой макета, показанного в предыдущем уроке , который показывает только один фрагмент. Для того чтобы заменить один фрагмент другим, макет деятельности включает в себя пустой FrameLayout , выступающий в роле контейнера фрагментов.

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

res/layout/news_articles.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Внутри вашей деятельности, вызов getSupportFragmentManager() для получения FragmentManager использует API Библиотеки поддержки. Затем вызовите beginTransaction() для создания FragmentTransaction и вызовите add() для добавления фрагмента.

Вы можете выполнить несколько операций с фрагментами деятельности используя тот же FragmentTransaction. Когда вы будете готовы внести изменения, вы должны вызвать commit().

Например, вот как добавить фрагмент к предыдущему макету:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);

        // Check that the activity is using the layout version with
        // the fragment_container FrameLayout
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            if (savedInstanceState != null) {
                return;
            }

            // Create a new Fragment to be placed in the activity layout
            HeadlinesFragment firstFragment = new HeadlinesFragment();
            
            // In case this activity was started with special instructions from an
            // Intent, pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());
            
            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }
}

Поскольку фрагмент был добавлен в FrameLayout контейнер во время выполнения — вместо определения его в макете деятельности с помощью <fragment> элемента — деятельность может удалить фрагмент и заменить его другим.

Замена одного фрагмента другим

Процедура замены фрагмента похожа на добавление, но необходимо вызвать replace() метод вместо add().

Имейте в виду, что при выполнении операции с фрагментами, таких как замена или удаление, очень часто является целесообразным разрешить пользователю переместиться назад и "отменить" изменения. Чтобы позволить пользователю перемещаться назад по операциям с фрагментами, необходимо вызвать addToBackStack() прежде чем вы вызвать commit для FragmentTransaction.

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

Пример замены одного фрагмента другим:

// Create fragment and give it an argument specifying the article it should show
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

addToBackStack() метод принимает необязательный строковый параметр, который определяет уникальное имя для операции. Имя не требуется, если вы не планируете выполнять специальные операции с фрагментами, используя FragmentManager.BackStackEntry API.