Android androidx: 프래그먼트

timothy jeong·2021년 11월 8일
0

Android with Kotlin

목록 보기
24/69

androidx 에서 제공하는 라이브러리가운데 프래그먼트와 뷰 페이저2는 중요도와 사용빈도가 높다. 프래그먼트는 플랫폼 API 에서도 android.app.Fragment 로 제공하지만 대부분 androidx.fragment 라이브러리를 이용해 구현한다.

프래그먼트

프래그먼트는 대부분의 뷰들과 마찬가지로 액티비티 화면을 구성하는 뷰이다. 그런데 그 자체만으로는 화면에 아무것도 출력되지 않는다. 이런 프래그먼트의 유용한 점은 액티비티 처럼 동작한다는 것이다. 즉, 액티비티에 작성할 수 있는 모든 코드는 프래그먼트에도 사용할 수 있다.

프래그먼트는 테블릿처럼 화면이 넓은 기기에서 동작하는 앱을 개발할 수 있도록 제공되었다. 한 화면은 하나의 액티비티 클래스에서 작성해야 하는데, 화면이 크면 액티비티 클래스에 너무 많은 코드를 작성해야 하는 문제가 있다.

왼쪽화면과 오른쪽 화면의 내용을 각각 클래스로 분리해서 작성하고 액티비티에서 두 클래스를 조합만 하면 이러한 문제를 해결할 수 있었다.

그런데 두 클래스가 액티비티 화면에 출력되어야 하므로 각각의 클래스를 뷰 클래스로 만들어야 한다. 문제는 뷰 클래스는 액티비티가 아니므로 액티비티에 작서오딘 모든 내용을 뷰 클래스에 담을 수 없다. 이때 프래그먼트가 필요하다.

그리고 프래그먼트는 반드시 테블릿처럼 넓은 화면을 목적으로 하는 앱에서만 사용할 필요는 없다. 보통 사이즈의 모바일 폰에서는 화면 전환을 통해 프래그먼트를 활용할 수 있다.

프래그먼트를 사용하는 대표적인 사례로 탭과 뷰 페이저 화면을 들 수 있다. 탭과 뷰 페이저 화면을 어떻게 구현하는지는 이후 포스팅에서 다룬다. 탭과 뷰 페이저는 모두 여러 화면을 제공하는 것이 목적인데, 이를 모두 하나의 클래스에 구현하기는 부담스러우므로 탭과 뷰 페이저가 제공하는 한 화면을 하나의 프래그먼트로 개발한다.

예를 들어 탭 버튼이 4개 있는 화면을 만들려면 각 버튼을 클릭했을 때 나와야 하는 화면도 4개가 필요하다. 이를 각각 프래그먼트로 개발하고 액티비티에서 탭 버튼만 제공하여 사용자가 각 탭 버튼을 클릭하면 프래그먼트만 교체해서 출력하는 것이다.

프래그먼트 구현

의존성, 레이아웃

프래그먼트 활용을 위한 의존성을 추가하고, 프래그먼트용 레이아웃을 작성해야한다. 레이아웃을 작성하는데 특별한건 없다.

dependencies {

    implementation 'androidx.fragment:fragment-ktx:1.3.6'
}

fragment_some.xml

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

    <androidx.appcompat.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="hello" />

</LinearLayout>

코드

코드 상에서는 Fragment 를 상속한 클래스를 만들어야 하며, 이 클래스는 최소한 onCreateView 함수를 override 해야한다. 만약 어떤 view 로 나타내지 않는 Fragment 라면 null 을 return 해도 된다.

import androidx.fragment.app.Fragment

class SomeFragment: Fragment() {
    lateinit var binding: FragmentSomeBinding

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

레이아웃에 프래그먼트 추가

프레그먼트로 뷰를 보여주는 경우 액티비티의 레이아웃 XML 에 프레그먼트 레이아웃을 등록하여야 한다. 프레그먼트도 View 이므로 당연히 가능하다.

<fragment
        android:name="com.example.testmodule.SomeFragment"
        android:id="@+id/fragment_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

액티비티 레이아웃 xml 에서 프래그먼트를 출력할 수 있지만 때로는 코드에서 직접 프레그먼트 객체를 생성하여 화면에 출력해야 할 수도 있다. 이럴 때는 우선 액티비티의 레이아웃 xml 파일에 프레그먼트가 출력될 뷰를 준비하고,

<?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=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/fragment_container"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView">
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

이상은 레이아웃 상에서 프래그먼트를 참조하는 방법이다.

코드에서 동적으로 생성한 프래그먼트를 출력할 수 있다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val fragmentManager = supportFragmentManager
        val transaction: FragmentTracsaction = supportFragmentManager.beginTransaction()
        val fragment = SomeFragment()
        transaction.add(R.id.fragment_container, fragment)
        transaction.commit()
        setContentView(R.layout.activity_main)
    }
}

FragmentTracsaction 객체의 add() 함수를 이용해 프레그먼트 객체를 화면에 출력하는 코드이다. add() 함수의 첫 인자는 프래그먼트가 출력될 뷰의 id 값이다. 그리고 commit() 함수를 호출하는 순간 화면에 적용된다.

  • add(int containerViewId, Fragment fragment): 새로운 프레그먼트를 화면에 추가한다.
  • replace(int containerViewId, Fragment fragment): 추가된 프래그먼트를 대체한다.
  • remove(Fragment fragment): 추가된 프레그먼트를 제거한다.
  • commit(): 화면에 적용한다.

프레그먼트 생명주기

프레그먼트는 액티비티처럼 동작하는 뷰이다. 따라서 액티비티의 생명주기 함수를 그대로 사용할 수 있으므로 각 생명단계마다 사용하는 함수의 이름도 동일하다. 액티비티 라이프 사이클 포스팅

이러한 프레그먼트의 생명주기는 onResume 된 뒤 활성 상태에서 두가로 생명 주기 흐름이 나뉠 수 있다. 생명 주기 흐름은 프레그먼트가 화면에서 사라지면서 백스택에 포함되었는지에 따라 다르다.

onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart -> onResume -> [활설상태] -> onPuase -> onStop -> onDestroyView -> onDestroy -> onDetach -> 프레그먼트 소멸

onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart -> onResume -> [활성상태] -> onPuase(백스택) -> onStop -> onDestroyView

백 스택을 이용할 것인지는 FragmentTransaction 의 addToBackStack() 함수로 설정할 수 있다.

transaction.addToBackStack(true)

프레그먼트에 추가되는 생명주기

  • onAttach(): 프레그먼트가 액티비티에 포함되는 순간 호출
  • onCreateView(): 프레그먼트의 화면을 구성할 때 호출, 이 함수가 반환하는 뷰를 프레그먼트 화면에 출력
  • onActivityCreated(): 프레그먼트의 액티비티가 생성되는 순간 호출
  • onDestroyView(): 프레그먼트가 화면에서 사라지는 후 백 스택에 추가될 때 호출
  • onDetach(): 프레그먼트가 액티비티에서 제거될 때 호출

참고하면 좋은 블로그

profile
개발자

0개의 댓글