Home Fragment
Post
Cancel

Fragment

프래그먼트

같은 레이아웃을 여러 화면에 사용한다면 화면을 구성하는 XML 레이아웃 파일의 코드와 기능을

동작시키는 소스 코드가 중복될 수 밖에 없다.

이렇게 화면 안에 들어가는 레이아웃이 중복되는 문제를 해결 하기 위해 부분 화면을 위한 레이아웃이나

소스 코드를 한 번만 정의하고 재사용할 수 있도록 만든것 이 프래그먼트이다.

프래그먼트 사용 목적

  • 분할된 화면들을 독립적으로 구성하기 위해 사용함
  • 분할된 화면들의 상태를 관리하기 위해 사용함.

프래그먼트는 다음과 같이 동작한다.

1

왼쪽은 액티비티가 동작하는 방식인데, 액티비티는 앱 구성 요소이므로 안드로이드 시스템에서 관리한다.

즉 액티비티 매니저가 액티비티의 동작 순서나 처리 방식을 결정한다.

또한 액티비티가 시스템에서 관리되기 때문에 시스템이 이해하는 형식으로 명령이나 데이터를

만들어 보내야 하는데, 인텐트가 그 역할을 한다.

오른쪽은 프래그먼트가 동작하는 방식인데 액티비티와 유사하다.

액티비티 매니저 대신 프래그먼트 매니저가 있고, 액티비티가 시스템 역할을 한다.

그리고 프래그먼트에서는 인텐트를 사용하지 않는데 그 이유는 인텐트는 시스템에서 이해하는 객체여서

프래그먼트와 액티비티 사이에 전달하게 만드는것은 바람직하지 않기 때문이다.

대신 인텐트대신 메서드를 만들어 메서드를 호출하는 방식을 사용한다.

프래그먼트는 액티비티 위에 올라가 있어 액티비티를 전환하지 않고도

훨씬 가볍게 화면 전환 효과를 낼 수 있다.

프래그먼트 사용하기

프래그먼트도 부분 화면이므로 화면에 뷰들을 배치할 때는 XML 레이아웃으로 만든다.

그 다음에는 프래그먼트를 위한 자바 소스를 만들어주고 Fragment 클래스를 상속하여 만들 수 있다.

클래스까지 만들었으면 XML 레이아웃 파일의 내용을 Java 파일과 매칭하는 과정이 필요하다.

그런데 프래그먼트에는 setContentView 메서드가 없고 대신

LayoutInflater를 사용해 인플레이션을 진행해야 한다.

XML 레이아웃 파일의 내용을 인플레이션 한 후 클래스에서 사용하도록 하는 코드는 onCreateView 메서드 안에

들어간다. 이 메서드는 콜백 메서드로 인플레이션이 필요한 시점에 자동으로 호출 된다.

프래그먼트 파일을 생성해준다

1

이름을 설정한다

2

최상위 레이아웃을 LinearLayout(vertical)으로 바꾸고 텍스트 뷰 삭제한다.

텍스트 뷰와 버튼을 추가한 후 MainFragment.java 파일에서 onCreateView 메서드만 남겨 놓는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MainFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        /*이 메서드의 매개변수에 LayoutInflater 객체가 전달되므로 inflate 메서드를 바로 호출 가능
          inflate 메서드의 첫 번째 매개변수에는 XML 레이아웃 파일이되고
          두 번째 매개 변수는 이 XML 레이아웃이 설정 될 뷰 그룹 객체가 된다.
          즉 container가 이 프래그먼트의 가장 상위 레이아웃이다.*/

        return inflater.inflate(R.layout.fragment_main, container, false);
        // 뷰 그룹 객체 반환 
    }
}


그 후 activity_main.xml 파일을 열과 다음과 같이 코드를 수정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>  
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  xmlns:app="http://schemas.android.com/apk/res-auto"  
  xmlns:tools="http://schemas.android.com/tools"  
  android:id="@+id/container"  
  android:layout_width="match_parent"  
  android:layout_height="match_parent"  
  tools:context=".MainActivity">  
  
 <fragment  
  android:id="@+id/mainFragment"  
  android:name="org.techtown.samplefragment.MainFragment"  
  android:layout_width="match_parent"  
  android:layout_height="match_parent"/>  
  
</FrameLayout>

프래그먼트는 뷰와 달라서 뷰를 담고 있는 공간만 확보한다.

따라서 태그 이름으로 프래그먼트의 이름을 사용할 수 없으며, name 속성에 새로 만든

MainFragment의 이름을 설정한다. 프래그 먼트의 이름을 설정할 때는

패키지 이름을 포함한 이름으로 설정한다.

결과

3

이렇게 하면 activity_main.xml 위에 fragment_main.xml 파일이 위에 올라가게 된다.

위 과정을 세 줄 요약하면 아래와 같다.

1. 프래그 먼트를 위한 XML 레이아웃 파일 만들기

2. 프래그먼트 클래스 만들기

3. 프래그먼트를 액티비티의 XML 레이아웃에 추가하기

프래그먼트 메소드 정리

1
2
3
4
5
6
7
8
public final Activity getActivity()
 ->  프래그먼트를 포함하는 액티비티를 반환함
public final FragmentManager getFragmentManager()
 ->  프래그먼트를 포함하는 액티비티에서 프래그먼트 객체들과 관련된 프래그먼트 매니저를 반환함.
