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

서로 다른 fragment들 끼리는 직접적으로 정보를 주고 받을 수 없다. Fragment끼리의 통신은 반드시 host activity(프래그먼트들이 바인딩되어 있는 activity)를 거쳐서 이루어져야 한다.

본 예제에서는 하나의 main activity와 두 개의 fragments를 생성할 것이다. 첫 번째 fragment에는 두 개의 Buttons를, 두 번째 fragment에는 하나의 TextView를 만들 것이다. 첫 번째 fragment의 버튼 중 하나를 클릭하면, main activity에서 버튼 클릭으로 발생한 이벤트를 처리하여 두 번째 fragment로 또 다른 정보를 보내줄 것이다.

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

안드로이드 스튜디오를 실행한다.

“Start a new Android Studio project”를 선택한다.FragmentCommunicateTest01

“Application name”에 “FragmentCommunicateTestApplication”을 입력하고 Next 버튼을 클릭한다.FragmentCommunicateTest02

“Phone and Table”, “Minimum SDK = API 22″를 선택하고 Next 버튼을 클릭한다.FragmentCommunicateTest03

“Blank Activity”를 선택하고 Next 버튼을 클릭한다.FragmentCommunicateTest04

기본 내용을그대로 유지하고 Finish 버튼을 클릭한다.FragmentCommunicateTest05

다음과 같은 모습의 안드로이드 스튜디오를 볼 수 있다. (content_main.xml이 선택된 상태)FragmentCommunicateTest06

“Hello World!”가 적혀있는 텍스트 뷰를 선택하고 삭제한다.FragmentCommunicateTest07FragmentCommunicateTest08

이제 첫 번째 프래그먼트의 UI를 생성할 차례이다. 프로젝트 도구 창에서 app->res->layout을 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, New->Layout resource file을 선택한다.FragmentCommunicateTest09

대화상자가 나타나면 File name=”first_fragment”, Root element=”RelativeLayout”을 입력하고 OK 버튼을 클릭한다. (File name은 반드시 모두 소문자이어야 한다.)FragmentCommunicateTest10

생성된 first_fragment.xml을 선택한다.FragmentCommunicateTest12

Widget 중에서 Button을 drag & drop으로 왼쪽 상단에 위치시킨다.FragmentCommunicateTest13

생성된 버튼을 더블클릭하여 text=”Button1″, id=”button1″을 입력한다.FragmentCommunicateTest14

본 예제에서는 버튼 왼쪽에 나타난 전구 아이콘(텍스트의 리소스화)은 무시한다.FragmentCommunicateTest15

마찬가지로 Widget 중에서 Button을 drag & drop으로 오른쪽 상단에 위치시킨다.FragmentCommunicateTest16

생성된 버튼을 더블클릭하여 text=”Button2″, id=”button2″를 입력한다.FragmentCommunicateTest17

본 예제에서는 버튼 왼쪽에 나타난 전구 아이콘(텍스트의 리소스화)은 무시한다.FragmentCommunicateTest18

이제 두 번째 프래그먼트의 UI를 생성할 차례이다. 프로젝트 도구 창에서 app->res->layout을 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, New->Layout resource file을 선택한다.FragmentCommunicateTest19

대화상자가 나타나면 File name=”second_fragment”, Root element=”RelativeLayout”을 입력하고 OK 버튼을 클릭한다. (File name은 반드시 모두 소문자이어야 한다.)FragmentCommunicateTest20

생성된 second_fragment.xml을 선택한다.FragmentCommunicateTest21

Widget 중에서 Large Text를 drag & drop으로 중앙에 위치시킨다.FragmentCommunicateTest22

생성된 텍스트를 더블클릭하여 text=”Press Any Button”, id=”textView1″을 입력한다.FragmentCommunicateTest23

본 예제에서는 텍스트왼쪽에 나타난 전구 아이콘(텍스트의 리소스화)은 무시한다.FragmentCommunicateTest24

이제 각 프래그먼트의 UI와 연동되는 JAVA 클래스를 생성할 차례이다.

