Android 11 デベロッパー プレビュー 2 が公開されました。ぜひお試しのうえ、フィードバックをお寄せください

フレキシブルな UI を作成する

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

たとえば、スマートフォンのように画面が小さく、複数のパネルを表示すると見にくくなる場合は、フラグメントを 1 つのみ表示します。一方、より横幅の広いタブレットでは、フラグメントを横並びに配置してより多くの情報をユーザーに表示できます。

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

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

フラグメントの実装について詳しくは、以下のリソースをご覧ください。

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

前のレッスンでは、<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" />
    

アクティビティ内で Support Library API を使用し、getSupportFragmentManager() を呼び出して FragmentManager を取得します。次に、beginTransaction() を呼び出して FragmentTransaction を作成し、add() を呼び出してフラグメントを追加します。

同じ FragmentTransaction を使用して、アクティビティに対して複数のフラグメント トランザクションを実行できます。変更の準備ができたら、commit() を呼び出す必要があります。

次に、上のレイアウトにフラグメントを追加する方法を示します。

Kotlin

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

    class MainActivity : FragmentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            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
                val firstFragment = HeadlinesFragment()

                // In case this activity was started with special instructions from an
                // Intent, pass the Intent's extras to the fragment as arguments
                firstFragment.arguments = intent.extras

                // Add the fragment to the 'fragment_container' FrameLayout
                supportFragmentManager.beginTransaction()
                        .add(R.id.fragment_container, firstFragment).commit()
            }
        }
    }
    

Java

    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() を呼び出す必要があります。

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

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

Kotlin

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

    val transaction = supportFragmentManager.beginTransaction().apply {
      // 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
      replace(R.id.fragment_container, newFragment)
      addToBackStack(null)
    }

    // Commit the transaction
    transaction.commit();
    

Java

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