본문 바로가기
Android

[Kotlin] Fragment 사용하기

by bryan.oh 2023. 7. 28.
반응형

Fragment

 

Android에서 Fragment는 UI 조각을 나타내는 컴포넌트로, 하나의 화면 내에서 재사용 가능한 UI 요소를 구성하는데 사용됩니다. Fragment는 Activity 내에 포함되어 독립적으로 존재하지만, Activity의 라이프사이클에 의존하여 동작합니다.

Fragment를 사용하면 

  • 앱의 UI를 더 모듈화하고 재사용성을 높일 수 있음
  • 여러 화면 크기와 디바이스에 대해 더 유연하게 대응
  • 화면을 나누어 여러 작은 조각으로 구성하여 사용자 인터페이스를 관리
  • 각 조각을 독립적으로 관리


Fragment의 라이프사이클은 Activity의 라이프사이클과 밀접한 관련이 있으며, 다양한 메소드를 사용하여 상태 변화를 감지하고 제어할 수 있습니다.

Fragment의 주요 라이프사이클 메소드

onAttach(): Fragment가 Activity에 연결될 때 호출됩니다.
onCreate(): Fragment가 생성될 때 호출됩니다.
onCreateView(): Fragment의 레이아웃을 그리기 위해 호출됩니다.
onViewCreated(): Fragment의 뷰가 생성되고 초기화된 직후 호출됩니다.
onActivityCreated(): Fragment가 속한 Activity의 onCreate()가 완료된 후 호출됩니다.
onStart(): Fragment가 사용자에게 보여지기 시작할 때 호출됩니다.
onResume(): Fragment가 활성화되고 사용자와 상호작용이 가능해질 때 호출됩니다.
onPause(): Fragment가 사용자와 상호작용을 중단하고 다른 Fragment가 활성화될 때 호출됩니다.
onStop(): Fragment가 더 이상 보이지 않을 때 호출됩니다.
onDestroyView(): Fragment의 뷰가 소멸될 때 호출됩니다.
onDestroy(): Fragment가 소멸될 때 호출됩니다.
onDetach(): Fragment가 Activity와 연결이 해제될 때 호출됩니다.


Fragment는 기본적으로 하나의 Activity 내에서 관리되며, Activity에서 여러 개의 Fragment를 호스팅할 수 있습니다. 

Activity와 Fragment 간의 통신은 인터페이스를 통해 이루어질 수 있으며, Fragment 내에서는 자체적으로 로직을 구성하여 UI와 동작을 관리합니다.


Fragment 생성

지난 글에 사용한 프로젝트에 이어서 설명하겠습니다.

fragments 라는 페키지를 만들고 그 안에 new > Fragment > Fragment(Blank) 를 생성합니다.

LoginFragment 로 이름을 변경합니다.

기본적으로 생성된 loginFragment 소스에는 파라메터를 받는 셈플이 작성되어 있습니다.

파라메터는 나중에 설명하기로 하고, 우선은 onCreateView 를 제외하고 다 삭제합니다.

그럼 소스는 아래와 같이 됩니다.

class LoginFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_login, container, false)
    }
}

Fragment 도 ViewBinding 으로 변경합니다.

class LoginFragment : Fragment() {

    private lateinit var binding: FragmentLoginBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentLoginBinding.inflate(inflater, container, false)
        return binding.root
    }
}

 

그리고 이 Fragment 를 Activity에 추가합니다.

MainActivity 에 추가해도 되고, FragmentTest 만을 위한 Activity 를 만들어도 됩니다.

 

(여기에서는

  • FragmentTestActivity 를 만들고,
  • MainActivity 에서 버튼 생성하고
  • 클릭하면 FragmentTestActivity 로 이동하겠습니다.

이 부분은 지난 글에서 많이 해봐서 생략하겠습니다.)

보통 FrameLayout 을 사용해서 Fragment 를 생성했었고 지금도 가능합니다.
FrameLayout 을 대체할 FragmentContainerView 가 나왔습니다.
FragmentContainerView 
androidx.fragment 1.2.0 이상
FrameLayout을 확장
프래그먼트 트랜잭션을 안정적으로 처리
프래그먼트 동작을 조정할 수 있음

 

