Fragment - 4. 액티비티와의 통신
액티비티와의 통신
Fragment
는 Activity
로부터 독립적인 객체로 구현되었고 여러 개의 액티비티 안에서 사용할 수 있는 것이 사실이지만, 프래그먼트의 주어진 인스턴스는 그것을 포함하고 있는 액티비티에 직접적으로 연결되어 있습니다.
구체적으로 말하면, 이 프래그먼트는 getActivity()
를 사용하여 Activity
인스턴스에 액세스하여 액티비티 레이아웃에서 뷰를 찾는 것과 같은 작업을 손쉽게 수행할 수 있습니다.
View listView =getActivity()
.findViewById
(R.id.list);
이와 마찬가지로, 액티비티도 프래그먼트 안의 메서드를 호출할 수 있습니다. 그러려면 FragmentManager
로부터의 Fragment
에 대한 참조를 가져와야 하며, 이때 findFragmentById()
또는 findFragmentByTag()
를 사용합니다. 예:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
액티비티로의 이벤트 콜백 생성
어떤 경우에는 프래그먼트로 하여금 액티비티와 이벤트를 공유하게 해야 할 수 있습니다. 이렇게 하기 위한 한 가지 좋은 방법은 프래그먼트 내부의 콜백 인터페이스를 정의한 다음 해당 호스트 액티비티가 이를 구현하도록 하는 것입니다. 액티비티가 인터페이스를 통해 콜백을 수신하면, 필요에 따라 그 정보를 레이아웃 내의 다른 프래그먼트와 공유할 수 있습니다.
예를 들어 어떤 뉴스 애플리케이션에서 액티비티 하나에 프래그먼트가 두 개 있습니다. 하나는 기사 목록을 표시(프래그먼트 A)하고 다른 하나는 기사 하나를 표시(프래그먼트 B)하는 경우 목록 항목이 선택되면 프래그먼트 A가 액티비티에 알려야 프래그먼트 B에 해당 기사를 표시하라고 알릴 수 있습니다. 이 경우, OnArticleSelectedListener
인터페이스는 프래그먼트 A 내부에 선언됩니다.
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
그러면 프래그먼트를 호스팅하는 액티비티가 OnArticleSelectedListener
인터페이스를 구현하고 onArticleSelected()
를 재정의하여 프래그먼트 A로부터 발생한 이벤트를 프래그먼트 B에 알립니다. 호스트 액티비티가 이 인터페이스를 구현하도록 하려면 프래그먼트 A의 onAttach()
콜백 메서드(프래그먼트를 액티비티에 추가할 때 시스템이 호출하는 메서드)가 OnArticleSelectedListener
의 인스턴스를 생성해야 합니다. 이때 onAttach()
로 전달되는 Activity
를 형변환하는 방법을 씁니다.
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}
액티비티가 인터페이스를 구현하지 않은 경우, 프래그먼트가 ClassCastException
을 발생시킵니다. 성공 시, mListener
멤버가 액티비티의OnArticleSelectedListener
구현에 대한 참조를 보유하므로, 프래그먼트 A가 액티비티와 이벤트를 공유할 수 있습니다. 이때 OnArticleSelectedListener
인터페이스가 정의한 메서드를 호출하는 방법을 사용합니다. 예를 들어 프래그먼트 A가 ListFragment
의 확장인 경우, 사용자가 목록 항목을 클릭할 때마다 시스템이 프래그먼트 안의 onListItemClick()
을 호출하고, 그러면 이것이 onArticleSelected()
를 호출하여 해당 이벤트를 액티비티와 공유하는 것입니다.
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri
Uri noteUri = ContentUris.withAppendedId
(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
}
...
}
onListItemClick()
에 전달된 id
매개변수는 클릭한 항목의 행 ID이며, 액티비티(또는 다른 프래그먼트)가 이것을 사용해 애플리케이션의 ContentProvider
에서 기사를 가져옵니다.
콘텐츠 제공자 사용법에 대한 자세한 정보는 콘텐츠 제공자 문서에서 이용하실 수 있습니다.
앱 바에 항목 추가
프래그먼트는 액티비티의 옵션 메뉴에(결과적으로 앱 바에도) 메뉴 항목을 추가할 수 있습니다. 이때 onCreateOptionsMenu()
를 구현하는 방법을 씁니다. 이 메서드가 호출을 수신하도록 하려면, setHasOptionsMenu()
중에 onCreate()
를 호출하여 프래그먼트가 옵션 메뉴에 항목을 추가하고자 한다는 것을 나타내야 합니다(그렇지 않으면 해당 프래그먼트가 onCreateOptionsMenu()
로의 호출을 받지 못하게 됩니다).
그런 다음 프래그먼트로부터 옵션 메뉴에 추가하는 모든 항목은 기존의 메뉴 항목에 추가됩니다. 메뉴 항목을 선택하면 해당 프래그먼트는 onOptionsItemSelected()
콜백도 수신하게 됩니다.
또한 프래그먼트 레이아웃에 뷰를 등록하여 컨텍스트 메뉴를 제공하도록 할 수도 있습니다. 이때 registerForContextMenu()
를 호출하면 됩니다. 사용자가 컨텍스트 메뉴를 열면, 해당 프래그먼트는 {@linkandroid.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) onCreateContextMenu()} 호출을 수신합니다. 사용자가 항목을 선택하면, 해당 프래그먼트는 onContextItemSelected()
호출을 수신합니다.
참고: 프래그먼트는 추가한 각 메뉴 항목이 선택될 때 콜백을 받게 되지만, 사용자가 메뉴 항목을 선택할 때 그에 상응하는 콜백을 가장 처음 받는 것은 액티비티입니다. 액티비티가 구현한 항목 선택 시 콜백이 선택된 항목을 다루지 않는 경우, 해당 이벤트는 프래그먼트의 콜백으로 전달됩니다. 이는 옵션 메뉴와 컨텍스트 메뉴에 모두 해당됩니다.