프레그먼트 다루기 (Managing Fragments)
액티비티에서 프레그먼트를 다루기 위해서는 FragmentManager를 이용해야한다. FragmentManager의 객체를 얻으려면 액티비티에서 제공하는 getFragmentManager()를 호출하면 된다.
FragmentManager에서 제공하는 기능으로 할 수 있는 것을 나열해 보면...
- findFragmentById() 또는 findFragmentByTag() 함수로 액티비티가 갖고 있는 프레그먼트들에 접근할 수 있다. UI를 갖는 프레그먼트의 경우 findFragmentById(), findFragmentByTag() 둘 중 하나를 사용하면되는데, UI가 없는 프레그먼트의 경우에는 findFragmentByTag()함수만을 사용할 수 있다.
- popBackStack() 함수를 사용해서 백스택으로부터 프레그먼트를 꺼낼 수 있다. (사용자가 Back 명령을 했을때)
- 백스택의 변경 사항을 처리하기 위한 리스너는 addOnBackStackChangedListener()함수로 등록할 수 있다.
이러한 함수들의 좀 더 자세한 내용은 FragmentManager 클래스에 대한 문서를 참고하자.
이전 글에서 거론한대로 우리는 FragmentManager 클래스를 통해 프레그먼트 트랜잭션(추가, 삭제 등)을 하는데 필요한 FragmentTransaction 객체를 생성할 수 있다.
프레그먼트 트랜잭션 수행하기 (Performing Fragment Transactions)
액티비티에서 프레그먼트를 사용하는 것의 가장 큰 특징은 사용자의 동작에 따라 추가, 삭제, 교체 그리고 그 외 다른 동작들을 수행할 수 있다는 것이다. 액티비티에 적용한 각각의 변경 셋트를 Transaction(이하 트랜잭션)이라 부르는데, FragmentTransaction 클래스가 제공하는 API를 사용해서 이러한 트랜잭션을 수행할 수 있다. 또한, 각각의 트랜잭션에 대한 이력을 액티비티가 관리하는 백스택에 저장한 후 프레그먼트를 조작해서 사용자에게 뒤로가기 기능을 제공할 수 있다.
FragmentTransaction의 인스턴스는 FragmentManager 클래스로부터 가져올 수 있다.
FragmentManager fragmentManager =getFragmentManager()
;
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
;
각각의 트랜잭션은 우리가 한 번에 수행하고자하는 변경 내용의 셋트라고 할 수 있다. 우리에게 주어진 하나의 트랜잭션 동안 add(), remove(), replace()와 같은 함수를 사용해서 원하는 변경 내용을 모두 모아 셋트를 구성할 수 있다. 그 다음에 반드시 commit() 함수를 호출해서 실제 액티비티에 이 트랜잭션을 반영하면 된다.
이번 변경사항을 백스택에 추가하기 위해 commit() 함수를 호출하기 전에 addToBackStack()을 호출할 수도 있다. 백스택은 액티비티가 관리하는데, 백버튼을 눌렀을 때 사용자에게 이전 프레그먼트 상태로 되돌려줄 수 있도록 해준다.
예를 들어 아래 코드는 하나의 프레그먼트를 다른 녀석으로 교체해주면서 백스택에 이전 상태를 저장하고 있다.
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
이 코드에서 newFragment는 R.id.fragment_container ID로 식별되는 레이아웃 컨테이너에 포함된 다른 프레그먼트를 대체하게 된다. addToBackStack()함수를 호출해서 교체하는 트랜잭션은 백스택에 저장되고 사용자는 이 트랜잭션을 되돌릴 수 있게 되어 결과적으로 백버튼을 눌러 이전 프레그먼트로 돌아올 수 있다.
만약 우리가 add(), remove()와 같은 여러 변경 사항을 한 트랜잭션에 추가하고 addToBackStack()을 호출한다면, commit()을 호출하기 전의 모든 변경 내용은 개별 트랜잭션 단위로 백스택에 추가되고 백버튼은 그 모든 변경 내용을 되돌려줄 것이다.
FragmentTransaction 클래스에 변경 내용을 추가할때는 호출 순서가 크게 상관없지만, 아래 내용은 주의해야한다.
- commit()함수는 맨 마지막에 호출해야한다.
- 동일한 컨테이너에 다수의 프레그먼트를 추가할 때, 그 추가하는 순서는 뷰 계층에서 화면에 보여지는 순서를 결정한다.
프레그먼트를 삭제하는 트랜잭션을 수행할 때, addToBackStack()을 호출하지 않으면 해당 프레그먼트는 트랜잭션이 최종반영(commit)될 때 완전 제거(destroy)되어 사용자는 그 프레그먼트로 돌아갈 수 없게된다. 반면, addToBackStack()함수를 호출했을 때는 프레그먼트는 잠시 정지(stop)되고 사용자가 이전으로 되돌아오려고 할 때 재개(resume)된다.
주의 : 각각의 프레그먼트 트랜잭션이 수행되는 동안, 우리는 최종반영(commit) 이전에 setTransitiojn()을 호출해서 애니메이션을 적용할 수 있다.
commit()을 호출한다고 해서 트랜잭션이 바로 실행되지 않고 액티비티의 UI 쓰레드(메인쓰레드)의 스케쥴에 따르게되고 이 쓰레드가 수행 가능할 때에 맞춰서 실행된다. 하지만, 우리는 executePendingTransactions()을 호출해서 UI쓰레드가 commit()호출되는 즉시 트랜잭션이 실행되도록 할 수 있다. 하지만, 이렇게 하는 것은 해당 트랜잭션이 다른 쓰레드와 의존적이지 않은 경우라면 딱히 필요하지 않다.
주의 : 우리는 액티비티가 현재 상태를 저장하기 전(사용자가 액티비티를 떠날 때)에만 commit()함수를 사용할 수 있다. 이 시점 이후에 commit을 시도하면 예외가 발생한다. 이렇게 한 이유는 액티비티가 원복될 때 commit 이전의 상태가 저장되지 않을 수 있기 때문이다. commit에 대한 내용이 날라가도 괜찮은 상황이라면 commitAllowingStateLoss()함수를 사용하면 된다.
'Android' 카테고리의 다른 글
InentService를 구현했으나, onHandleIntent()가 호출되지 않을 때 (2) | 2014.08.21 |
---|---|
[Android] 화면꺼짐 상태에서 GCM 메시지 수신 안되는 문제 (2) | 2014.01.24 |
[번역글] 프레그먼트 #3 - 프레그먼트 만들기(Creating a Fragment) (2) | 2014.01.14 |
google-play-services_lib 업데이트 후, 죽는 문제 (3) | 2014.01.13 |
[번역글] 프레그먼트 #2 - 디자인 철학(Design Philosophy) (2) | 2014.01.08 |