안드로이드 스튜디오 예제 6 – 프래그먼트 간 통신

예제 5에서 설명했듯이 서로 다른 프래그먼트들 끼리는 직접적으로 정보를 주고 받을 수 없다. 아마도 프래그먼트는 재사용성에 중점을 두고 설계된 요소이기 때문에 이러한 정책을 취하고 있는 것 같다. 만약 A라는 프래그먼트에서 B라는 프래그먼트의 setData()라는 함수를 호출하는 식으로 개발했을 때 , 다른 프래그먼트들에는 setData()라는 함수의 존재 여부가 보장되지 않기 때문에 재사용성에 문제가 발생할 수 있기 때문이다. 따라서, 프래그먼트가 바인딩되어 있는 호스트 액티비티를 통해서만 통신이 가능하도록 되어있다. FirstFragment -> MainActivity -> SecondFragment 방식으로 정보를 전달하는 것은 예제 5에서 살펴보았다. FirstFragment에 CustomOnClickListener 인터페이스를 선언하여 사용하였고, MainActivity는 CustomOnClickListener를 상속받아서 버튼 클릭 이벤트를 처리하였다. 이는 많은 책이나 온라인 예제에서 다루는 방식이다. 하지만, 다소 복잡함에서 느껴지는… 처음 봤을 때부터 뭔가 코드가 직관적이지 못하여 애착이 생기지 않는 방식이라는 생각이 들었다. 따라서, “좀 더 직관적이고 깔끔하게 프래그먼트 사이의 통신을 처리할 수는 없을까?”라는 생각에서 테스트 삼아 시도를 해보았는데, 예상대로 잘 작동하였다. 이런식으로 해도 별 문제가 없는 것인지는 현재로서는 잘 모르겠다. 하지만, 개인적으로는 본 예제에서의 방식이 더 마음에 든다. 본 예제에서도 역시 FirstFragment -> MainActivity -> SecondFragment 방식으로 정보를 전달하기는 하지만 커스텀 이벤트 리스너 인터페이스를 사용하지 않는다. FirstFragment에서 MainActivity의 public 함수를 직접 호출하며, 이 함수에서 SecondFragment의 public 함수를 호출하는 방식으로 프래그먼트들 사이의 데이터 전달을 수행한다. 앞의 예제들에서 각 단계를 스크린 샷을 이용해서 충분히 자세하게 설명했으므로, 본 예제에서는 스크린 샷의 사용 없이 내용 위주로 설명하겠다.

——————————————————————————————————–

<프로젝트 생성>
1. 안드로이드 스튜디오를 실행하고, Start a new Android Studio project를 선택한다.
2. New Project 창이 나타나면, Application name에 FragmentsTestApplication을 입력하고 Next 버튼을 클릭한다.
3. Target Android Devices 창이 나타나면, Phone and Table, 그리고 Minimum SDK는 API 22를 선택하고 Next 버튼을 클릭한다.
4. Add an activity to Mobile 창이 나타나면, Blank Activity를 선택하고 Next 버튼을 클릭한다.
5. Customize the Activity 창이 나타나면, 기본 설정 그대로 Finish 버튼을 클릭한다.
6. content_main.xml이 선택되어 있는 상태에서, “Hello World!”가 적혀있는 TextView를 선택하고 삭제한다.

<첫 번째 프래그먼트 UI 생성>
7. app/res/layout을 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, New/Layout resource file을 선택한다.
8. New Resource File 창이 나타나면, File name에 first_fragment, Root element에 RelativeLayout을 입력하고, OK 버튼을 클릭한다. (File name에는 대문자를 사용할 수 없다.)
9. Palette/Widgets/Button을 drag & drop으로 최상단 왼쪽 끝에 배치하고, 더블클릭하여 text에 Button1, id에 button1을 입력하고 [Enter] 버튼을 클릭한다. (버튼 왼쪽에 나타나는 전구 모양의 아이콘은 무시한다.)
10.Palette/Widgets/Button을 drag & drop으로 최상단 오른쪽 끝에 배치하고, 더블클릭하여 text에 Button2, id에 button2를 입력하고 [Enter] 버튼을 클릭한다. (버튼 왼쪽에 나타나는 전구 모양의 아이콘은 무시한다.)

<두 번째 프래그먼트 UI 생성>
11. app/res/layout을 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, New/Layout resource file을 선택한다.
12. New Resource File 창이 나타나면, File name에 second_fragment, Root element에 RelativeLayout을 입력하고, OK 버튼을 클릭한다. (File name에는 대문자를 사용할 수 없다.)
13. Palette/Widgets/Large Text를 drag & drop으로 중앙에 배치하고, 더블클릭하여 text에 Press Any Button, id에 textView1을 입력하고 [Enter] 버튼을 클릭한다. (버튼 왼쪽에 나타나는 전구 모양의 아이콘은 무시한다.)

