티스토리 뷰

Android

10. 프래그먼트 : Fragment

알 수 없는 사용자 2018. 10. 10. 00:32


이번 포스팅에서 다룰 주제는

프래그먼트(Fragment)입니다.

프래그먼트의 경우

화면 UI를 구성할 때 일반이므로

알아두어야 합니다.


=============================================================


프래그먼트 : Fragment




1. Fragment란 무엇일까?


  Fragment의 사전적 의미는 '파편, 조각'을 의미합니다. 안드로이드 프로그래밍에서의 Fragment는 조각의 의미에 중점을 두면 될 것 같습니다.

Fragment는 쉽게 말해 화면 분할을 가능하게 해주는 것입니다. 안드로이드를 기반으로 한 핸드폰뿐만 아니라 태블릿 등 화면이 큰 제품이 생겨나면서 큰 화면에서 여러 부분으로 화면을 나누어 부분 화면 단위로 보여주기 위해 개발되었습니다.

하지만 현재는 큰 화면뿐만 아니라 일반 스마트폰에서도 Fragment를 이용해 설계하는 경우가 일반적이라고 합니다. 대표적으로 카카오톡, 페이스북과 같은 메신저에서 볼 수 있습니다.



[카카오톡 탭 버튼 사진]


카카오톡의 UI를 보면 하단에 5개의 버튼으로 나뉘어 버튼에 따라 친구목록, 대화목록, 설정 창 등으로 진입할 수 있습니다.





2. Activity - Fragment - View의 구조


프래그먼트는 액티비티와 뷰의 중간이라고 생각하면 됩니다. 기존에는 한 개의 액티비티 위에 여러 개의 뷰를 지정하여 액티비티를 꾸렸다면

액티비티 위에 프래그먼트를 올리고 프래그먼트 위에 뷰를 올리는 것입니다. 




액티비티 위에 프래그먼트를 여럿 생성하고 상황에 따라 프래그먼트를 제어하는 것입니다. 기존에 액티비티에 있던 뷰는 프래그먼트 위에 올려져 있기 때문에 뷰도 자동으로 상황에 따라 변하게 되는 것이죠. 


그럼 다시 돌아가 카카오톡의 탭으로 살펴보겠습니다. (물론 카카오톡에서 어떻게 설계했는지는 모르겠지만 프래그먼트를 이용해 구현했다 가정하겠습니다.)

액티비티 위에는 탭 버튼만 존재하고 탭 버튼이 눌릴 때 각 버튼에 맞는 프래그먼트를 불러올 수 있는 구조입니다. 물론 친구 목록이나 뉴스 정보 같은 뷰는 각 프래그먼트 위에 구현될 것입니다.


탭 버튼에 따라 화면이 바뀌기 때문에 액티비티를 전환한다고 생각할 수도 있습니다. 물론 액티비티 전환으로도 구현할 수 있겠죠.

하지만 액티비티로 모두 구현할 시 하단의 버튼 탭같은 모든 액티비티에서 동일하게 되는 부분을 따로따로 구현해줘야하고 액티비티가 전환되면서 깜빡거리는 시각적인 불편함도 생기기 마련입니다.









3. fragment의 동작 방식 / 수명주기


[참고] Do it! 안드로이드 앱 프로그래밍



fragment의 동작 방식은 activity를 본 떠 만들었습니다. 프래그먼트의 사용 목적은 분할된 화면들을 독립적으로 구성하기 위해 사용하고, 분할된 화면들의 상태를 관리하기 위해 사용합니다. 그렇기 때문에 프래그먼트를 독립적으로 다루기 위해서 액티비티의 구조를 본떠서 프래그먼트를 만들었습니다.


액티비티의 경우 액티비티를 관리하는 시스템 모듈인 액티비티 매니저를 통해 각 액티비티에서의 상호작용을 인텐트를 이용하여 동작합니다.

그와 비슷하게 프래그먼트 매니저를 통해 각 프래그만트에서의 상호작용을 하게 설계되어 있습니다.


fragment의 생명주기는 액티비티와 매우 유사합니다.



- onAttach() : 프래그먼트가 액티비티에 attach될 때 호출됩니다.

