[안드로이드 공식문서 파헤치기] Fragment-1편(사용이유/장점/FragmentTransaction /FragmentManager )

dada·2022년 7월 16일
7
post-thumbnail

참고 자료

Android Developer 도큐먼트 - 프래그먼트 개요

Android Developer 도큐먼트 - 프래그먼트 만들기

Android Developer 도큐먼트 - 프래그먼트 관리자(FragmentManager)

choheeis.github.io - Fragment

✅Fragment 공부해보자

안드로이드 프로젝트를 할때마다 재사용될 것 같은 뷰는 fragment로 사용하곤 했습니다. 그런데 내가 fragment를 제대로 알고 사용하는걸까? 하는 의심이 들어서 이번 기회에 fragment의 모든~~것을! 알아보도록 하겠습니다. 안드로이드 공식문서를 보면서 차근히 공부해봐욧!

✅Fragment 등장 배경

  • 뭐든 기존 기술에 문제가 있거나 개선사항이 필요할 때 새로운 기술이 나오는법이죠! Fragment는 Activity 가 가지고 있는 문제를 해결하기 위해 등장했습니다

  • Activity가 가지고 있는 문제는 아래 2가지 입니다
    1) Activity 안에 코드가 길어지게 되면 코드가 길어지니까 유지보수할 때 관리가 어려워짐
    2) 안드로이드 디바이스는 휴대폰,태블릿 등 다양하기 때문에 태블릿UI를 고려할 때 단순 Activity로 화면을 그리기에 한계가 있음

  • 2번 문제를 더 자세히 살펴봅시다 위 그림은 프래그먼트가 정의한 두 가지 UI 모듈이 태블릿 디자인에서는 하나의 액티비티로 조합될 수 있는 반면, 핸드셋 디자인에서는 분리될 수 있다는 것을 나타낸 예시입니다.

    • 그림을 예로 들어 뉴스 어플리케이션이 있다고 해봅시다! 이 애플리케이션을 태블릿 크기의 기기에서 실행하는 경우, 애플리케이션 내의 액티비티 A 안에 두 개의 프래그먼트를 포함할 수 있습니다. 그러나 휴대폰 크기의 화면은 두 프래그먼트를 모두 쓸 만큼 공간이 충분치 않습니다.
    • 따라서 액티비티 A에는 기사 목록에 해당되는 프래그먼트만 포함되고, 사용자가 기사를 하나 선택하면 액티비티 B가 시작됩니다. 여기에 기사를 읽을 두 번째 프래그먼트가 포함되어 있습니다. 따라서 이 애플리케이션은 서로 다른 조합으로 프래그먼트를 재사용함으로써 태블릿과 휴대폰 화면 모두 지원합니다

✅Fragment주요 특징들

  • 프래그먼트는 앱의 전체 UI(User Interface의 약자로, 사용자가 눈으로 볼 수 있는 화면 및 사용자와 앱이 상호작용(버튼 클릭 등)할 수 있는 부분)에서 어딘가에 반복적으로 재사용 가능한 부분을 말합니다.(Fragment의 존재 이유)

  • 프래그먼트는 자체 레이아웃(xml파일을 정의할 수 있음)을 가질 수 있으며 자체 생명 주기를 보유합니다. 또한 자체 입력 이벤트를 받으며 처리할 수 있습니다.(자체 레이아웃을 갖는 것은 선택 사항, 자체 UI가 없는 프래그먼트도 만들 수 있음)

  • 프래그먼트는 독립적으로 존재할 수 없고, 반드시 Activity나 다른 프래그먼트에 호스팅(주인 역할을 하다 / 호스팅되다 = 노예가 되다)되어야 합니다.

    • 항상 액티비티, 다른 프래그먼트 내에서 호스팅되어야 하며 해당 프래그먼트의 수명 주기는 호스트 액티비티의 수명 주기에 직접적으로 영향을 받습니다.
    • 예를 들어 액티비티가 일시정지되는 경우, 그 안의 모든 프래그먼트도 일시정지되며 액티비티가 소멸되면 모든 프래그먼트도 마찬가지로 소멸됩니다. 그러나 액티비티가 실행 중인 동안(재개됨 수명 주기 상태에 있을 경우)에는 각 프래그먼트를 추가 또는 제거하는 등 개별적으로 조작할 수 있습니다.
  • 따라서 프래그먼트의 뷰 계층 구조는 호스트 뷰 계층 구조의 일부가 됩니다.

  • Android Jetpack 라이브러리 중 Navigation, BottomNavigationView, ViewPager2 등은 프래그먼트와 호환되도록 설계되어 있어서 프래그먼트가 해당 라이브러리와 함께 자주 사용됩니다.

