[Android 앱 개발 숙련] 4. 프래그먼트

0
post-thumbnail

[Android 앱 개발 숙련] 4. 프래그먼트

  • 프래그먼트(Fragment)?
    • 액티비티 위에서 동작하는 모듈화된 사용자 인터페이스
    • 여러 개의 프래그먼트를 하나의 액티비티에 조합 가능
    • 하나의 프래그먼트를 여러 액티비티에서 재사용 가능
  • build.gradle(app)에 depedency 추가 필요!
    dependencies {
    	//...
       implementation "androidx.fragment:fragment-ktx:1.2.5"
    }

📌참고자료: 프래그먼트 | Android Developers

  • 프래그먼트:
    • 자체 레이아웃을 정의 및 관리
    • 자체 생명주기 보유
    • 자체 입력 이벤트 처리
  • 프래그먼트는 단독으로 실행될 수 X
    -> Activity 또는 다른 Fragment에서 호스팅해야
  • UI를 프래그먼트로 나누면, 런타임 시 Activity의 모양 쉽게 수정 가능
    -> Activity의 생명주기가 STARTED일 때, 프래그먼트 교체/삭제 가능

📌참고자료: 프래그먼트 생명주기 | Android Developers

  • 프래그먼트는 LifecycleOwner 인터페이스를 구현하여 수명주기 관리
  • 프래그먼트의 뷰는, 별도의 Lifecycle을 프래그먼트의 Lifecycle과 독립적으로 관리

프래그먼트 및 프래그먼트 관리자

  • 프래그먼트 인스턴스화되면 INITIALIZED 상태
    -> 다른 Lifecycle 상태로 전환하려면, FragmentManager에 프래그먼트를 추가해야
  • FragmentManager: 프래그먼트가 어떤 상태인지 확인 -> 다음 상태로 전환 담당
  • onAttatch():
    • 프래그먼트가 FragmentManager에 추가되었을 때, 호스트 활동에 연결되면 호출됨
    • 이 시점에 프래그먼트는 active
      (-> findFragmentById()로 검색 가능)
  • onCreate():
    • 프래그먼트 생성될 때 호출됨
  • ⭐onCreateView():
    • 프래그먼트의 레이아웃 인플레이트
  • ⭐onActivityCreated():
    • 액티비티의 onCreate()가 완료된 후 호출됨
    • 액티비티와 프래그먼트의 뷰가 모두 생성 완료된 상태
    • 뷰 관련 초기화 수행
  • onStart():
    • 프래그먼트가 사용자에게 보여질 준비가 되었음
    • 필요한 리소스를 할당하거나 애니메이션을 시작할 수 있음
  • ⭐onResume():
    • 프래그먼트가 사용자와 상호작용할 수 있는 상태가 되었음
    • 프래그먼트다 포그라운드에 있을 때 실행되는 작업을 여기서 처리
  • onPause():
    • 프래그먼트가 일시정지될 때 호출됨
    • 상태 저장, 스레드 중지 등의 작업 수행
  • onStop():
    • 프래그먼트가 더 이상 사용자에게 보이지 않음
    • 리소스 해제, 스레즈 정지 등의 작업 수행
  • onDestroyView():
    • 프래그먼트의 뷰와 관련된 리소스 정리
  • onDestroy():
    • 프래그먼트 파괴
    • 프래그먼트의 상태를 정리하고, 모든 리소스 해제
  • onDetach():
    • 프래그먼트가 FragmentManager에서 삭제되었을 때, 호스트 활동에서 분리되면 호출됨
    • 이 시점에 프래그먼트는 inactive
      (-> findFragmentById()로 검색 불가능)

프래그먼트 생명 주기 상태 및 콜백

  • 프래그먼트의 생명주기 상태는 상위 요소(Activity or Fragment)보다 클 수 X
    -> 상위 요소는 하위 프래그먼트보다 먼저 시작해야
    -> 하위 프래그먼트는 상위 요소보다 먼저 중지돼야