public final Fragment getParentFragment()
 ->  프래그먼트를 포함하는 부모가 프래그먼트일 경우 리턴함. 액티비티이면 null을 반환.
public final int getId()
 ->  프래그먼트 ID를 반환함.
1
2
3
4
5
6
7
8
9
public abstract FragmentTransaction()
 -> 프래그먼트를 변경하기 위한 트랜잭션을 시작함.
public abstract Fragment findFragmentById (int id)
 -> ID를 이용해 프래그먼트 객체를 찾음.
public abstract Fragment findFragmentByTag (String tag)
 -> 태그 정보를 사용해 프래그먼트 객체를 찾음.
public abstract boolean executePendingTransactions()
 -> 트랜잭션은 commit 메서드를 호출하면 실행되지만 비동기(asynchronous) 방식으로
    실행되므로 즉시 실행하고 싶다면  메서드를 추가로 호출함.

메뉴 프래그먼트 만들고 화면 전환 예시

위와 같이 메뉴 프래그먼트 파일을 생성해주고 아래와 같이 코드를 수정해준다.

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class MainActivity extends AppCompatActivity {
    MainFragment mainFragment;
    MenuFragment menuFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mainFragment  = (MainFragment) getSupportFragmentManager().findFragmentById(R.id.mainFragment);
        menuFragment = new MenuFragment();
     }

     public void onFragmentChanged(int idx){
        if (idx ==0 )
            getSupportFragmentManager().beginTransaction().replace(R.id.container, menuFragment).commit();
        else if (idx == 1)
            getSupportFragmentManager().beginTransaction().replace(R.id.container,mainFragment).commit();

     }
}



MainFragment.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_main,
                container,false);

        Button button = rootView.findViewById(R.id.button);
        button.setOnClickListener((v)->{
            MainActivity activity = (MainActivity) getActivity();
            activity.onFragmentChanged(0);
        });

        return rootView;
        // 뷰 그룹 객체 반환
    }
}



MenuFragment.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MenuFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_menu,
                container,false);

        Button button1 = rootView.findViewById(R.id.button1);
        button1.setOnClickListener((v)->{
            MainActivity activity = (MainActivity) getActivity();
            activity.onFragmentChanged(1);
        });


        return rootView;
    }
}



액티비티와 프래그먼트가 의사소통하는 방식 1


결과

ezgif com-gif-maker



프래그먼트의 생명주기

프래그먼트는 액티비티를 본떠 만들어서 액티비티처럼 독립적으로 동작하도록 하는 생명주기 메서드가 있다.

프래그먼트를 사용하는 목적 중의 하나가 분할된 화면들의 상태를 관리하는 것인데

이것을 가능하게 하는 것이 생명주기 메서드이다.

즉, 액티비티 안에 들어있는 프래그먼트도 필요할 때 화면에 보이거나 보이지 않게 되므로

액티비티 처럼 각각의 상태가 관리되는 것이 필요하다.

한 가지 주의 해야할 점은 프래그먼트는 액티비티 안에 추가되어 사용되면서 동시에 액티비티에

종속되어 있어 프래그먼트와 액티비티가 연결되어야 초기화 될 수 있다.

이 때문에 new 연산자를 사용해 프래그먼트 객체를 만드는 시점과 onCreate 메서드가 호출되는

시점이 달라진다. onAttach 메서드가 호출될 때 파라미터로 전달되는 액티비티 객체 위에

프래그먼트가 올라가 있게 된다.

즉, 액티비티를 위해 설정해야 하는 정보들은 onAttach 메서드에서 처리해야함



화면에 보이기 전에 호출되는 상태 메서드

메서드설명
onAttach(Activity)프래그먼트가 액티비티와 연결될 때 호출됨
onCreate(Bundle)프래그먼트가 초기화 될 때 호출된다.
주의! new 연산자를 이용해 새로운 프래그먼트 객체를 만드는 시점이 아님!
onCreateView(LayoutInflator,ViewGroup, Bundle)프래그먼트와 관련되는 뷰 계층을 만들어 리턴함.
onActivityCreated(Bundle)프래그먼트와 연결된 액티비티가 onCreate 메서드의
작업을 완료 했을 때 호출됨.
onStart()프래그먼트와 연결된 액티비티가 onStart되어 사용자에게 프래그먼트가 보일 때 호출됨
onResume()프래그먼트와 연결된 액티비티가 onResume되어 사용자와 상호작용할 수 있을 때 호출됨



중지되면서 호출되는 메서드

메서드설명
onPause()프래그먼트와 연결된 액티비티가 onPause되어 사용자와 상호작용을 중지할 때 호출됨.
onStop프래그먼트와 연결된 액티비티가 onStop되어 화면에서 더 이상 보이지 않을 때나
프래그먼트의 기능이 중지되었을 때 호출됨
onDestoryView()프래그먼트와 관련된 뷰 리소스를 해체할 수 있도록 호출됨
onDestory()프래그먼트의 상태를 마지막으로 정리할 수 있도록 호출됨
onDetach()프래그먼트가 액티비티와 연결을 끊기 바로 전에 호출됨



프래그먼트 수명 주기

1

onAttach와 onDetach는 프래그먼트가 액티비티 위에 올라갈 때와 떨어져 나올 때 호출된다.

마지막으로 프래그먼트 객체가 new 연산자로 만들어 졌더라도 액티비티 위에 올라가기 전까지

동작하지 않는 점을 기억하자.

This post is licensed under CC BY 4.0 by the author.