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 호출하기
를 해보도록 하겠습니다.
'Android' 카테고리의 다른 글
[Kotlin] Activity 에서 Fragment 변경하기 (0) | 2023.07.28 |
---|---|
[Kotlin] Fragment 에서 상위 Activity Method 호출하기 (0) | 2023.07.28 |
[Kotlin] Android 에서 Retrofit2 사용하여 API 호출하기 (0) | 2023.07.25 |
[Kotlin] apply, with, let, ?: 사용하기 (0) | 2023.07.25 |
[Kotlin] 리니어 레이아웃(LinearLayout) 예제 (0) | 2023.07.25 |
댓글