라이브러리 관점에서 보는 프래그먼트

  • Android Jetpack 라이브러리 중 Fragment 라이브러리가 등장하면서 Fragment가 하나의 프레임워크 또는 라이브러리화 되었습니다.

  • Fragment 라이브러리가 제공하는 클래스 중 Fragment 라는 클래스 가 있습니다.

  • Android Developer 사이트 - Fragment 클래스 공식 문서Fragment 클래스 실제 코드 가 존재합니다.

  • 01
  • Android Developer 사이트 - Fragment 클래스 공식 문서에서 Fragment가 Object 클래스를 상속하고 LifecycleOwner, ActivityResultCaller 등 여러 인터페이스를 구현하고 있는 클래스임을 확인할 수 있습니다.

  • 앞으로 언급할 모든 '프래그먼트 클래스' 라는 용어는 말 그대로 '프래그먼트 클래스'를 의미하는 것이고 '프래그먼트 자체 UI' 라는 용어로 프래그먼트의 UI를 의미할 것입니다.

프래그먼트의 재사용성

  • 앞서 프래그먼트의 특징에서 언급했듯이, 프래그먼트의 존재 이유는 UI 중에서 재사용 가능한 부분을 재사용하기 위함입니다.

  • 프래그먼트는 자체 UI를 개별적인 청크로써 사용할 수 있습니다. 개별 청크 단위로 다른 곳에서 재사용할 수 있습니다.

Activity와 프래그먼트의 서로 다른 목적성

  • Activity : 앱 전체적인 사용자 인터페이스(UI)에 포함될 요소들을 배치하는 곳입니다.

  • Fragment : 단일 화면이나 화면 일부에 관한 사용자 인터페이스(UI)를 정의하는데 적합합니다.

  • 02
  • (위 그림 참고) 하늘색 박스 부분은 Activity에 배치된 NavigationView 이고, 연두색 박스 부분은 프래그먼트 자체 UI입니다. Activity은 연두색 프래그먼트를 호스팅하고 있습니다.

프래그먼트의 장점

  • 앱의 단일 화면이나 부분 화면을 프래그먼트로 구현하면 런타임 시 UI 모습을 사용자와 상호작용하면서 실시간으로 수정할 수 있습니다.

  • 사용자가 앱을 실행하여 사용하는 도중(Activity is running)에 Activity의 모양을 수정할 수 있다는 의미입니다. 예를 들어 BottomNavigationView가 존재하는 앱에서는 사용자가 '홈 탭'을 클릭하면 홈 화면이 나오고, '마이페이지 탭'을 클릭하면 마이페이지 화면이 나옵니다. 이러한 현상이 런타임동안 사용자와 상호작용을 하면서 앱의 UI가 실시간으로 바뀌는 것이라고 할 수 있습니다.(프래그먼트 추가/교체/삭제 등의 작업이 실행됨으로써 화면이 바뀌는 것처럼 보입니다.)

  • 대신 프래그먼트를 사용하여 런타임 동안 UI를 실시간으로 바꿀 때는 호스트 Activity의 수명 주기가 STARTED 상태 이상에 있는 동안에만 가능합니다.

  • 런타임 동안 프래그먼트의 변경 사항(추가/교체/삭제)이 발생하면 FragmentManager라는 것이 관리하는 프래그먼트 백 스택(Activity 백 스택과 다름) 에 변경 사항 히스토리를 저장하여 기록할 수 있습니다.

  • 프래그먼트 백 스택에 저장된 변경 사항들에 한에서 사용자가 '뒤로' 버튼을 눌렀을 경우 '변경 사항 취소' 가 되어 '되돌리기' 작업을 진행할 수 있습니다.