우선 첫 번째 프래그먼트의 JAVA 클래스를 생성해보자. app->java->com.example.administrator를 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, Java Class를 선택한다.FragmentCommunicateTest25

대화상자가 나타나면 Name=”FirstFragment”를 입력하고 OK 버튼을 클릭한다. (JAVA 클래스 이름은 대문자로 시작하는 것이 관례이다.)FragmentCommunicateTest26

다음과 같이 FirstFragment.java의 기본 코드가 나타난다.FragmentCommunicateTest27

소스코드를 다음과 같이 수정한다.FragmentCommunicateTest28

package com.example.administrator.fragmentcommunicateapplication;

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

public class FirstFragment extends Fragment {
    
    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
        View view = inflater.inflate( R.layout.first_fragment, container, false );
        return;
    }
}

인플레이트(inflate)를 한다는 것은 동작 가능한 view 객체로 생성한다는 의미이다. 여기서는 첫 번째 프래그먼트의 layout id인 first_fragment를 이용하여 JAVA 클래스와 UI를 연동시키도록 하였다.

이제 두 번째 프래그먼트의 JAVA 클래스를 생성해보자. app->java->com.example.administrator를 마우스 오른쪽 버튼(Mac에서는 control+click)으로 클릭한 후, Java Class를 선택한다.FragmentCommunicateTest29

대화상자가 나타나면 Name=”SecondFragment”를 입력하고 OK 버튼을 클릭한다. (JAVA 클래스 이름은 대문자로 시작하는 것이 관례이다.)FragmentCommunicateTest30

소스코드를 다음과 같이 수정한다.FragmentCommunicateTest31

package com.example.administrator.fragmentcommunicateapplication;

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

public class SecondFragment extends Fragment {
    
    @Override
    public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState ) {
        View view = inflater.inflate( R.layout.second_fragment, container, false );
        return view;
    }
}

첫 번째 프래그먼트와 마찬가지로, 두 번째 프래그먼트의 layout id인 second_fragment를 이용하여 JAVA 클래스와 UI를 연동시키고 인플레이트(inflate)하였다.

이제 생성한 두 개의 프래그먼트를 액티비티에 배치해보자.

“context_main.xml”을 선택한다.FragmentCommunicateTest32

Custom 중에서 <fragment>를 클릭한다. 다음과 같은 대화상자가 나타나면 FirstFragment를 선택한다.FragmentCommunicateTest33

그리고, 최상단 중앙에 배치하고, Rendering Problems 메시지가 나오면 Use @layout/first_fragment를 선택한다.FragmentCommunicateTest34

다시, Custom 중에서 <fragment>를 클릭한다. 다음과 같은 대화상자가 나타나면 SecondFragment를 선택한다.FragmentCommunicateTest35

그리고, 중앙에 배치하고, Rendering Problems 메시지가 나오면 Use @layout/second_fragment를 선택한다.FragmentCommunicateTest36

다음과 같이 main activity 상단에 첫 번재 fragment가, 그 아래 중앙에 두 번째 fragment가 배치된 것을 확인할 수 있다.FragmentCommunicateTest37

다음과 같이 FirstFragment.java를 수정한다.FragmentCommunicateTest38

package com.example.administrator.fragmentcommunicateapplication;

import android.app.Activity;
import android.app.Fragment;
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 {

    Button mButton1;
    Button mButton2;

    public interface CustomOnClickListener {
        public void onClicked( View v );
    }

    private CustomOnClickListener customOnClickListener;

    @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 );
        mButton2 = (Button)view.findViewById( R.id.button2 );
        mButton1.setOnClickListener( new View.OnClickListener() { public void onClick( View v ) { buttonClicked( v ); } } );
        mButton2.setOnClickListener( new View.OnClickListener() { public void onClick( View v ) { buttonClicked( v ); } } );
        return view;
    }

    public void buttonClicked( View v ) {
        customOnClickListener.onClicked(v);
    }

    @Override
    @Deprecated
    public void onAttach( Activity activity ) {
        super.onAttach( activity );
        customOnClickListener = (CustomOnClickListener)activity;
    }
}

