유연한 UI 빌드

다양한 화면 크기를 지원하는 애플리케이션을 디자인하는 경우, 사용 가능한 화면 공간을 토대로 사용자 경험을 최적화하도록 Fragment를 여러 레이아웃 구성에 재사용할 수 있습니다.

예를 들어 송수화기에서는 단일 창 사용자 인터페이스에 한 번에 하나의 Fragment만 표시하는 것이 적합할 수 있습니다. 반대로 화면이 커서 사용자에게 더 많은 정보를 표시하는 태블릿에서는 Fragment를 나란히 배치할 수 있습니다.

그림 1. 크기가 다른 화면에 동일한 Activity에 대해 각기 다른 구성으로 표시된 두 개의 Fragment. 큰 화면에서는 두 Fragment가 나란히 표시되며, 송수화기에서는 Fragment가 한 번에 하나만 표시되고 사용자가 탐색할 때 Fragment가 서로 교체됩니다.

FragmentManager 클래스는 동적 경험을 연출하기 위해 런타임에 Activity에 Fragment를 추가, 삭제 및 교체할 수 있는 메서드를 제공합니다.

Fragment 구현에 대한 자세한 내용은 다음 리소스를 참조하세요.

런타임에 Activity에 Fragment 추가

<fragment> 요소를 사용하여 이전 과정에서 소개한 것처럼 레이아웃 파일에 Activity의 Fragment를 정의하기 보다는 Activity 런타임 중 Fragment를 Activity에 추가할 수 있습니다. 이는 Activity의 수명 도중 Fragment를 변경하려는 경우 필요합니다.

Fragment 추가 또는 삭제와 같은 트랜잭션을 수행하려면 FragmentManager를 사용하여 FragmentTransaction을 생성해야 합니다. FragmentTransaction은 Fragment를 추가, 삭제 및 교체하고 기타 Fragment 트랜잭션을 수행하는 API를 제공합니다.

Activity에서 Fragment의 교체와 삭제를 허용하는 경우, Activity의 onCreate() 메서드를 실행하는 동안 초기 Fragment를 Activity에 추가해야 합니다.

Fragment 작업 시, 특히 런타임에 Fragment를 추가할 때 유념해야 할 중요한 규칙은 Fragment를 삽입할 수 있는 컨테이너 View를 Activity 레이아웃에 포함하는 것입니다.

다음은 이전 과정에서 소개한, 한 번에 하나의 Fragment만 표시하는 레이아웃에 대한 대체 레이아웃입니다. Fragment를 다른 Fragment와 교체하기 위해 Activity 레이아웃은 Fragment 컨테이너로 작용하는 빈 FrameLayout을 포함합니다.

파일 이름이 이전 과정의 레이아웃 파일과 같지만, 레이아웃 디렉토리에 large 한정자가 없음을 주의하세요. 이 레이아웃은 기기 화면이 대형보다 작아서 두 개의 Fragment를 동시에 표시할 수 없을 때 사용합니다.

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" />

Activity에서 지원 라이브러리 API를 통해 getSupportFragmentManager()를 호출하여 FragmentManager를 가져옵니다. 그런 다음 beginTransaction()을 호출하여 FragmentTransaction을 생성하고 add()를 호출하여 Fragment를 추가합니다.

동일한 FragmentTransaction을 사용하여 Activity의 여러 Fragment 트랜잭션을 수행할 수 있습니다. 변경할 준비가 되면 commit()을 호출해야 합니다.

다음은 이전 레이아웃에 Fragment를 추가하는 방법에 대한 예입니다.

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가 <fragment> 요소로 Activity 레이아웃에 정의되는 대신 런타임에 FrameLayout 컨테이너에 추가되었기 때문에, Activity가 이 Fragment를 삭제하고 다른 Fragment로 교체할 수 있습니다.

다른 Fragment와 교체

Fragment 교체 절차는 추가 절차와 비슷하지만, add() 대신 replace() 메서드를 사용해야 합니다.

Fragment 교체 또는 삭제와 같은 Fragment 트랜잭션 수행 시, 사용자가 뒤로 돌아가서 변경 사항을 "실행 취소"할 수 있도록 허용하는 것이 필요할 때도 있습니다. Fragment 트랜잭션에서 사용자가 뒤로 돌아갈 수 있게 하려면, FragmentTransaction을 커밋하기 전에 addToBackStack()을 호출해야 합니다.

참고: Fragment를 삭제 또는 교체하고 해당 트랜잭션을 백 스택에 추가하면 삭제된 Fragment가 소멸되지 않고 중단됩니다. 사용자가 되돌아가서 Fragment를 복원하면 Fragment가 다시 시작됩니다. 트랜잭션을 백 스택에 추가하지 않으면, Fragment가 삭제되거나 교체될 때 소멸됩니다.

다른 Fragment와의 교체 예:

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를 사용하여 고급 Fragment 작업을 수행하려는 경우 외에는 이 이름이 필요하지 않습니다.