프래그먼트 사용 시 주의점

  • 프래그먼트는 재사용 가능한 자체 UI를 가지기 때문에 어느 Activity에나 호스팅 될 수 있고 어느 프래그먼트에나 호스팅 될 수 있습니다.

  • 따라서 프래그먼트 클래스에는 자체 UI를 관리하는 로직만 구현해야 하고 다른 Activity나 다른 프래그먼트를 직접 조작하는 로직을 포함해서는 안됩니다.(모듈성, 재사용성을 해치게 됨)

  • 즉, 프래그먼트가 다른 Activity나 프래그먼트에 의존하면 안됩니다.

✅ 프래그먼트 클래스 만드는 방법

  • 프래그먼트 클래스를 어떻게 만들고 Activity에 호스팅 시키는지 알아봅시다!
  • 1. 환경 설정

    • 프래그먼트 클래스를 만들어보기 전, 안드로이드 프로젝트에 Jetpack Fragment 라이브러리 종속성을 추가해야 합니다.

    • Android 라이브러리는 Google Maven Repository에서 찾을 수 있으므로 build.gradle파일(프로젝트 수준)에 Google Maven 저장소를 먼저 추가해야 합니다.

      ~~~gradle
      // build.gradle(Project 수준) 파일
      buildscript {
          ...
      
          repositories {
              google()
              ...
          }
      }
      
      allprojects {
          repositories {
              google()
              ...
          }
      }
      ~~~
    • 프로젝트에 Google Maven 저장소를 추가했다면 build.gradle파일(app 수준)에 Jetpack Fragment 라이브러리 종속성을 추가하면 됩니다.

      ~~~gradle
      // build.gradle(App 수준) 파일
      dependencies {
          // 최신 버전 대입
          def fragment_version = "1.2.5"
      
          // Java language implementation(개발 언어 별 선택)
          implementation "androidx.fragment:fragment:$fragment_version"
          // Kotlin(개발 언어 별 선택)
          implementation "androidx.fragment:fragment-ktx:$fragment_version"
      }
      ~~~
  • 2. 프래그먼트 클래스 생성하기

    • 프래그먼트 클래스를 생성한다는 것은 해당 프래그먼트의 자체 UI 조작을 담당하는 클래스를 생성한다는 것입니다.

    • 프래그먼트 클래스는 위 과정에서 추가한 Jetpack Fragment 라이브러리에서 제공하는 Fragment 클래스를 상속하는 클래스를 구현함으로써 생성할 수 있습니다.

    • 03
    • Fragment 클래스의 생성자 함수는 두 가지 종류가 있는데 하나는 default 생성자 함수이고, 다른 하나는 default 생성자 함수의 대안책으로 사용할 수 있는 생성자 함수입니다.

    • 대안책으로 사용할 수 있는 생성자 함수를 사용하여 프래그먼트 클래스의 인스턴스를 생성할 경우 장점이 존재합니다. default 생성자 함수를 사용하여 프래그먼트 클래스의 인스턴스를 생성하는 경우에는 클래스 내부에 onCreateView(LayoutInflater, ViewGroup, Bundle) 메소드를 구현해야 합니다. 하지만 대안책으로 사용할 수 있는 생성자 함수를 사용하면 onCreateView() 구현을 생략할 수 있습니다.

    • 보통 onCreateView() 메소드 내에서 프래그먼트 자체 UI 레이아웃을 inflate하는 코드를 작성합니다.
