Register now for Android Dev Summit 2019!

다른 Fragment와 통신

Fragment UI 구성 요소를 재사용하려면 각 구성 요소를 자체 레이아웃 및 동작을 정의하는 완전히 자체 포함된 모듈식 구성 요소로 구축해야 합니다. 이러한 재사용 가능한 Fragment를 정의한 후에는 Activity와 연결하고 애플리케이션 로직과 연결하여 전반적인 복합 UI를 실현할 수 있습니다.

예를 들어 사용자 이벤트를 기반으로 콘텐츠를 변경하려는 경우와 같이 한 Fragment가 다른 Fragment와 통신하도록 해야 할 경우가 종종 있습니다. 모든 Fragment 간 통신은 공유된 ViewModel 또는 관련 Activity를 통해 실행됩니다. 두 Fragment는 직접 통신해서는 안 됩니다.

Fragment 간 통신은 공유된 ViewModel 객체를 생성하는 방법을 권장합니다. 두 Fragment는 모두 포함된 Activity를 통해 ViewModel에 액세스할 수 있습니다. 이 Fragment는 ViewModel 내에서 데이터를 업데이트하고 이 데이터가 LiveData를 사용하여 노출될 경우 다른 Fragment로 새 상태가 전달됩니다. 단, ViewModel에서 LiveData를 관찰하고 있어야 합니다. 이런 종류의 통신을 구현하는 방법은 ViewModel 가이드의 'Fragment 간 데이터 공유'를 참조하세요.

공유된 ViewModel을 사용하여 Fragment 간 통신이 불가능할 경우 인터페이스를 사용한 통신 플로를 직접 구현할 수 있습니다. 그러나 이 방법은 구현하는 데 더 많은 노력이 들어가고 다른 Fragment에서 쉽게 다시 사용할 수 없습니다.

인터페이스 정의

Fragment가 해당 Activity와 통신하게 하려면 Fragment 클래스에서 인터페이스를 정의하고, 이를 Activity에서 구현하면 됩니다. Fragment는 해당 onAttach() 수명 주기 메서드 동안 인터페이스 구현을 캡처하며, Activity와의 통신을 위해 인터페이스 메서드를 호출할 수 있습니다.

다음은 Fragment와 Activity 간 통신의 예입니다.

HeadlinesFragment

Kotlin

class HeadlinesFragment : ListFragment() {
    internal var callback: OnHeadlineSelectedListener

    fun setOnHeadlineSelectedListener(callback: OnHeadlineSelectedListener) {
        this.callback = callback
    }

    // This interface can be implemented by the Activity, parent Fragment,
    // or a separate test implementation.
    interface OnHeadlineSelectedListener {
        fun onArticleSelected(position: Int)
    }

    // ...
}

Java

public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener callback;

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener callback) {
        this.callback = callback;
    }

    // This interface can be implemented by the Activity, parent Fragment,
    // or a separate test implementation.
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    // ...
}

MainActivity

Kotlin

class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener {
    // ...

    fun onAttachFragment(fragment: Fragment) {
        if (fragment is HeadlinesFragment) {
            fragment.setOnHeadlineSelectedListener(this)
        }
    }
}

Java

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    // ...

    @Override
    public void onAttachFragment(Fragment fragment) {
        if (fragment instanceof HeadlinesFragment) {
            HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
            headlinesFragment.setOnHeadlineSelectedListener(this);
        }
    }
}

이제 Fragment는 OnHeadlineSelectedListener 인터페이스의 mCallback 인스턴스를 이용하여 onArticleSelected() 메서드(또는 인터페이스 내 다른 메서드)를 호출함으로써 메시지를 Activity에 전달할 수 있습니다.

예를 들어 사용자가 목록 항목을 클릭하면 Fragment 내의 다음과 같은 메서드가 호출됩니다. Fragment는 콜백 인터페이스를 이용하여 이벤트를 상위 Activity에 전달합니다.

Kotlin

override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
    // Send the event to the host activity
    callback.onArticleSelected(position)
}

Java

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    // Send the event to the host activity
    callback.onArticleSelected(position);
}

인터페이스 구현

Fragment에서 이벤트 콜백을 받으려면 Fragment를 호스팅하는 Activity가 Fragment 클래스에 정의된 인터페이스를 구현해야 합니다.

예를 들어 다음 Activity는 위에 나와 있는 예의 인터페이스를 구현합니다.

Kotlin

class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener {
    ...

    fun onArticleSelected(position: Int) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

Java

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

Fragment에 메시지 전달

호스트 Activity는 findFragmentById()Fragment 인스턴스를 캡처하여 Fragment에 메시지를 전달한 다음, Fragment의 공용 메서드를 직접 호출할 수 있습니다.

예를 들어 위에 나와 있는 Activity가 위의 콜백 메서드에서 반환된 데이터가 지정한 항목을 표시하는 데 사용되는 다른 Fragment를 포함할 수 있다고 가정해 보세요. 이 경우 Activity는 콜백 메서드로 받은 정보를 항목을 표시할 다른 Fragment에 전달할 수 있습니다.

Kotlin

class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener {
    ...

    fun onArticleSelected(position: Int) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        val articleFrag = supportFragmentManager.findFragmentById(R.id.article_fragment) as ArticleFragment?

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position)
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...
            // Create fragment and give it an argument for the selected article
            val newFragment = ArticleFragment()
            val args = Bundle()
            args.putInt(ArticleFragment.ARG_POSITION, position)
            newFragment.arguments = args

            val transaction = supportFragmentManager.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()
        }
    }
}

Java

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...

            // Create fragment and give it an argument for the selected article
            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();
        }
    }
}

Fragment 구현에 대한 자세한 내용은 Fragments를 참조하세요. 관련 샘플 앱을 살펴보면서 더욱 자세히 알아볼 수도 있습니다.