Study Record

[안드로이드] Fragment 살펴보기 본문

안드로이드

[안드로이드] Fragment 살펴보기

초코초코초코 2023. 6. 22. 13:50
728x90

😶 Fragment

Fragment 는 앱 화면에서 사용자와 상호작용할 수 있는 한 부분으로 ViewGroup 에 속한다. Activity 에 여러 개의 Fragment 가 존재할 수 있고 여러 개의 Activity 에 하나의 Fragment 를 재사용할 수 있다. 항상 Activity 내에서 호스팅 되어야 한다.

 

 

자체 생명 주기를 가지고 자체 입력 이벤트를 수신하며 Activity 실행 중 추가 및 삭제가 가능하다. Activity 가 관리하는 백 스택에도 Fragment 를 추가할 수 있다. (ex. 뒤로 가기 버튼을 누르면 액티비티가 종료하는 것이 아닌 이전 Fragment 가 나오는 경우)

 

 

😶 Fragment 수명주기

developers 공식 문서 이미지

프래그먼트의 수명 주기는 왼쪽 그림과 같다. 그 중 최소한 구현해야 하는 수명 주기를 살펴보면 onCreate() , onCreateView() , onPause() 가 있다.

 

onCreate()

프래그먼트를 생성할 때 시스템에서 호출하는 매서드로 프래그먼트가 일시정지되거나 중단되었다 재개되었을 때 유지하고자 하는 것을 초기화한다.

 

onCreateView()

사용자 인터페이스를 처음으로 그릴 때 호출되는 매서드로 return 값으로 화면에 그려질 View 를 반환해야 한다. null 을 반환할 경우 프래그먼트의 UI 를 제공하지 않는다는 의미가 된다.

 

onViewCreated()

View 가 생성된 후 호출되는 메서드로 여기서 일반적으로 findViewById 함수를 호출한다.

 

onPause()

프래그먼트가 소멸하기 전, 사용자가 프래그먼트를 사용하지 않을 때 호출된다. 사용자가 다시 프래그먼트로 돌아올수도, 돌아오지 않을 수도 있다.

 

onActivityCreated()

Activity 의 onCreate() 메서드가 반환될 때 호출된다.

 

onDetach()

Fragment 가 Activity 와 연결이 끊어지는 중일 때 호출된다.

 

 

 

 

 

 

😶 Fragment State

INITIALIZED : 프래그먼트의 새 인스턴스가 인스턴스화됨

CREATED : 첫 프레그먼트 라이프사이클 메서드가 호출됨. 이 상태 동안 프레그먼트와 뷰가 상호작용하기 시작됨.

STARTED : 프래그먼트가 화면에 보이기 시작했을 때로 아직 focus 는 얻지 못함. 사용자와 상호작용 불가능.

RESUED : 프래그먼트가 화면에 보이고 focus 를 얻은 상태.

DESTORYED : 프래그먼트 객체가 해제됨.

 

 

수명 주기 메서드와 State 와의 관계

Lifecycle State Fragment Callback
CREATE onCreate(), onCreateView(), onViewCreated()
STARTED onStart()
RESUMED onResume()
STARTED onPause()
CREATED onStop(), onDestoryView()
DESTORYED onDestory()

 

 

 

😶 Fragment 시작하기

ExampleFragment 를 MainActivity 에 연결하기

Fragment 의 xml 파일(fragment_example.xml)을 만들고 Fragment 클래스를 상속받는 ExampleFragment 클래스를 만든다. 이 예시는 데이터 바인딩을 사용한다.

class ExampleFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        // return inflater.inflate(R.layout.fragment_example, container, false)
        val binding = DataBindingUtil.inflate<FragmentExampleBinding>(
          inflater,
          R.layout.fragment_example, 
          container, 
          false,
        )
        return binding.root
    }

    companion object {
        @JvmStatic
        fun newInstance(param1: String, param2: String) =
            ExampleFragment()
    }

 

Fragment 를 Activity 에 연결하는 2가지 방법이 있는데 한가지는 xml 코드에서 바로 연결하는 것이고, 나머지 하나는 프로그래밍 방식으로 프래그먼트를 ViewGroup 에 추가하는 방법이다.

 

 

1. xml 코드에서 바로 연결하는 방법

FragmentContainerView 태그의 android:name 속성에 연결할 Fragment 클래스를 입력한다.

<?xml version="1.0" encoding="utf-8"?>
<layout>
    <androidx.constraintlayout.widget.ConstraintLayout 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">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/main_fg"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:name="com.example.basic_text.ExampleFragment"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

 

2. 프로그래밍 방식으로 프래그먼트를 기존의 ViewGroup 에 추가한다.

Activity 내에서 프래그먼트 트랜잭션(추가, 제거, 교체)을 사용하여 추가한다.

class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

        // 프래그먼트 추가
        with(supportFragmentManager.beginTransaction()) {
            add(binding.mainFg.id, ExampleFragment())
            commit()
        }
    }
}

 

 

😶 프래그먼트 관리 (FragmentManager)

Activity 에서 Fragment 를 관리하려면 FragmentManager 를 사용해야 한다. 이것은 Activity 에서 supportFragmentManager 로 불러올 수 있다.

 

FragmentManager 에서 불러올 수 있는 트랜잭션(Transaction)이 있는데 이 트랜잭션은 수행하고자 하는 변경사항의 집합니다. 즉, xml 에 정의한 프래그먼트 태그에 어떤 프래그먼트를 연결할지와 같은 변경사항들을 수행할 수 있다. 이렇게 변경사항들(Transaction)을 적용하려면 commit() 을 호출하면 된다.

val newFragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.add(R.id.fragment_container, newFragment)
transaction.commit()

 

프래그먼트를 교체하려면 transaction.replace() 함수를 사용하면 되는데 이 때 commit() 을 호출하기 전 addToBackStack() 을 호출하면 백 스택에 프래그먼트 정보를 저장하여 사용자가 Back 버튼을 누를 때 자동으로 이전 프래그먼트를 가져올 수 있다.

val newFragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, newFragment)
transaction.addToBackStack(null)
transaction.commit()

만약 현재 프래그먼트를 제거하는 트랜잭션을 수행하면서 addToBackStack() 를 사용하지 않으면 해당 프래그먼트는 소멸되고 사용자가 탐색할 수 없게 된다.

 

 

 

commit() vs commitAllowingStateLoss()

트랜잭션을 커밋할 때 commit() 을 호출하더라도 즉시 트랜잭션이 실행되지 않고 Activity 의 UI 스레드에 이 트랜잭션이 수행되도록 예약하는 것에 가깝다. commit() 을 사용할 수 있는 것은 Activity 가 상태를 저장하기 전뿐이고 이 이후에 커밋하려고 하면 예외가 발생한다. 

 

예외가 발생하는 이유는 Activity 를 복원해야 하는 경우 커밋 이후의 상태가 손실될 수 있기 때문이다. 손실되어도 괜찮다면 commitAllowingStateLoss() 를 사용할 수 있다.

 

 

 

😶 Activity 와의 통신

 

 

Fragment 에서 Activity 참조

간단하게 Fragment 에서 activity 로 참조하는 Activity 를 가져올 수 있다.

(activity as MainActivity)

 

 

Activity 에서 Fragment 참조

findFragmentById() 혹은 findFragmentByTag() 함수로 id 혹은 tag 로 Fragment 에 접근할 수 있다.

(supportFragmentManager.findFragmentById(binding.mainFg.id) as ExampleFragment)

 

 

 

 

728x90