안드로이드 Fragment

김민준·2024년 2월 28일

프래그먼트란?
프래그먼트
프레그먼트 페이징
공부하며 느낀 점

프래그먼트란?

  • 안드로이드의 UI를 유연하게 관리하기 위해 조각(Fragment)으로 모듈화하여 유연하게 관리하는 것을 의미한다.
  • 프래그먼트는 자체 생명주기를 가진다.
  • 액티비티 내에서 추가, 제거, 교체가 가능하다.
  • 런타임에서 동적인 UI 변경이 가능하다.
  • 프래그먼트간 상호작용으로 앱의 다양한 기능들을 통합/관리할 수 있다.

목적
1. 복잡한 UI의 모듈화를 통한 유연한 관리
2. Activity를 기능별 프레그먼트로 분할하여 유연하게 관리

효과
1. 모듈화 : 작은 단위로 효율적으로 관리, 복잡한 UI를 가진 앱 개발에 유용
2. 재사용성 : 여러 액티비티에서 재사용 가능, 코드 중복을 줄이고 개발 시간을 단축시키는데 도움이 된다.
3. 유연함 : 액티비티의 내에서 여러 프레그먼트를 유연하게 사용하여, 화면의 크기와 비율에 맞는 UI를 제공

프래그먼트의 구조

아래는 안드로이드 스튜디오에서 기본 제공하는 프래그먼트 구조들이다.

프래그먼트

class FirstFragment : Fragment() {
    private var _binding: FragmentFirstBinding? = null
    private val binding get() = _binding!!

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.textviewFirst.text = "First Fragment"
        binding.buttonFirst.setOnClickListener {
        }
    }

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

class FirstFragment : Fragment() { : Fragment() 라는 클래스를 상속한다.

_binding = FragmentFirstBinding.inflate(inflater, container, false) : FragmentFirstBinding.xml 이라는 파일을 참조한다.

override fun onViewCreated : 아래에 바인딩 된 동작들이 있다.

레이아웃(xml)

<!-- fragment_first.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"
    android:padding="16dp"
    tools:context=".FirstFragment">

    <Button
        android:id="@+id/button_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/next"
        app:layout_constraintBottom_toTopOf="@id/textview_first"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textview_first"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="@string/lorem_ipsum"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/button_first" />
</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.constraintlayout.widget.ConstraintLayout : 가장 큰 레이아웃

<Button, <TextView : 그 아래에 버튼과 텍스트가 하나씩 있다.

Activity에 추가하기

  1. 액티비티 레이아웃 xml파일에 FragmentContainnerView를 추가한다.
  2. android:name 속성에 Fragment의 전체 클래스 이름을 지정한다.
<!-- activity_main.xml -->
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.yourpackage.FirstFragment" />

또는 동적으로 추가한다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Fragment를 동적으로 추가하는 코드
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, FirstFragment())
                .commit()
        }
    }
}

프레그먼트 페이징

ViewPager2FragmentStateAdapter를 사용해서 프레그먼트 페이징을 구현해보자

액티비티 XML파일에 ViewPager2 레이아웃 추가

<!-- activity_main.xml -->
<androidx.viewpager2.widget.ViewPager2
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

FragmentStateAdapter 만들기

class NameOfFragmentStateAdapter(activity: AppCompatActivity) : FragmentStateAdapter(activity) {
    override fun getItemCount() = 2 //추가 하고 싶은 Fragment 갯수

    override fun createFragment(position: Int): Fragment {
        return when (position) {
            0 -> FirstFragment() // 각 Position 별 Fragment 연결
            else -> SecondFragment()
        }
    }
}

class NameOfFragmentStateAdapter(activity: AppCompatActivity) : FragmentStateAdapter(activity) { : FragmentStateAdapter를 상속받는 어댑터(클래스) 생성

override fun getItemCount() : 프래그먼트 갯수

verride fun createFragment(position: Int): Fragment { : 인덱스마다 보여줄 프래그먼트 설정

ViewPager2에 adapter 연결

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        with(binding) {
            setContentView(root)
            // viewPager에 FragmentStateAdapter를 상속해서 만든 Adapter 연결
            viewPager.adapter = NameOfFragmentStateAdapter(this@MainActivity)
        }
    }
}

페이징 시 인디케이터 닫기

// build.gradle
dependencies {
    implementation("me.relex:circleindicator:2.1.6")
<!-- activity_main.xml -->
<me.relex.circleindicator.CircleIndicator3
        android:id="@+id/indicator"
        android:layout_width="0dp"
        android:layout_height="20dp"
        android:layout_marginBottom="23dp"
        app:ci_drawable="@drawable/indicator_black_circle"
        app:ci_drawable_unselected="@drawable/indicator_gray_circle"
        app:ci_height="8dp"
        app:ci_margin="10dp"
        app:ci_width="8dp"
        app:layout_constraintBottom_toBottomOf="@+id/view_pager"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        with(binding) {
            setContentView(root)
            val adapter = NameOfFragmentStateAdapter(this@MainActivity)
            viewPager.adapter = adapter
            // 위의 코드 아래에 추가
            indicator.setViewPager(viewPager)
            adapter.registerAdapterDataObserver(indicator.adapterDataObserver)
        }

공부하며 느낀 점

  1. 프래그먼트라는건 결국 객체지향과 방향성이 비슷한것같다. 효율을 따지다보니 객체지향처럼 된건지 아니면 객체지향이라는게 이미 나온 이후에 코틀린이 나왔기 때문에 안드로이드 개발에도 이러한 방식이 추가된 건지는 모르겠지만, "각자의 역할을 수행하는 것들"로 조각 내서 관리하는 방법이 매우 효율적인 건 맞는 것 같다.

  2. 사용자가 볼때는 차이가 있다. 객체지향은 개발자의 유지보수를 좋게 하기 위한 것이며 사용자는 객체지향적으로 만들어진 프로그램과 아닌 것의 차이점을 알기 힘들다. 프래그먼트는 프로그래머의 유지보수 능력도 좋아지지만, 사용자가 보기에 편해진다는 특징이 있다,

profile
node 개발자

0개의 댓글