<첫 번째 프래그먼트 JAVA 클래스 생성>
14. app/java/com.example.administrator.fragmentstestapplication을 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, New/Java Class를 선택한다. (컴퓨터 설정에 따라 com.example.administrator 부분은 다를 수 있다.)
15. Create New Class 창이 나타나면, Name에 FirstFragment를 입력하고 OK 버튼을 클릭한다.
16. FirstFragment.java 소스코드를 다음과 같이 수정한다.

package com.example.administrator.fragmentstestapplication;

import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class FirstFragment extends Fragment {

    MainActivity mHostActivity;
    Button mButton1;
    Button mButton2;

    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
        View view = inflater.inflate( R.layout.first_fragment, container, false );
        mButton1 = (Button)view.findViewById( R.id.button1 );
        mButton1.setOnClickListener( new View.OnClickListener() { public void onClick( View v ) { buttonClicked(v); } } );
        mButton2 = (Button)view.findViewById( R.id.button2 );
        mButton2.setOnClickListener( new View.OnClickListener() { public void onClick( View v ) { buttonClicked(v); } } );
        return view;
    }

    public void buttonClicked( View v ) {
        if( mHostActivity == null ) { return; }
        switch ( v.getId() ) {
            case R.id.button1: { mHostActivity.setDataToSecondFragment(1); break; }
            case R.id.button2: { mHostActivity.setDataToSecondFragment(2); break; }
            default: { mHostActivity.setDataToSecondFragment(0); break; }
        }
    }

    @Override
    public void onAttach( Activity activity ) {
        super.onAttach( activity );
        mHostActivity = (MainActivity)activity;
    }
}

mHostActivity는 MainActivity를 받을 멤버 변수이다. FirstFragment가 MainActivity에 바인딩이 될 때 호출되는 onAttach()에서 복사를 하여 설정한다. 두 개의 버튼이 클릭될 때 buttonClicked() 함수가 호출되도록 설정하였으며, 이 함수에서는 MainActivity의 setDataToSecondFragment() 함수를 호출하도록 하였다. 이 때,  몇 번째 버튼이 눌렸는지에 대한 정수값이 인자로 전달된다.

<두 번째 프래그먼트 JAVA 클래스 생성>
17. app/java/com.example.administrator.fragmentstestapplication을 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, New/Java Class를 선택한다. (컴퓨터 설정에 따라 com.example.administrator 부분은 다를 수 있다.)
18. Create New Class 창이 나타나면, Name에 SecondFragment를 입력하고 OK 버튼을 클릭한다.
19. SecondFragment.java 소스코드를 다음과 같이 수정한다. SecondFragment.java의 내용은 예제 5와 동일하다.

package com.example.administrator.fragmentstestapplication;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class SecondFragment extends Fragment {

    TextView mTextView1;

    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
        View view = inflater.inflate( R.layout.second_fragment, container, false );
        mTextView1 = (TextView)view.findViewById( R.id.textView1 );
        return view;
    }

    public void setText( String text ) {
        mTextView1.setText( text );
    }
}

<메인 액티비티에 두 개의 프래그먼트 배치>
20. app/res/layout/content_main.xml을 선택한다.
21. Palette/Custom/를 클릭하면 Fragments 창이 나타나는데 FirstFragment를 선택하고 OK 버튼을 클릭한 후, 최상단 중앙에 배치한다. Rendering Problems 메시지가 나타나면 Use @layout/first_fragment를 클릭해준다.
22. Palette/Custom/를 클릭하면 Fragments 창이 나타나는데 SecondFragment를 선택하고 OK 버튼을 클릭한 후, 중앙에 배치한다. Rendering Problems 메시지가 나타나면 Use @layout/second_fragment를 클릭해준다.

<메인 액티비티 코드 수정>
23. MainActivity.java를 선택하고 다음과 같이 소스코드를 수정한다.

package com.example.administrator.fragmentstestapplication;

import android.app.FragmentManager;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {...}

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {...}

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {...}

    public void setDataToSecondFragment( int whichButton ) {
        FragmentManager fragmentManager = getFragmentManager();
        SecondFragment secondFragment = (SecondFragment)fragmentManager.findFragmentById( R.id.fragment2 );
        switch( whichButton ) {
            case 1: { secondFragment.setText( "Button1 was clicked." ); break; }
            case 2: { secondFragment.setText( "Button2 was clicked." ); break; }
            default: {  secondFragment.setText( "No buttons were clicked."); break; }
        }
    }
}

굵은 글씨가 기본 코드에서 추가한 부분이다. 앞에서 FirstFragment에서 buttonClicked() 함수가 호출되면 호스트 액티비티인 MainActivity의 setDataToSecondFragment() 함수가 호출되도록 해놓았다. 이 때, 어느 버튼이 클릭되었는지에 대한 정보가 whichButton 변수에 의해 전달된다. setDataToSecondFragment() 함수에서는 프래그먼트 매니저를 이용하여 SecondFragment를 얻고, SecondFragment의 public 함수인 setText()를 호출하여 textView1에 정보를 출력하도록 하였다.

 

답글 남기기