관리 메뉴

막내의 막무가내 프로그래밍 & 일상

[안드로이드] 프래그먼트(fragment) 예제 + FrameLayout 본문

안드로이드/자바 & Previous

[안드로이드] 프래그먼트(fragment) 예제 + FrameLayout

막무가내막내 2019. 2. 9. 03:45
728x90

 

 

[2021-04-03 업데이트]

 

출처 및 참고:

https://www.edwith.org/boostcourse-android/lecture/17074/

 

부스트코스 소개 : 부스트코스

부스트코스는 혼자일 때보다 더 쉽게 더 많은 것을 배울 수 있는 공간, 선배 개발자가 재능을 나누고 더 많은 이와의 소통을 경험할 수 있는 커뮤니티를 만들어가고자 합니다. 이를 위해 2020년 12

www.boostcourse.org

 

 

 
 
 
 
 
 

프래그먼트

Fragment는 동작 또는 Activity 내에서 사용자 인터페이스의 일부를 나타냅니다. 여러 개의 프래그먼트를 하나의 액티비티에 조합하여 창이 여러 개인 UI를 구축할 수 있으며, 하나의 프래그먼트를 여러 액티비티에서 재사용할 수 있습니다. 프래그먼트는 자체 수명 주기를 가지고, 자체 입력 이벤트를 받으며, 액티비티 실행 중에 추가 및 제거가 가능한 액티비티의 모듈식 섹션이라고 생각하면 됩니다(다른 액티비티에 재사용할 수 있는 "하위 액티비티"와 같은 개념).

출처: https://developer.android.com/guide/components/fragments?hl=ko

 

프래그먼트  |  Android 개발자  |  Android Developers

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section

developer.android.com

 

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

 

안드로이드를 처음 배울 때 화면을 액티비티로 여러개 만들고 액티비티끼리 전환해서 화면을 이동했다. 

하지만 하나의 화면을 일부분의 부분화면으로 구성하고 그 부분화면 별로 UI나 동작을 조작하게 구성하고 싶으면 어떻게해야 할까..?

그래서 나온게 프래그먼트이다. 프래그먼트는 하나의 액티비티 화면을 여러개의 프래그먼트로 나누어 부분화면 단위로 보여주거나 부분화면별로 조작하고 싶을때 사용할 수 있다. 이 밖에도 프래그먼트는 여러가지 장점을 지니고 있다. 

 

먼저 간단하게 프래그먼트의 장점, 사용목적, 특징을 정리해봤다.

 

1. 분할된 화면(프래그먼트)들을 독립적으로 구성하기 위해 사용된다.

 

2. 분할된 화면들의 상태를 관리하기 위해 사용된다. 액티비티처럼 프래그먼트도 수명주기를 갖고있기 떄문이다.

 

3. 액티비티나 프래그먼트나 똑같이 xml 레이아웃으로 만들기는 하지만 액티비티에 사용되면 안드로이드 시스템에서 관리하는 화면이 되지만 프래그먼트에서 사용되면 단순히 액티비티 위에 올라가는 화면의 일부 , 즉 '부분화면'이 된다. 

 

4.액티비티로도 프래그먼트 처럼 부분화면을 만들 수 는 있다고하나 액티비티는 하나의 화면을 독립적으로 구성할 때 필요한 여러가지 속성들을 사용하여 안드로이드 시스템에서 관리하는 애플리케이션 구성 요소여서 액티비티 안에 다른 액티비티를 넣는 것은 단말의 리소스를 많이 사용하여 비효율적인 방법이다.

 

5. 액티비티는 4대 컴포넌트 중 하나이며 드로이드 시스템에서 관리한다.  그리고 이 관리하는 시스템의 모듈을 액티비티 매니저라 부른다. 그리고 이렇게 액티비티가 시스템에서 관리되기 떄문에 시스템이 이해하는 형식으로 명령이나 데이터를 만들어서 보내는데 인텐트가 그 역할을 담당한다. 

 

하지만 프래그먼트는 동작방식이 액티비티와 유사하지만 액티비티에서 안드로이드 시스템이 하던 역할을 액티비티가 하게되고 액티비티 매니저 시스템 모듈대신에 프래그먼트 매니저라는 것이 프래그먼트들을 관리하게된다.

 

마지막으로 액티비티에서는 인텐트를 사용할 수 있지만 인텐트는 시스템이 이해하는 객체이기 때문에 시스템이아닌 액티비티단에서 관리하는 액티비티와 프래그먼트에서는 사용할 수 없다.  따라서 액티비티와 프래그먼트 간에 데이터를 젆달할 때는 단순히 메소드를 만들고 메소드를 호출하는 방식을 사용한다. (인터페이스를 사용하면 관리하기 더 좋을 것이다.)

 

6. 프래그먼트도 앞서 말한것처럼 액티비티를 본떠 만든것이기 때문에 생명주기가 있다.

 

7. 프래그먼트는 부분화면말고도 전체화면으로도 사용할 수 있다. (예를들어 액비비티에 뷰를 프래그먼트로만 해놓으면 된다.) 화면전환은 당연히 부분화면을 프래그먼트로 했을때처럼 동일하게 해주면된다.

 