두 개의 버튼에 대한 멤버 변수인 mButton1과 mButton2를 선언하고, onCreateView() 함수에서 각 버튼의 id인 button1과 button2를 이용하여 버튼의 UI와 JAVA 클래스를 연결하였다. 그리고, 두 개의 버튼이 클릭되었을 때 buttonClicked() 함수가 호출되도록 setOnClickListener() 함수를 이용하여 이벤트 리스너를 설정하였다. customOnClickListener는 CustomOnClickListener의 객체이다. CustomOnClickListener는 FirstFragment 클래스 내에서 정의하는 인터페이스(interface)로서, FirstFragment 클래스에서 호스트 액티비티로 데이터를 전달하는 목적으로 사용될 리스너이다. CustomOnClickListener의 이름은 임의로 정한 것이며 다른 이름으로 정의하여 사용해도 무방하다. 인터페이스 내에 선언한 onClicked() 함수 또한 임의로 정한 함수이며 편의에 따라 다른 이름과 형식으로 정의하여 사용해도 된다. 프래그먼트가 액티비티에 바인딩될 때 호출되는 onAttach() 함수에서 액티비티로 데이터를 전달할 커스텀 리스너를 연결해주었다. onAttach(Activity) 함수는 deprecated되었으며, 새로운 형식은 onAttach(Context)이다. 하지만, 당분간은 기존형을 사용해도 무방하다고 한다. 어쨌든, 버튼이 클릭되어 호출되는 buttonClicked() 함수에서는 FirstFragment 클래스 내에서 선언한 customOnClickListener 인터페이스의 onClicked() 함수를 호출하게 된다. onClicked() 함수에 대한 정의는 뒤에서 MainActivity.java 부분에 구현할 예정이다.

다음과 같이 SecondFragment.java를 수정한다.FragmentCommunicateTest39

package com.example.administrator.fragmentcommunicateapplication;

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 );
    }
}

한 개의 텍스트뷰에 대한 멤버 변수인 mTextView1을 선언하고 텍스트뷰의 id인 textView1을 이용하여 UI와 JAVA 클래스를 연결해주었다. 그리고, MainActivity.java에서 호출할 함수인 setText() 함수도 선언하고 구현해주었다. 호스트 액티비티에서 setText() 함수를 이용하여 String을 넘겨주면 텍스트뷰의 나타나는 문장이 변하게 된다.

다음과 같이 MainActivity.java를 수정한다.FragmentCommunicateTest40

package com.example.administrator.fragmentcommunicateapplication;

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 implements FirstFragment.CustomOnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClicked( View v )  {
        FragmentManager fragmentManager = getFragmentManager();
        SecondFragment secondFragment = (SecondFragment)fragmentManager.findFragmentById( R.id.fragment2 );
        switch( v.getId() ) {
            case R.id.button1: { secondFragment.setText( "Button1 was clicked." ); break; }
            case R.id.button2: { secondFragment.setText( "Button2 was clicked." ); break; }
        }
    }
}

호스트 액티비티인 MainActivity 클래스를 첫 번째 프래그먼트 내에서 선언한 CustomOnClickListener 인터페이스를 상속받도록 수정하였다. 그리고, 앞에서 미루어 두었던 onClicked() 함수를 구현해준다. 첫 번째 프래그먼트의 Button1이 클릭되면 “Button1 was clicked.”라는 문자열을, Button2가 클릭되면 “Button2 was clicked.”라는 문자열을 두 번째 프래그먼트로 넘겨주며, 이 때 두 번째 프래그먼트의 setText() 함수를 이용한다.

Shift+F10를 눌러서 “Device Chooser”를 띄우고, 첫 번째 프래그먼트의 Button1과 Button2를 클릭하면, 두 번째 프래그먼트의 텍스트가 변하는 것을 확인할 수 있다.FragmentCommunicateTest41

답글 남기기