* 아래 코드처럼 프래그먼트 클래스를 쉽게 생성할 수 있습니다.
    
    ~~~kotlin
    // ExampleFragment.kt 파일
    import androidx.fragment.app.Fragment

    // Default 생성자 함수 사용
    class ExampleFragment : Fragment() {
        // TODO(onCreateView 메소드 구현하기)
    }
    ~~~

    ~~~kotlin
    import androidx.fragment.app.Fragment

    // Alternate 생성자 함수 사용
    // 생성자 함수 인자로 프래그먼트 자체 UI 레이아웃이 정의된 xml 파일의 id값을 전달해야 합니다.
    class ExampleFragment : Fragment(R.layout.example_fragment) {
        
    }
    ~~~

* 이렇게 생성한 클래스 내에서 상속한 Fragment 클래스에 포함된 여러 메소드를 override 하여 해당 프래그먼트가 담당할 로직들을 구현하면 됩니다.
  • 3. Activity에 프래그먼트 호스팅 시키기

    • 프래그먼트는 독립적으로 존재할 수 없고, 반드시 Activity나 다른 프래그먼트에 호스팅되어야 합니다.

    • 프래그먼트를 Activity에 호스팅 시키려면 해당 Activity가 FragmentActivity 를 상속하는 Activity여야 합니다.

    • FragmentActivity를 상속하는 Activity여야만 해당 Activity 레이아웃 일부분에 프래그먼트 자체 UI를 배치할 수 있습니다.

    • 만약 Activity가 이미 AppCompatActivity 클래스를 상속하고 있다면 FragmentActivity를 따로 상속할 필요가 없습니다.

    • 26
    • AppCompatActivity가 이미 FragmentActivity를 상속하는 클래스이기 때문입니다.

    • Activity에 프래그먼트를 호스팅 시키는 방법은 2가지 방법이 있습니다.

    1. Activity UI 레이아웃 안에 프래그먼트 존재를 정의하여 Activity UI가 Activity 클래스에 inflate될 때 프래그먼트 자체 UI도 자동으로 프래그먼트 클래스에 inflate 시키는 방법

    2. Activity UI 레이아웃 안에 프래그먼트 컨테이너(=이 위치에 프래그먼트 자체 UI가 배치될 것입니다~ 라고 위치를 지정해두는 것)를 정의하고 프로그래밍적으로 해당 컨테이너 안에 프래그먼트를 추가(Add)하는 방법

    • 주의할 점은 두 방법 모두 Activity UI 레이아웃 안에 FragmentContainerView 를 정의함으로써 해당 프래그먼트가 배치될 위치를 정의해야 한다는 점입니다.

    • FragmentContainerViewFrameLayout에서 제공하지 않는 조작 관련 사항이 추가된 새로운 뷰입니다. 따라서 Activity UI 레이아웃 내에 프래그먼트 자체 UI 위치를 지정할 때는 FragmentContainerView를 사용하길 권장합니다.

    • (FragmentContainerView가 등장하기 전까지는 Acitivity에 프래그먼트를 호스팅할 때 FrameLayout을 많이 사용했습니다. 또한 애초에 FragmentContainerView가 FrameLayout 클래스를 상속하는 클래스입니다)

    • 첫 번째 방법으로 Activity에 프래그먼트 호스팅시키기

      <!-- activity_main.xml 파일 -->
      <androidx.constraintlayout.widget.ConstraintLayout 
          ...>
      
          <!-- FragmentContainerView 배치 -->
          <androidx.fragment.app.FragmentContainerView
              android:id="@+id/fragment_container_view"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              app:layout_constraintTop_toTopOf="parent"
              app:layout_constraintStart_toStartOf="parent"
              app:layout_constraintBottom_toBottomOf="parent"
              app:layout_constraintEnd_toEndOf="parent"
              android:name="com.chohee.fragment.ExampleFragment"/>
      
      </androidx.constraintlayout.widget.ConstraintLayout>
      • 편의상 FragmentContainerView를 한글 번역한 '프래그먼트 컨테이너'라고 부르겠습니다

      • android:name 속성을 통해 프래그먼트 컨테이너에 추가될 프래그먼트 클래스를 지정합니다. 이 속성을 지정하면 자동으로 일련의 내부 동작이 실행됩니다.

      • android:name 속성의 내부 동작은 다음과 같습니다. Activity UI 레이아웃이 inflate되는 과정에서 FragmentContainerView가 inflate될 때 자동으로 android:name 속성에 지정된 프래그먼트를 새로운 인스턴스로 생성합니다.

        인스턴스(instance)란? -> 클래스(설계도)를 바탕으로 소프트웨어 세계에 구현된 실체(=메모리에 할당)

        예 - ExampleFragment라는 클래스를 프로젝트 어딘가에 작성해놓고, MainActivity 클래스에서 val exampleFragment = ExampleFragment() 라는 코드(ExampleFragment 클래스의 생성자 함수를 사용해 인스턴스를 생성하는 코드)를 작성하면 ExampleFragment 클래스(설계도)를 바탕으로 구현된 객체가 메모리에 할당됩니다. 이것을 ExampleFragment 클래스가 인스턴스되었다 라고 합니다.

      • 지정된 프래그먼트의 인스턴스가 생성되면 프래그먼트 수명 주기 메소드 중 onInflate() 메소드가 호출됩니다.

      • onInflate() 메소드가 호출된 후, 해당 프래그먼트 자체 UI를 프래그먼트 컨테이너 위치에 추가하는 FragmentTransaction이 실행됩니다.(FragmentTransaction은 Android Jetpack Fragment 라이브러리가 제공하는 클래스입니다. 이에 관해서는 이어지는 다음 포스팅에서 다룰 예정입니다)

      • 실제로 테스트해본 결과, activity_main.xml 파일을 위 코드와 같이 구성한 후 앱을 실행하니 MainActivity에 ExampleFragment가 잘 호스팅 되었습니다.(example_fragment.xml의 배경색을 지정해 프래그먼트 영역을 구분함)

      • 04
    • 두 번째 방법

      <!-- activity_main.xml 파일 -->
      <androidx.constraintlayout.widget.ConstraintLayout
          ...>
      
          <!-- FragmentContainerView 배치 -->
          <androidx.fragment.app.FragmentContainerView
          android:id="@+id/fragment_container_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          app:layout_constraintTop_toTopOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent" />
      
      </androidx.constraintlayout.widget.ConstraintLayout>
      • android:name 속성을 지정하지 않음으로써 Activity UI 레이아웃 안에서 프래그먼트 컨테이너의 위치만 배치합니다.(자동으로 프래그먼트가 인스턴스화 되지 않음)

      • 이러한 두 번째 방법을 사용하면 Activity가 running 상태일 동안(=런타임 동안 =사용자가 앱을 사용할 동안) 프래그먼트 컨테이너 안에 프로그래밍적으로 프래그먼트를 추가/교체/삭제할 수 있습니다.

      • 프로그래밍적으로 프래그먼트를 추가/교체/삭제하려면 프래그먼트를 호스팅하는 Activity 내에서 FragmentManager 클래스를 인스턴스화해야 합니다.(FragmentManager 클래스는 Android Jetpack Fragment 라이브러리에서 제공하는 클래스이고 이에 관해서는 조금 아래에 나올 내용에서 다룰 예정입니다.)

      • FragmentManager 클래스를 인스턴스화하는 이유는 프래그먼트 추가/교체/삭제 작업을 제공하는 FragmentTransaction 클래스를 사용하기 위함입니다.

      • 27
      • (위 그림 참고) FragmentTransaction 클래스는 public 클래스가 아닌 abstract 클래스(추상 클래스)입니다. 이 추상 클래스는 FragmentManager 클래스에 구현되어 있기 때문에 FragmentTransaction 클래스를 사용하기 위해 FragmentManager 클래스를 인스턴스화 하는 것입니다.

      • 프래그먼트를 호스팅하는 Activity 내에서 FragmentManager 클래스를 인스턴스화하는 방법은 FragmentActivity 클래스에서 제공하는 getSupportFragmentManager() 함수를 호출하는 것입니다.

      • 28
      • FragmentManager 클래스도 public 클래스가 아닌 abstract 클래스(추상 클래스)입니다. 이 추상 클래스는 FragmentActivity 클래스에 구현되어 있기 때문에 getSupportFragmentManager() 함수를 호출해야 하는 것입니다.

      • FragmentManager 클래스를 인스턴스화 한 후, Activity의 onCreate() 메소드 내에서 FragmentTransaction 클래스가 제공하는 add() 함수를 호출하여 프래그먼트 컨테이너 위치에 추가하고자 할 프래그먼트 자체 UI를 추가하면 됩니다.

      • add() 함수는 오버로딩되어 여러 모양으로 존재하므로 상황에 적합한 add() 함수를 선택하여 호출하면 됩니다.

      • 05
      • (위 코드 참고) if 조건문을 통해 savedInstanceState가 null일 경우에만 FragmentManager를 인스턴스화 하여 FragmentTransaction을 생성했습니다. 이렇게 하면 MainActivity가 첫 번째로 생성된 시점에만 컨테이너에 프래그먼트 자체 UI를 추가하도록 할 수 있습니다. 만약 어떠한 이유로 MainActivity가 재생성되면 더 이상 savedInstanceState가 null이 아니기 때문에 if 조건문이 실행되지 않습니다. if 조건문을 걸어주지 않을 경우, Activity가 재생성되어 Activity의 생명 주기에 의해 onCreate() 메소드가 한 번 더 호출되는 시점에 이미 추가했던 프래그먼트를 또 추가하게 됩니다.(위 코드에서 아직 언급하지 않은 코드들을 이어지는 내용과 다음 포스팅에서 다룰 예정입니다)

      • 두 번째 방법으로 테스트해본 결과, activity_main.xml에 name 속성을 주지 않고 MainActivity의 onCreate() 메소드를 위와 같이 구현한 후 앱을 실행하니 MainActivity에 Fragment가 잘 호스팅 되었습니다.


