フレキシブルな UI のビルド

さまざまな画面サイズに対応したアプリケーションをデザインする場合、異なるレイアウト構成でフラグメントを再利用することで、利用可能な画面の領域に応じて最適なユーザー エクスペリエンスを実現できます。

たとえば携帯端末の場合、一般的に、1 つのペインのユーザー インターフェースには 1 つのフラグメントのみを表示するのが適切です。一方、より横幅の広いタブレットでは、フラグメントを横並びに配置して、より多くの情報をユーザーに表示できます。

図 1. サイズの異なる画面に異なる構成で表示された、同じアクティビティの 2 つのフラグメント。大きな画面では 2 つのフラグメントが横並びに配置されていますが、携帯端末では 1 つのフラグメントのみを表示するため、ユーザーの操作に基づいてフラグメントを差し替える必要があります。

FragmentManager クラスでは、アクティビティの実行時にフラグメントを追加、削除、置換して動的な操作感を実現できるメソッドが提供されます。

Fragment の実装の詳細については、以下のリソースをご覧ください。

実行時にフラグメントをアクティビティに追加する

前のレッスンで示したように <fragment> 要素を使用してレイアウト ファイルのアクティビティにフラグメントを定義する代わりに、アクティビティの実行時にフラグメントを追加できます。アクティビティが実行されている間にフラグメントを変更する予定がある場合は、この方法を使う必要があります。

フラグメントの追加や削除といったトランザクションを実行するには、FragmentManager を使用して FragmentTransaction を作成する必要があります。ここから提供される API により、フラグメントを追加、削除、置換したり、フラグメントの他のトランザクションを実行できます。

アクティビティでフラグメントの削除や置換を許可する場合は、アクティビティの onCreate() メソッド中に最初のフラグメントを追加する必要があります。

フラグメントを扱うとき(特に実行時にフラグメントを追加するとき)の重要なルールは、フラグメントを挿入できるコンテナ View がアクティビティのレイアウトに含まれていることです。

次のレイアウトは、前のレッスンで説明した、一度に 1 つのフラグメントのみを表示するレイアウトの代替となるレイアウトです。1 つのフラグメントを別のフラグメントに置き換えるため、アクティビティのレイアウトにはフラグメント コンテナとして機能する空の FrameLayout が含まれています。

ファイル名は前のレッスンのレイアウト ファイルと同じですが、レイアウトのディレクトリには large 修飾子がないため、端末の画面が「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() を呼び出し、Support Library API を使用して FragmentManager を取得します。次に、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();
        }
    }
}

<fragment> 要素を使用してアクティビティのレイアウトでフラグメントを定義する代わりに、実行時にフラグメントが FrameLayout コンテナに追加されたため、アクティビティはフラグメントを削除して別のフラグメントに置き換えることができます。

フラグメントを別のフラグメントに置き換える

フラグメントを置き換える手順はフラグメントの追加と似ていますが、add() メソッドの代わりに replace() メソッドが必要になります。

置換や削除といったフラグメントのトランザクションを行う場合、ユーザーが「元に戻す」などの操作を行えるようにしておくと良いでしょう。フラグメントのトランザクション中にユーザーの「戻る」操作を許可するには、FragmentTransaction をコミットする前に、addToBackStack() を呼び出す必要があります。

注: フラグメントを削除または置換してトランザクションをバックスタックに追加すると、削除されたフラグメントは停止します(破棄はされません)。ユーザーが「戻る」操作を行ってフラグメントを復元すると、フラグメントは再開されます。トランザクションをバックスタックに追加しないと、削除または置換時にフラグメントは破棄されます。

フラグメントを別のフラグメントに置き換える例を次に示します。

// 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 を使用して高度なフラグメント操作を実行する予定がない場合、名前は必要ありません。