프레그먼트 만들기(Creating a Fragment)
프레그먼트를 만들기 위해서는 반드시 Fragment 클래스를 상속받아야 한다. (또는 이미 생성한 서브클래스를 상속받아도 됨) 프레그먼트 클래스는 액티비티와 매우 유사한 코드로 작성되어있는데, onCreate(), onStart(), onPause(), onStop()과 같은 액티비티에서 제공하던 유사한 콜백함수를 제공한다. 사실, 기존에 개발했던 앱을 프레그먼트를 이용하도록 변경한다면 액비티비 콜백 함수들에서 구현했던 코드를 각각 프레그먼트의 콜백함수로 그대로 옮겨와도 된다.
일반적으로 우리는 최소한 아래 나열된 생명주기(lifecycle) 함수를 구현해야한다.
onCreate()
프레그먼트가 생성될 때, 시스템이 호출한다. 구현 시, 프레그먼트가 pause 또는 stop 되었다가 다시 resume이 되는 상황에 영향받지 않도록 유지해야하는 필수적인 콤포넌트를 초기화해야 한다.
onCreateView()
프레그먼트가 처음으로 UI를 그리는 시점에 시스템이 호출한다. 프레그먼트에 UI를 넣으려면 프레그먼트 레이아웃의 루트격인 이 함수에서 VIew를 리턴해야한다. 만약 UI가 필요없는 프레그먼트인 경우 null을 반환하면 된다.
onPause()
사용자가 프레그먼트를 떠나는 순간 시스템에서 호출한다. (프레그먼트가 반드시 destroy되는 상황은 아니다) 이 함수는 보통 해당 사용자의 세션에서 유지되어야 하는 모든 변경 사항을 저장하는 곳이다. (당장에는 사용자가 해당 프레그먼트로 돌아오지 않을 수 있기 때문이다)
대부분의 앱은 프레그먼트를 작성할 때 적어도 위의 3개 함수를 구현해야 한다. 하지만, 프레그먼트의 생명주기의 다양한 상황을 다루기 위해서는 위 3개의 콜백함수 외에도 다른 콜백함수들을 작성해야한다. 모든 콜백 함수에 대한 더 자세한 내용은 다른 글에서 다두도록한다.
안드로이드는 기본 Fragment 클래스 대신, 쓸만한 몇 개의 서브 클래스를 제공한다.
DialogFragment
둥실둥실(?) 떠있는 다이얼로그를 띄울 때 사용한다. 액티비티 클래스에서 제공하던 Dialog Helper 함수 대신 사용하기 적절한데, 이유는 프레그먼트 다이얼로그를 액티비티에서 관리하는 프레그먼트들의 back stack 속에 포함시킬 수 있어서 사용자를 이전 프레그먼트로 돌아갈 수 있도록 할 수 있기 때문이다.
ListFragment
(SimpleCursorAdapter와 같은) 어댑터에 의해 관리되는 리스트 항목을 표출할 수 있는 ListActivity와 유사한 클래스다. 사용자 클릭을 처리하는 onListItemClick() 콜백 함수와 같이 리스트뷰를 관리할 수 있는 다양한 함수를 제공한다.
PreferencesFragment
ReferencesActivity와 유사하게 리스트 형태로 환경설정 계층을 표출할 수 있다. 우리의 앱에서 환경설정 액티비티를 제공할 때 유용하다.
액티비티가 동작중일 때의 프레그먼트의 생명주기
출처 : http://developer.android.com/guide/components/fragments.html
UI 추가하기 (Adding a user interface)
프레그먼트는 보통 액티비티의 한 부분을 차지하면서 액티비티에 자신의 레이아웃을 제공한다.
프레그먼트를 위한 레이아웃을 반영하려면 onCreateView() 콜백함수를 구현해야하는데, 위에서 설명한대로 이 함수는 프레그먼트가 레이아웃을 그리는 시점에 시스템에서 호출하는 콜백함수다. 이 함수를 구현할 때는 프레그먼트 레이아웃의 루트가 될 View를 리턴해야 한다.
주의사항 : ListFragment의 서브클래스인 경우, onCreateView()에서 디폴트로 ListView를 리턴하기 때문에 별도의 추가 구현이 필요없다.
onCreateView()에서 레이아웃을 리턴하려면, XML로 정의해둔 레이아웃 리소스를 이용하여 inflate 할 수 있다. 이 과정을 쉽게 할 수 있도록 onCreateView()가 호출될 때 LayoutInflater 오브젝트가 전달된다.
아래 코드는 example_fragment.xml 파일을 로드하는 프레그먼트의 서브클래스의 사용 예이다.
public static class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
onCreateView()로 전달된 container 파라메터는 액티비티가 소유한 부모 ViewGroup인데, 이 그룹에 프레그먼트가 속하게 되는 것이다. 또다른 파라메터 savedInstanceState는 Bundle 오브젝트인데, 프레그먼트가 resume될 때, 프레그먼트의 이전 인스턴스 데이터가 담겨있다. (이에 대한 내용은 Handling the Fragment Lifecycle 섹션에서 자세히 다루도록 하자)
inflate() 함수를 호출하려면 3개의 매개변수가 필요하다.
- inflate 하려는 레이아웃의 리소스 ID
- inflate된 레이아웃의 부모가 될 ViewGroup. container(ViewGroup)객체를 전달하는 것은 매우 중요한데, 이는 시스템이 레이아웃 파라메터가 inflate되었을 때 이 녀석의 루트뷰(레이아웃이 속할 부모뷰에 의해 정의된)가 되도록 적용시켜야하기 때문이다.
- inflate 작업이 진행되는 동안, inflate된 레이아웃이 ViewGroup(두 번째 파라메터)에 적용(attatched)되어야 하는지 여부를 판단하는 boolean값. (여기서는 시스템이 이미 inflate된 레이아웃을 container에 집어넣었기 때문에 false값을 부여했다. 만약, 이 값을 true로 하면, 맨 마지막 레이아웃에서 불필요한 뷰 그룹을 생성하게 된다.)
여기까지 우리는 레이아웃을 갖는 프레그먼트를 만드는 법을 배웠다. 이제는 액티비티에 프레그먼트를 집어넣는 방법에 대해 알아보자.
액티비티에 프레그먼트 넣기(Adding a fragment to an activity)
일반적으로, 프레그먼트는 액티비티의 전체 뷰 계층의 일부로 포함되어있는 상위 액티비티에 UI 요소를 제공한다. 액티비티 레이아웃에 프레그먼트를 추가하는 방법에는 2가지가 있다.
- 액티비티 레이아웃 파일에 fragment 선언하는 방법
이 경우, view에서 했던 것 처럼 레이아웃 속성을 작성할 수 있다. 아래는 두 개의 프레그먼트를 갖는 액티비티의 레이아웃 파일의 예시이다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
<fragment> 안에 있는 "android:name" 속성은 Fragment 클래스가 레이아웃에서 인스턴스화되는 것을 의미한다. 시스템이 액티비티 레이아웃을 생성할 때, 레이아웃안에 정의된 각각의 프레그먼트의 인스턴스를 만들고 프레그먼트 레이아웃을 반환하도록 각 프레그먼트의 onCreateView()함수를 호출한다. 시스템은 프레그먼트에서 직접 반환한 View를 <fragment> 요소를 대체하여 추가한다.
주의 : 액티비티가 재실행되는 경우 프레그먼트를 복구하기 위해 시스템에서 사용하는 프레그먼트의 유니크한 ID가 필요하다. (또한, 프레그먼트 삭제와 같은 조작을 할 때도 프레그먼트를 구분하는데 사용할 수도 있다.) 프레그먼트를 위한 ID를 부여하는데는 3가지 방법이 있다.
- "android:id" 속성을 통해 유니크한 ID를 부여한다.
- "android:tag" 속성을 통해 유니크한 문자열를 부여한다.
- 위의 두 개를 모두 부여하지 않는 경우, 시스템은 컨테이너뷰의 ID를 사용한다.
- 직접 구현을 통해 이미 생성되어 있는 ViewGroup에 프레그먼트를 추가하는 방법
액티비티가 실행되는 동안에 우리는 액티비티 레이아웃에 프레그먼트를 추가할 수 있다. 이를 위해 간단하게 프레그먼트가 위치할 ViewGroup을 지정하면 된다.
프레그먼트 추가, 삭제, 교체 등과 같은 액비티비 내에서 프레그먼트를 조작하려면 반드시 FragmentTransaction에서 제공하는 API들을 사용해야한다. FragmentTransaction의 인스턴스는 Activity로 부터 얻을 수 있는데, 아래처럼 구현하면 된다.
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
;
이제, 추가할 프레그먼트와 이를 포함할 뷰를 지정해주는 add()함수를 통해 프레그먼트를 추가할 수 있다.
아래 코드를 보자.
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
add()함수로 전달하는 첫번째 인자는 프레그먼트가 위치해야할 ViewGroup의 리소스 ID이고, 두번째 파라메터는 추가할 프레그먼트 객체다.
FragmentTransaction으로 변경할 내용을 완료했으면 실제 적용을 시키기 위해 commit()함수를 반드시 호출해야한다.
UI없는 프레그먼트 추가하기 (Adding a fragment without a UI)
위에서는 액티비티에 UI를 제공할 목적으로 프레그먼트를 만드는 법에 대해 설명했다. 하지만, 별도의 UI를 표출하지 않고 액티비티의 백그라운드 작업만을 수행하도록 만들 수도 있다.
UI없는 프레그먼트를 추가하려면 액티비티내에서 add(Fragment, String) 함수를 이용하여 추가하면 된다. (View ID보다는 유니크한 Tag 문자열을 부여하는 것이 더 좋다) 이 함수는 프레그먼트를 추가하긴 하지만 액티비티 레이아웃에 있는 뷰와는 연결되지 않기 때문에 onCreateView()함수가 호출되지 않는다. 즉, 이 콜백함수를 구현하지 않아도 된다.
프레그먼트에 문자열 Tag를 부여하는 것은 UI없는 프레그먼트만을 위한 것은 아니다. (UI를 갖는 프레그먼트에도 문자열 Tag를 부여할 수 있다.) 하지만, UI가 없는 프레그먼트의 경우라면 이 문자열 Tag는 해당 프레그먼트를 식별할 수 있는 유일한 정보다. 만약, 구동 중에 액티비티로부터 해당 프레그먼트 객체를 받아오고 싶다면 findFragmentByTag()함수를 사용하면 된다.
UI없이 백그라운드 작업을 수행하는 프레그먼트가 포함된 액티비티 클래스를 참고하려면 FragmentRetainInstance.java 샘플 코드를 참고하자.
[원문보기]