- onCreate() : 액티비티와 같습니다. 리소스들을 초기화 해주는 단계로 프래그먼트를 생성하면서 넘겨준 값들이 있다면, 변수에 넣어줍니다.

- onCreateView() : 레이아웃을 inflate 하는 메소드입니다. view 객체를 얻을 수 있어 view와 관련된 객체들을 초기화할 수 있습니다.

- onActivityCreated() : 프래그먼트의 onCreatView() 와 액티비티의 onCreate()가 호출되고 나서 호출됩니다. 액티비티와 프래그먼트의 뷰가 모두 생성된 상태로 뷰를 변경하는 작업이 가능합니다.

- onStart() : 사용자에게 프래그먼트를 띄워주는 단계입니다. (액티비티는 started 상태)

- onResume() : 사용자와 상호작용이 가능하게 되는 부분입니다.

- onPause(), onStop() : 프래그먼트의 부모 액티비티가 아닌 액티비티가 나오게 되면 onPause() 호출하여 Backstack으로 들어갑니다. 아예 다른 액티비티로 전환될 시 onStop()을 호출합니다.

- onDestroyView() : 프래그먼트의 뷰가 제거되는 단계입니다. 만약 backstack으로부터 프래그먼트가 돌아온다면 onCreateView() 메소드를 호출하게 됩니다.

- onDestroy() : 프래그먼트의 리소스들을 제거합니다.

- onDetach() : 프래그먼트가 액티비티로부터 떨어지는 단계입니다.





4. 간단한 fragment를 테스트해봅시다.


프래그먼트도 액티비티와 마찬가지로 하나의 레이아웃과 자바 소스파일로 구성됩니다. 하지만 액티비티와는 엄연히 다르기 떄문에 각자 생성하여 inflate 작업이 필요하게 되는 것입니다.


테스트는 메인 액티비티에서 2개의 버튼을 구성한 후 버튼에 따라 프래그먼트를 띄우는 작업과 프래그먼트내에 버튼을 두어 다른 프래그먼트로 넘어가는 2가지 기능을 구현하도록 하겠습니다.



① 메인 액티비티의 레이아웃과 각 프래그먼트의 xml 파일을 생성하고 디자인합니다.


1) 메인 액티비티.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메인"/>

<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="464dp"
android:layout_below="@+id/button">
</FrameLayout>

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="@+id/button"
android:text="메뉴" />

</RelativeLayout>



2) fragment_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_orange_dark"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment1"
android:textSize="40dp"/>

<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메뉴화면으로" />

</LinearLayout>



3) fragment_manu.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimaryDark"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment2"
android:textSize="40dp" />

<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메인화면으로" />

</LinearLayout>




② 각 프래그먼트에서 레이아웃과 inflate하고 기능을 정의합니다.


1) ManuFragment.java

public class ManuFragment extends Fragment {

MainActivity mainActivity;

@Override
public void onAttach(Context context) {
super.onAttach(context);
mainActivity = (MainActivity)getActivity();
}

@Override
public void onDetach() {
super.onDetach();
mainActivity = null;
}

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

ViewGroup rootview = (ViewGroup)inflater.inflate(R.layout.fragement_manu,container,false);
Button button4 = (Button)rootview.findViewById(R.id.button4);
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mainActivity.onChangeFragment(0);
}
});
return rootview;
}
}



2) MainFragment.java

public class MainFragment extends Fragment {

MainActivity mainActivity;

@Override
public void onAttach(Context context) {
super.onAttach(context);
mainActivity = (MainActivity)getActivity();
}

@Override
public void onDetach() {
super.onDetach();
mainActivity = null;
}

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

ViewGroup rootview = (ViewGroup)inflater.inflate(R.layout.fragement_main,container,false);
Button button3 = (Button)rootview.findViewById(R.id.button3);
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mainActivity.onChangeFragment(1);
}
});

return rootview;
}
}



두 프래그먼트에서 작업할 내용은 어떤 프래그먼트로 넘어갈 것이고 어떤 레이아웃을 inflate할 것인지만 다릅니다.


- onAttach() :