생성한 Fragment를 추가할 Activity 의 layout 파일을 열고

palette 에서 Containers > FragmentContainerView 를 화면으로 드래그합니다.

Fragment 를 선택하는 팝업이 뜨면, 아까 만들었던 LoginFragment 를 선택합니다.

기본적으로 constaintLayout 이기때문에,

추가된 fragmentContainerView 에 constraint 설정을 합니다.

최상단에는 TextView 를 추가하고, 그 아래 FragmentContainerView 를 위치하도록 합니다.

<?xml version="1.0" encoding="utf-8"?>
<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=".FragmentTestActivity">
    
    <TextView
        android:id="@+id/fragment_test_top_textview"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:text="여기는 Activity TextView 입니다"
        android:background="#2196F3"
        android:textAlignment="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:name="com.example.test01.fragments.LoginFragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/fragment_test_top_textview" />

</androidx.constraintlayout.widget.ConstraintLayout>
참고
혹시 추가 한 FragmentContainerView 에  Unknown fragments 오류 메시지가 뜬다면, 
방법1. 아래 스크린샷에 보이는 Pick Layout.. 을 눌러서 fragment_login.xml 을 선택합니다.
방법2. androidx.fragment.app.FragmentContainerView 를 사용하지 말고 FrameLayout 을 사용합니다.

 

그리고 Activity 의 onCreate 에 다음 코드를 입력합니다.

supportFragmentManager
    .beginTransaction()
    .replace(binding.fragmentContainerView.id, LoginFragment())
    .commit()
추가 설명
androidx.fragment.app.FragmentContainerView 에 name attribute가 없다면 위의 supportFragmentManager 코드가 필요합니다.
반면, android:name="com.example.test01.fragments.LoginFragment" 가 있다면 supportFragmentManager 없이도 화면에 보입니다. 간혹 안 나오는 경우가 있다면 위 코드를 실행하면 보입니다.

 

Fragment 에 아무것도 없기 때문에, 몇가지 컴포넌트들을 넣어줍니다.

fragment의 layout xml 내용입니다.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragments.LoginFragment">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:orientation="horizontal">

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="3"
            android:orientation="vertical">

            <EditText
                android:id="@+id/editTextTextPersonName4"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:inputType="textPersonName"
                android:text="Name" />

            <EditText
                android:id="@+id/editTextTextPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:inputType="textPassword" />
        </LinearLayout>

        <Button
            android:id="@+id/button"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="Button" />
    </LinearLayout>
</FrameLayout>

 

그리고 앱을 실행하고, MainActivity 에서 생성한 버튼으로 FragmentTestActivity를 실행하면 다음과 같이 화면이 만들어집니다.

 

참고

추가로, 리소스를 효율적으로 관리하려면 Fragment 의 binding 을 메모리에서 해제해야합니다. 안그러면 메모리 릭이 발생한다고 하네요.

onDestroyView() 에서 binding = null 해야 하는데, lateinit 으로 선언한 값은 nullable이 아니기 때문에 아래와 같이 컴파일 오류가 발생합니다.

binding 이 lateinit 이면 none-null type 이라서 원래는 아래와 같은 코드를

class LoginFragment : Fragment() {

    private lateinit var binding: FragmentLoginBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentLoginBinding.inflate(inflater, container, false)
        return binding.root
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        binding = null  // none-null type value error
    }
}

다음과 같이 소스를 수정하면, binding = null 로 해제할 수 있습니다.

class LogoutFragment : Fragment() {
    private var binding: FragmentLogoutBinding? = null
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentLogoutBinding.inflate(inflater, container, false)

        binding!!.logoutButton.setOnClickListener {
            val activity = activity as FragmentTestActivity?
            activity?.someMethodInActivity(1)
        }

        return binding!!.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
    }
}

 

 


다음 포스트에서

Fragment 에서 상위 Activity Method 호출하기

를 해보도록 하겠습니다.

 

 

 

 

728x90
반응형

댓글