✅ 프래그먼트 관리자(FragmentManager)

프래그먼트를 관리하는 더 좋은 방법

  • Android Jetpack Navigation 라이브러리를 사용하여 앱의 탐색 구조를 구현한다면 FragmentManager를 직접 다뤄야 할 일이 적어져 편합니다.(Google I/O 2018 소개)

  • 이 Navigation 라이브러리 내부 동작에 FragmentManager를 사용한 작업이 구현되어 있기 때문입니다.(개발자의 편의성 증가)

FragmentManger가 중요한 이유

  • Navigation 라이브러리도 결국 내부 동작에 FragmentManager를 사용할 만큼 프래그먼트를 사용하는 앱에서는 FragmentManager의 사용이 빈번합니다.

  • 그렇기 때문에 FragmentManager가 무엇인지, 어떻게 작동하는지 파악하는 것이 중요합니다.

FragmentManager 인스턴스화하기

  • '프래그먼트 관리자를 인스턴스화 한다' = FragmentManager 클래스를 바탕으로 구현된 객체를 메모리에 할당해 코드에서 사용할 수 있게 한다.

  • 런타임 시에 프래그먼트 추가/교체/삭제 작업 등을 하려면 먼저 프래그먼트 관리자 클래스를 인스턴스화 해야 합니다.(프래그먼트 관리자 클래스에 프래그먼트 추가/교체/삭제 작업을 제공하는 FragmentTransaction 이 구현되어 있기 때문이라고 위에서 언급했습니다)

  • 프래그먼트 관리자를 인스턴스화 하는 방법은 2가지 경우에 따라 다릅니다. 2가지 경우는 'Activity 클래스에서 인스턴스화하는 경우', '프래그먼트 클래스에서 인스턴스화하는 경우' 입니다.

