안드로이드 학습 - Fragment

cherrylime69·2022년 3월 30일
0

안드로이드

목록 보기
4/6

📚 Fragment

👀 fragment란?

  • 앱 UI에서 특정 영역을 재사용하기 위해 사용되는 요소입니다.

  • 프래그먼트는 자체 레이아웃(xml파일을 정의)을 정의 및 관리하고 자체 수명 주기를 보유하며 자체 입력 이벤트를 처리할 수 있습니다.

  • 프래그먼트는 독립적으로 존재할 수 없고 반드시 Activity나 Host Fragment에 호스팅되어야 합니다. (Host Fragment도 Activity로부터 호스팅되므로 결국 Activity로부터 호스팅되어야 합니다)

  • 따라서 프래그먼트의 뷰 계층 구조는 호스트 뷰 계층 구조의 일부가 됩니다.

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

👀 Activity와의 차이점

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

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

    • Activity UI를 모듈화하여 유연성을 높입니다.

      • 실제로 1개의 Activity에 여러 개의 fragment를 구성한 UI를 google은 권장합니다.
    • UI 를 조립할 수 있습니다.

    • 화면 크기에 따른 배치를 할 수 있습니다.

👉 Fragment 장점

유연하게 UI를 디자인할 수 있다는 게 가장 큰 장점
fragment 독립적인 모듈(재사용 가능한 화면)

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

    • 사용자가 앱을 실행하여 사용하는 도중(Activity is running)에 Activity의 모양을 수정할 수 있다는 의미입니다.

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

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

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

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

👉 Fragment 주의점

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

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

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

👀 Fragment Manger (프래그먼트 관리자)

👉 Fragment Manger 란?

  • fragment manager는 액티비티에 프래그먼트를 추가, 삭제, 교체하는 작업을 관리하는 클래스입니다.

    • 즉, 프래그먼트 백스택(Back Stack) 에 프래그먼트 추가/교체/삭제 등 작업에 의한 변경 사항을 push 및 pop 하는 작업을 담당하는 클래스입니다

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

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

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

    • 하지만 라이브러리 내부 동작도 결국은 프래그먼트 관리자를 사용하기 때문에 어떤 원리로 프래그먼트 관리자가 작동하는지 이해하는 것이 중요합니다.

👉 프래그먼트 관리자 인스턴스화하기

  • 프래그먼트 관리자에는 추가, 제거, 변경을 제공하는 Fragment Transaction 클래스가 내부에 구현되어있어, 이를 인스턴스화해야 프래그먼트를 추가, 제거, 변경을 할 수 있습니다.

    • Fragment Transaction 클래스는 추상 클래스며, FragmentManger 클래스에 구현되어있습니다.
  • 프래그먼트 관리자는 2가지 방법으로 인스턴스화할 수 있습니다.

    1. Activity에서 인스턴스화

      • FragmentActivity 클래스를 상속하는 모든 서브 클래스에서 getSupportFragmentManager() 함수를 호출하면 프래그먼트 관리자를 인스턴스화할 수 있습니다.(코틀린에서는 supportFragmentManager 호출)
      • Activity는 FragmentActivity를 상속받기 때문에, activity 내에서 supportFragmentManger를 호출할 수 있습니다.
    2. Fragment에서 인스턴스화

      • child 프래그먼트에서는 parentFragmentManger를 호출하여 프래그먼트 매니저를 인스턴스화 할 수 있습니다.
      • host 프래그먼트에서는 childFragmentManger를 호출하여 프래그먼트 매니저를 호출할 수 있습니다.

👀 Fragment Transaction

  • 프래그먼트 트랜잭션은 Android Jetpack Fragment 라이브러리에서 제공하는 클래스입니다.

  • 프래그먼트 트랜잭션 클래스는 프래그먼트 추가/교체/삭제 작업을 제공합니다.

  • 이 외에도 프래그먼트 트랜잭션은 프래그먼트 관리자가 수행할 단일 단위입니다. 따라서 하나의 프래그먼트 트랜잭션 단위 내에 프래그먼트 트랜잭션 클래스가 제공하는 프래그먼트 추가/교체/삭제 작업 등을 명시하면 됩니다.

  • 하나의 프래그먼트 트랜잭션 단위 내에 작성된 프래그먼트 조작 관련 작업들은 해당 프래그먼트 트랜잭션이 수행될 때 모두 실행됩니다.
    (프래그먼트 조작 작업의 묶음 단위라고 생각하면 쉽습니다)

  • 앞서 설명했듯이, FragmentTransaction 클래스는 추상 클래스로 FragmentManger 클래스에 구현되어있습니다. 따라서 FragmentTransaction을 생성하기 위해서는 프래그먼트 관리자 클래스에서 제공하는 beginTransaction() 함수 호출하여 생성해야 합니다.