8. 부분화면이라는 말을 많이 했는데 카카오톡이 그 예시이다. 위에 탭화면은 그대로 있고 부분적인 화면만 전환되면 된다. 프래그먼트를 사용하는 대표적인 예로 볼 수 있다.

 

 

 

 

요약하면 부분화면을 독립적으로 모듈화해서 사용할 수 있는 프래그먼트(fragment)를 사용하는것이 좋은점이 많다고 한다. 액티비티는 그대로 있고 프래그먼트만 전환되어 시스템이 직접 관리하지않기 때문에 메모리 리소스도 덜 잡아먹고 가볍기 때문이다. 

그래서 하나의 액티비티에 모든 화면을 프래그먼트로 만드는 SPA(Single Page Application) 또는 싱글액티비티 디자인이라는 개발방식도 있다. 액티비티로만 개발하는거에 편해져있는데 프래그먼트를 사용도 많이 연습해야할 것 같다.

 

 

 

 

밑은 프래그먼트를 사용하여 프래그먼트끼리 화면을 전환하는 예제소스를 짜서 공부한것을 정리해봤다.

 

메인액티비티에서 누른 버튼에 해당하는 프래그먼트를 화면에 띄워주고 띄어진 프래그먼트의 버튼을 누르면 다른 프래그먼트가 띄워지도록하는 로직이다.

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

 

소스코드:

 

메인액티비티 소스코드이다.(밑에 있는 MainFragment와 MenuFragment 소스코드 부터 참고바랍니다.) 

 

앞서 생성한 프래그먼트 2개를 생성하고 메인액티비티의 2개의 버튼을 각각 클릭하면 해당 프래그먼트가 화면에 나타나도록 만들었다.

 

우선 액티비티에서 프래그먼트를 불러올려면 프래그먼트 매니저를 사용해야 하고 프래그먼트 매니저가 관리해준다.  그래서 밑 코드와 같이 getSupportFragmentManger()를 사용하는 것을 볼 수 있다.

 

그리고 FrameLayout이라는 레이아웃을 사용해서 이 안에 프래그먼트가 화면에 띄워질 수 있다. 한마디로 FrameLayout이 Fragment 를 담을 레이아웃 컨테이너라고 생각하면 된다. FrameLayout은 Fragment의 컨테이너로써 자주 사용된다. 

 

FrameLayout(프레임레이아웃)을 간략히 설명하자면, 이 FrameLayout안에 여러화면을 겹쳐 넣을 수 있게 한다. 여러 화면이 겹치면 스택구조 또는 자식 뷰 구조 관계가 생길 수 있다. 또한 여러 뷰들이 프레임 레이아웃이 스택구조로 겹쳐있는거에 layout_gravity옵션을 주어 뭐가 스택맨위로(현재보이고있는 레이아웃) 올라오게 할지 자식뷰들의 우선순위를 지정해서 FrameLayout내의 위치제어도 할 수 있다, 이것외에도 여러가지 기능이 있다.

 

좀 더 쉽게 비유해서 말하면 Frame은 한국말로 액자(틀)인데 액자라는 틀이 있으면 그 액자에 사진을 한장만 넣을 수 도 있지만 사진을 여러개 중첩해서 스택구조로 끼워넣어 볼수도 있을 것이다. 그리그 맨위의 사진이 사라지면 그 밑에 깔려있는 사진이 보여지게되고 그럴 것이다. 이렇게 액자를 비유로 들면 쉽게 이해가 간다. 액자=프레임레이아웃, 프래그먼트 = 사진

 

onFragmentChange()는 지금 화면에 올라온 프래그먼트가 아니라 다른 프래그먼트를 띄우도록 하는 메소드인데 MainFragment에서 사용을 할 것이므로 뒤에서 보면된다.

public class MainActivity extends AppCompatActivity {

    //프래그먼트는  xml레이아웃 파일 하나랑 자바소스 파일 하나로 정의할 수 있다.
    //이게 하나의 뷰처럼 쓸 수 있는데 뷰하고 약간 다른특성들이 있다.
    //엑티비티를 본떠 만들었기 떄문에 프래그먼트 매니저가 소스코드에서 담당한다.
    MainFragment fragment1;
    MenuFragment fragment2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //프래그먼트는 뷰와 다르게 context를 매개변수로 넣어줄 필요가 없다.
        fragment1 = new MainFragment();
        fragment2 = new MenuFragment();

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

                //프래그먼트 추가하거나 할떄는 여러개 명령을 한꺼번에 쓸 수 있으므로
                //beginTransaction을 사용함
                //fragment1를 R.id.container에 넣어달라(add 또는 replace, replace는 기존에있던걸 대체해줌)
                //트랜잭션안에서 수행되는것이므로 마지막에 꼭 commit을 해줘야 실행이된다.
                getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit();/*프래그먼트 매니저가 프래그먼트를 담당한다!*/
                /*getSupportFragmentManager().beginTransaction().add(R.id.container, fragment1).commit();*/

            }
        });

        Button button3 = findViewById(R.id.button3);
        button3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment2).commit();/*프래그먼트 매니저가 프래그먼트를 담당한다!*/
            }
        });
    }

    //프래그먼트와 프래그먼트끼리 직접접근을하지않는다. 프래그먼트와 엑티비티가 접근함
    public void onFragmentChange(int index){
        if(index == 0 ){
            getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment1).commit();
        }else if(index == 1){
            getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment2).commit();
        }
    }
}

 

 

 