Activity 클래스에서 프래그먼트 관리자 클래스 인스턴스화하기

  • FragmentActivity 클래스를 상속하는 모든 서브 클래스에서 getSupportFragmentManager() 함수를 호출하면 프래그먼트 관리자를 인스턴스화할 수 있습니다.(코틀린에서는 supportFragmentManager 호출)
    • Activity가 상속하는 AppCompatActivity 클래스는 FragmentActivity를 상속하기 때문에 Activity클래스에서 getSupportFragmentManager()를 호출해 프래그먼트 관리자를 얻을 수 있었던 것입니다
  • getSupportFragmentManager()FragmentActivity 클래스에서 제공하는 함수입니다.

프래그먼트 클래스에서 프래그먼트 관리자 클래스 인스턴스화하기

  • 프래그먼트는 Activity에 호스팅될 수도 있지만 프래그먼트에 호스팅될 수도 있습니다.

  • 어떤 프래그먼트가 하위 프래그먼트를 호스팅하는 경우 호스트 프래그먼트 내에서 getChildFragmentManager() 함수를 호출하여 하위 프래그먼트에 관한 프래그먼트 관리자 클래스를 인스턴스화할 수 있습니다.

  • 하위 프래그먼트에서 호스트 프래그먼트를 관리하는 프래그먼트 관리자를 인스턴스화해야 한다면 getParentFragmentManager() 함수를 호출하면 됩니다.

  • getChildFragmentManager() 함수와 getParentFragmentManager() 함수는 Fragment 클래스에서 제공하는 함수입니다.