👉 반드시 fragment transaction 마지막에 commit()

  • 프래그먼트 트랜잭션을 생성하여 프래그먼트 추가/교체/삭제 작업을 명시한 후에는 반드시 마지막에 커밋(commit)을 해줘야 합니다.

  • commit() 함수가 비동기로 처리되는 함수이기 때문에 commit() 함수 호출 시점에서 트랜잭션이 즉시 수행되는 것이 아니라 메인 쓰레드에 예약됩니다.

  • 메인 쓰레드가 예약된 트랜잭션을 수행할 준비가 되면 비로소 그 때 트랜잭션이 수행되어 명시한 프래그먼트 조작 작업들이 실행됩니다.

👀 Fragment lifecycle

  • 프래그먼트가 별도로 라이프사이클을 가지는 이유?

    • 프래그먼트는 액티비티에서 inflate되는 구성 요소입니다.

    • 따라서 액티비티의 상태가 변함에 따라 프래그먼트의 상태도 변합니다.

  1. onCreate()
  • FragmentInstance를 구성합니다.

    → Model과 같이 Fragment 구성에 필요한 것을 초기화합니다.

  • onCreate() 에서 inflate 하면 안되는 이유

    • 프래그먼트는 액티비티에서 inflate되는 뷰이므로 최소한 액티비티의 레이아웃이 inflate 된 이후에 진행되어야 되기 때문에 onCreateView()에서 inflate 합니다.
  1. onCreateView
  • Fragment뷰의 Layout을 inflate합니다.

  • inflate된 뷰를 hostingactivity에 반환합니다

  • attachToRoot는 false로 해야 합니다.

    • 프래그먼트는 호스트가 되는 액티비티 위에 레이아웃을 inflate하기 때문에 액티비티의 구성 요소가 모두 구성된 이후에 inflate 되어야 합니다.
    • 다시말해, inflater.inflate(R.layout.fragment_home, container, false) 에서 attachToRoot를 false로 하면 호스트 액티비티 또는 호스트 프래그먼트가 모두 생성된 다음에 해당 레이아웃을 inflate 시킵니다.
    • 따라서 해당 프래그먼트의 layout 생성 시기를 늦추기 위해 false를 합니다.
  1. onViewCreated()
  • [view] : onCreateView에서 생성된 View객체

    → 초기값설정, listener설정 등

  1. onViewStateRestored()
  • 화면이 소멸되었다가 재성성되는 경우, 이전에 저장했던 데이터를 기반으로 뷰를 복원하는 절차를 진행할 때 활용되는 상태입니다.

Created 단계에서 호출되는 콜백함수들은 프래그먼트를 프래그먼트 매니저에 추가할 때 호출되는 메서드들입니다.

  1. CREATED - onStart - onResume - onPause - onStop
  • Activity와 Fragment의 생명주기 콜백은 대응됩니다.

  • Fragment는 Activity의 상태를 반영해야 합니다.

    →Fragment의 생명주기함수는 FragmentManager가 관리합니다.

  • 따라서 각 콜백함수에 적합한 작업은 프래그먼트에도 동일하게 적용할 수 있다

  1. 참고로 onSaveInstanceState()은 화면이 소멸되기 전에 화면을 복원할 때 필요한 데이터를 저장할 때 호출되는 콜백함수입니다.. 여기서 저장한 데이터를 onViewStateRestored()에 전달하여 화면을 복원합니다.

  2. onDestroyView() 는 onCreateView()와 대응하는 단계로, 화면이 소멸되기 직전에 아직 리소스를 해제하지 못했다면 여기서 처리합니다.

  3. 액티비티와 동일하게 화면이 소멸되기 전에 onDestroy()가 호출됩니다.

👀 Fragment Back Stack

  • 액티비티와 동일하게 프래그먼트도 Back Stack 을 가지고 있습니다.

  • 하지만 액티비티와는 다르게 프래그먼트 백스택은 프래그먼트 관리자가 프래그먼트가 아니라 프래그먼트 트랜잭션을 push합니다.
    (프래그먼트 관리자가 백스택을 관리합니다)

  • 따라서 프래그먼트에서도 뒤로 가기를 누르면 이전 프래그먼트 화면 또는 작업이 보이게 됩니다.

  • 단, 트랜잭션을 기록하기 위해서는 addToBackStack() 메서드를 트랜잭션 안에서 호출해야 합니다.

  • multiple backstack 이란?

    • 액티비티 입장에서는 각각의 프래그먼트가 백스택이 있으니 이를 multiple backstack 이라 생각할 수 있습니다.
profile
개발자 일기

0개의 댓글