액티비티와 프래그먼트를 붙일 때 호출되는 메소드입니다. 사용할 액티비티는 MainActivity이기 때문에 getActivity() 메소드를 통해 액티비티를 찾아주어 버튼 클릭 시 액티비티의 메소드를 호출할 수 있도록 정의해줍니다.


- onCreateView() :

inflate를 위한 메소드입니다. 

떄문에 rootview라는 변수에 fragement_main.xml을 인플레이션을 통해 참조합니다. 이때의 rootview는 프래그먼트 안에 들어 있고 프래그먼트는 이 뷰를 보여주기 위한 틀이라고 생각하면 됩니다.

버튼 객체를 찾아낼 때도 버튼 객체는 fragement_main.xml 레이아웃 위에 있기 때문에 rootview를 통해서 버튼 객체를 찾아냅니다.


 - onChangeFragement() :

액티비티에서 프래그먼트를 바꾸기 위해 정의할 메소드입니다. 앞서 메인 액티비티를 참조할 수 있는 값을 변수에 저장해두었기 때문에 이를 이용해 메소드를 호출합니다.




③ 액티비티에서 버튼을 클릭 시 해당 프래그먼트로 넘어갈 수 있도록 정의합니다.


[MainActivity.class]

public class MainActivity extends AppCompatActivity {

MainFragment mainFragment;
ManuFragment manuFragment;

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

mainFragment = new MainFragment();
manuFragment = new ManuFragment();

Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

getSupportFragmentManager().beginTransaction().replace(R.id.container,mainFragment).commit();
}
});

Button button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getSupportFragmentManager().beginTransaction().replace(R.id.container,manuFragment).commit();
}
});
}

public void onChangeFragment(int index){
if(index == 0){
getSupportFragmentManager().beginTransaction().replace(R.id.container,mainFragment).commit();
}else if(index ==1){
getSupportFragmentManager().beginTransaction().replace(R.id.container,manuFragment).commit();
}
}
}


프래그먼트를 호출하는 기능은 몇 가지 메소드 빼고는 단순합니다.

해당 프래그먼트들을 객체로 정의하고 객체를 생성해준 다음 버튼을 클릭했을 때 해당 프래그먼트를 띄우도록 하면 끝입니다.

이때 new 연산자를 통해 프래그먼트 객체를 생성했다해서 프래그먼트로 동작하지 않는다는 것을 주의해야합니다.

실제 프래그먼트를 동작시키는 작업은 onChangeFragment() 메소드나 button2를 클릭시에 일어나게 됩니다.


 - getSupportFragmentManager() :

 FragmentManager를 반환하는 메소드입니다. 프래그먼트 매니저는 액티비티 매니저와 같은 역할을 한다고 소개하였는데 프래그먼트를 다루는 작업을 해 주는 객체로 프래그먼트를 추가, 삭제, 또는 교체 등의 작업을 할 수 있게 해줍니다. 


- beginTransaction() : 

프래그먼트 매니저는 프래그먼트를 바꿀 때 오류가 생기면 다시 원상태로 돌릴 수 있어야 하므로 트랜잭션 객체를 만들어 실행합니다. 


- replace() :

실행시킬 프래그먼트를 입력하는 메소드입니다.


- commit() :

트랜잭션을 완료시키고 프래그먼트를 띄우는 메소드입니다.




④ 출력물을 확인해 봅시다.



[메인화면] 



              

[플래그먼트 메인화면]                                                                        [플래그먼트 메뉴화면]


사진이기 때문에 플래그먼트안에 버튼을 눌렀을 시 변경되는 것을 확인할 수 없지만, 실제 작동 시 플래그먼트가 넘어가게 됩니다.


액티비티에 존재하는 상단 2개의 버튼은 액티비티 내의 기능을 이용해 프래그먼트를 변경하는 것이고, 프래그먼트 내의 버튼을 이용해 프래그먼트를 변경하는 것은

액티비티(프래그먼트 매니저)를 통해 메소드를 호출하여 프래그먼트를 변경한 것입니다.


이점을 유의해서 어떤 원리로 프래그먼트가 변경되고 기능의 어떤 차이점이 있는지 이해하면 될 것 같습니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함