상향 상태 전환

  • (1) 프래그먼트 CREATED
    • 이미 프래그먼트 FragmentManager에 추가되고 onAttach() 메서드 호출됨
    • 프래그먼트의 뷰 아직 생성되지 X
    • 이 시점에서 onCreate() 호출됨
    • SavedStateRegistery를 통해 프래그먼트 자체와 연결된 저장 상태 복원하기에 적합한 시점
  • (2) 프래그먼트 CREATED, 프래그먼트의 뷰 INITIALIZED
    • 프래그먼트의 뷰가 null이 아닌 View로 인스턴스화
      -> 프래그먼트의 뷰 Lifecycle 생성됨
    • 이 시점에서 ⭐onViewCreated() 호출됨
    • 뷰의 초기 상태 설정하기에 적합한 시점
  • (3) 프래그먼트 CREATED, 프래그먼트의 뷰 CREATED
    • 프래그먼트의 뷰의 이전 뷰 상태(있는 경우)가 복원됨
    • 이 시점에서 onViewStateRestored() 호출됨
  • (4) 프래그먼트 STARTED, 프래그먼트의 뷰 STARTED
    • 프래그먼트의 뷰를 사용할 수 있는 상태
    • 프래그먼트의 하위 FragmentManager에서 FragmentTransaction을 안전하게 실행할 수 있는 상태
    • 이 시점에서 onStart() 호출됨
  • (5) 프래그먼트 RESUMED, 프래그먼트의 뷰 RESUMED
    • 모든 Animator 효과와 Transition 효과 완료되며 프래그먼트 표시 완료
    • 사용자가 프래그먼트와 상호작용할 수 있게 된 상태
    • 이 시점에서 ⭐onResume() 호출됨

하향 상태 전환

  • (6) 프래그먼트 STARTED, 프래그먼트의 뷰 STARTED
    • 프래그먼트가 표시된 상태에서 사용자가 프래그먼트를 벗어나기 시작함
    • 이 시점에서 onPause() 호출됨
  • (7) 프래그먼트 CREATED, 프래그먼트의 뷰 CREATED
    • 프래그먼트가 더 이상 표시되지 않는 상태
    • ON_STOP 이벤트: 프래그먼트의 하위 FragmentManager에서 FragmentTransactoin을 안전하게 실행할 수 있는 마지막 지점
  • (8) 프래그먼트 CREATED, 프래그먼트의 뷰 DESTROYED
    • 나가기 Animation 효과와 Transaction 효과가 모두 완료되며 프래그먼트 창에서 분리 완료
    • 이 시점에서 onDestroyView() 호출됨
    • 이 때 프래그먼트 뷰의 모든 참조 삭제해야 -> 가비지 수집됨
  • (9) 프래그먼트 DESTROYED
    • 프래그먼트가 삭제되거나 FragmentManager가 소멸된 상태
    • 이 시점에서 onDestroy() 호출됨
  • 액티비티 vs 프래그먼트
    • Activity: 시스템의 ActivityManager에서 intent를 해석해 액티비티간 데이터 전달
    • Fragment: 액티비티의 FragmentManager에서 메소드로 프래그먼트간 데이터 전달
  • 프래그먼트를 사용하는 이유
    • Activity를 적게 만들 수 있다
    • Activity의 복잡도를 줄일 수 있다
    • 재사용할 수 있는 레이아웃을 분리해 관리할 수 있다

프래그먼트 정적으로 추가하기

  • XML 레이아웃에 fragment 추가
    • <<fragment>>android:name = 레이아웃 안에서 인스턴스화 할 Fragment 클래스
    • 각 프래그먼트는 시스템이 사용할 수 있는 고유한 식별자 필요
      • 방법(1) android:id 속성에 고유한 ID 지정하기
      • 방법(2) android:tag 속성에 고유한 문자열 지정하기
      • 방법(3) 위의 두 속성이 없는 경우, 시스템은 컨테이너 뷰의 ID 사용
  • Fragment의 서브 클래스 생성
    • onCreateView() 콜백 메서드에서 inflate() 함수를 통해 레이아웃 추가
class FirstFragment : Fragment() {  
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

프래그먼트 동적으로 추가하기

class MainActivity : AppCompatActivity() {

    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        setFragment(FirstFragment())
        
        binding.apply {
            fragment1Btn.setOnClickListener{
                setFragment(FirstFragment())
            }
            fragment2Btn.setOnClickListener {
                setFragment(SecondFragment())
            }
        }
    }
    
    private fun setFragment(frag : Fragment) {
        supportFragmentManager.commit {
            replace(R.id.frameLayout, frag)
            setReorderingAllowed(true)
            addToBackStack("")
        }
    }
}

📌참고자료: Fragment Manager | Android Developers

Fragment Manager 액세스

  • Fragment를 호스팅하는 Activity: getSupportFragmentManager()
  • Fragment를 호스팅하는 Fragment:
    • 하위 Fragment를 관리하기 위해 getChildFragmentManager()
    • 부모(호스트) Fragment를 관리하기 위헤 getParentFragmentManager()
profile
Be able to be vulnerable, in search of truth

0개의 댓글