프래그먼트와 프래그먼트 관리자 간의 관계

  • 07
  • (위 그림 참고) 연두색으로 표시된 Activity는 하늘색으로 표시된 프래그먼트의 호스트 Activity입니다. 이 Activity에는 BottomNavigationView가 배치되어 있고 Home/Profile 탭 변경 시 하늘색으로 표시된 프래그먼트를 교체하여 사용자에게 앱 탐색 경험을 제공합니다.

  • (위 그림 참고) Example1에서 하늘색으로 표시된 프래그먼트는 하위 프래그먼트 2개를 호스팅하는 호스트 프래그먼트 입니다. 하위 프래그먼트 2개를 통해 화면을 분할하고 있습니다.(하위 프래그먼트끼리는 동위 프래그먼트라고 부릅니다)

  • (위 그림 참고) Example2에서 하늘색으로 표시된 프래그먼트는 ViewPager2(스와이프 뷰)를 구현하는 하위 프래그먼트 1개를 호스팅하는 호스트 프래그먼트 입니다.

  • 08
  • Activity에서의 fragmentManager 와 supportFragmentManager 의 차이는, FragmentManager의 경우 Activity class에서 가져오고 SupportFragmentManager는 FragmentActivity에서 가져오는 것입니다. FragmentActivity 역시 Activity를 상속받지만, 상속받으면서 androidx로 마이그레이션 된다. 즉, supportFragmentManager는 androidx에서 사용하기 위한 fragmentManager입니다 Fragment에서의 fragmentManager 와 childFragmentManager 의 차이는, fragmentManager는 Activity와 Fragment 둘 다에서 관리가 가능하며, childFragmentManager는 Fragment에서만 관리가 가능합니다

  • (위 그림 참고) 위 예시에서 프래그먼트와 프래그먼트 관리자의 관계는 위와 같습니다. 개발자가 프래그먼트 관리자를 인스턴스화할 경우 이러한 관계를 잘 파악하고 있어야 합니다.