위 MainFragment 를 살펴보겠다. 우선 Fragment 기능을 하기 위해 Fragment를 상속해야한다.

 

onAttach()에서 프래그먼트가 엑티비티에 올라온 순간이며 프래그먼트를 부른 엑티비티를 참조해본다.

 

 

onCreateView()는 xml레이아웃인 fragment_main을 레이아웃 인플레이터를 이용해서 인플레이션해준다. (xml파일을 소스코드에서 사용할 수 있게 해준다는 말이다.) 그다음 xml을 참조해 사용할수 있으므로 버튼에 메인액티비티의 메소드를 호출하도록 설정한다. 

 

onCreatView()는 즉 매인엑티비티의 onCreate처럼 사용해주면 된다.(xml로 짜여진 뷰들을 소스코드에 연동하고 사용)

추가설명: Fragment가 UI를 처음으로 그리고자할 때에 호출된다. Fragment를 통해 UI를 그리고자 한다면 이 함수의 결과로 Fragment layout의 루트에 해당하는 View를 리턴해야한다. 만약 null을 리턴한다면 Fragment는 UI를 제공하지 않는다.

출처:  https://unikys.tistory.com/318

 

여기서 중요한 점은 프래그먼트 끼리는 서로 직접접근 직접교류가 불가하기 때문에 메인액티비티를 매개로 프래그먼트 화면 전환하는것을 구현해야한다.(MainFragment버튼동작->액티비티메소드호출->MenuFragment불러옴)

 

즉, MainFragment가 액티비티에 띄워지고 MainFragment 프래그먼트의 버튼이 눌러지면 메인액티비티의 onFrameChange메소드가 호출되어 MenuFragment가 메인액티비티에 불러와져서 띄어지게되는 방식이다.

 

//프래그먼트는 액티비티위에 올라가있을떄만 프래그먼트로서 동작할 수 있다.
public class MainFragment extends Fragment {

    MainActivity activity;
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
//이 메소드가 호출될떄는 프래그먼트가 엑티비티위에 올라와있는거니깐 getActivity메소드로 엑티비티참조가능
        activity = (MainActivity) getActivity();
    }

    @Override
    public void onDetach() {
        super.onDetach();
//이제 더이상 엑티비티 참초가안됨
        activity = null;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//프래그먼트 메인을 인플레이트해주고 컨테이너에 붙여달라는 뜻임
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_main , container, false);
        Button button = rootView.findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                activity.onFragmentChange(1);
            }
        });
        return rootView;
    }
}

 

 

MenuFragment이다. (위와 동일하나 버튼기능과 life cycle메소드는 생략했다.)

public class MenuFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //프래그먼트 메뉴를 인플레이트해주고 컨테이너에 붙여달라는 뜻임
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_menu, container, false);
        return rootView;
    }
}

 

 

 

activity_main.xml레이아웃

 

주석부분은 프래그먼트를 xml코드를 이용해서 메인엑티비티 레이아웃에 담은건데 저렇게 해도된다.(fragment태그를 만들고 name속성으로 프래그먼트를 참조하는것이다.) 하지만 여기서는 java소스파일에서 프래그먼트를 적용하는 방법을 사용해봤다.

그림에서는 보이지 않겠지만 container라는 아이디로 FramLayout이 버튼두개 밑에 들어가 있다.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

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

        <!--<fragment
            android:id="@+id/MainFragment"
            android:name="com.example.a82107.myfragment1.MainFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />-->
    </FrameLayout>

    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentStart="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginStart="98dp"
        android:layout_marginLeft="98dp"
        android:text="메뉴" />

</RelativeLayout>

 

 

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_blue_bright"
    android:orientation="vertical">

    <TextView
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="프래그먼트1"
        android:textSize="45dp" />

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

</LinearLayout>

 

fragment_menu.xml 레이아웃

:코드에 버튼기능을 생략했으므로 버튼은 아무동작을 하지 않는다.(만약 넣고싶다면 MainFragment처럼 onCreate에 전환하는 메소드를 작성하면된다. 동일 작업이므로 그냥 생략했다.)

 

<?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_purple"
    android:orientation="vertical">

    <TextView
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="프래그먼트2"
        android:textSize="45dp" />

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

</LinearLayout>

 

 

 

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

결과

첫화면->메뉴버튼->메인버튼->메인화면으로버튼

 

액티비티단의 메뉴버튼클릭시

 

액티비티단의 메인버튼클릭시

 

메인프래그먼트(프래그먼트1)단의 메인화면으로 버튼 클릭시

 

 

 

 

댓글과 공감은 큰 힘이 됩니다. 감사합니다. !!

 

 

728x90
Comments