Android

[Kotlin] Fragment 사용하기

bryan.oh 2023. 7. 28. 09: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
반응형