인스턴스화한 프래그먼트 관리자 사용하기

  • 또한 앞서 언급한 내용 중 프래그먼트 관리자는 프래그먼트 추가/교체/삭제 작업을 제공하는 FragmentTransaction 이라는 클래스를 구현하고 있다고 언급했습니다.

  • 29
  • (위 코드 참고) supportFragmentManager(=getSupportFragmentManager())를 호출하여 프래그먼트 관리자 클래스를 인스턴스하고 commit { } 블록을 호출한 코드입니다.

  • (위 코드 참고) commit{ } 블록 안에 프래그먼트 추가/교체/삭제 작업을 명시하면 됩니다(commit과 관련된 내용은 이어지는 다음 포스팅에서 다룰 예정입니다)

프래그먼트 관리자 사용하기 - 기존 프래그먼트 찾기

  • 프래그먼트 관리자가 제공하는 findFragmentById() 함수를 사용하면 프래그먼트 컨테이너 내의 현재 프래그먼트 참조를 가져올 수 있습니다.

  • 또는 프래그먼트에 고유한 태그를 부여한 후 findFragmentByTag() 함수를 호출하여 해당 태그가 부여된 프래그먼트 참조를 가져올 수 있습니다.

  • findFragmentById()는 UI를 제공하는 프래그먼트를 참조할 때 사용하고 findFragmentByTag()는 UI를 제공하지 않는 프래그먼트를 참조할 때 사용합니다.

하위 프래그먼트

  • 일반적으로 안드로이드 앱은 단일 또는 소수의 Activity로 구성하는 것이 좋습니다.

  • 주로 Activity에는 최상위 탐색 뷰(Navigation 같은 뷰)가 배치됩니다.

  • 이 외의 Activity를 구성하는 개별 UI은 프래그먼트를 사용하여 구현합니다.

  • 여러 프래그먼트를 한 번에 하나의 Activity에 표시하려면(분할 뷰) 대상 프래그먼트와 하위 프래그먼트를 사용해야 합니다.

  • 12
  • (위 그림 참고) 하늘색으로 표시된 부분이 대상 프래그먼트이고 하얀색으로 표시된 부분이 대상 프래그먼트의 하위 프래그먼트 2개입니다.(하위 프래그먼트끼리는 동위 프래그먼트입니다)

하위 및 동위 프래그먼트 특별 고려사항

  • 특정 시점에서 프래그먼트 백 스택을 제어하는데에는 프래그먼트 관리자 1개만 허용됩니다.

  • 앱에서 여러 동위 프래그먼트를 동시에 화면에 표시하거나 하위 프래그먼트를 사용하려는 경우 프래그먼트 관리자 한 개를 지정하여 앱의 기본 탐색을 처리해야 합니다.

  • 앱의 기본 탐색이란 다음과 같습니다.

  • Activity를 탐색의 가장 바깥쪽 레이어로 간주하고 하위 프래그먼트의 각 레이어를 바깥쪽 레이어 아래에 매핑합니다. 이 때 각 레이어에는 기본 탐색 프래그먼트가 하나씩 있어야 합니다. '뒤로' 이벤트가 발생하면 가장 먼저 가장 안쪽 레이어에서 탐색 동작을 컨트롤합니다. '뒤로' 이벤트가 발생했을 때 가장 안쪽 레이어에 다시 표시할 프래그먼트 트랜잭션이 더 이상 없으면 탐색 컨트롤은 다음 바깥 레이어로 옮겨집니다. 이 프로세스는 Activity에 도달할 때까지 반복됩니다.

  • 프래그먼트 트랜잭션 내에서 기본 탐색 프래그먼트를 지정하려면 FragmentTransaction 클래스에서 제공하는 setPrimaryNavigationFragment() 함수를 호출하여 childFragmentManager에 기본 컨트롤이 있어야 하는 프래그먼트의 인스턴스를 전달합니다.

profile
'왜?'라는 물음을 해결하며 마지막 개념까지 공부합니다✍

1개의 댓글

comment-user-thumbnail
2024년 3월 27일

감사합니다 ^^

답글 달기