안드로이드 스터디

뷰페이저2를 만들어 보고 탭 레이아웃과 연동시켜보자!

mky 2025. 11. 1. 22:31

설명할 미션 : 앨범 화면(AlbumFragment)에 TabLayout + Viewpager2를 구현하고 Viewpager2 에 DetailFragment, VideoFragment 연결하기

 

우선 어댑터 클래스를 만든다. 일단 전체 코드를 보자.

package com.example.flo.albumfrag

import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.example.flo.albumfrag.VideoFragment
import com.example.flo.dataclasses.Album
import com.example.flo.albumfrag.DetailFragment
import com.google.gson.Gson

//수록곡 전달을 위해 인자 하나 더 추가 -> 나 이거 왜써놓은거지 ㅋㅋ
class AlbumVPAdapter(fragment: Fragment, private val album: Album) : FragmentStateAdapter(fragment){

    override fun getItemCount(): Int = 3

    override fun createFragment(position: Int): Fragment {
        return when(position) {
            0 -> SongFragment().apply {
                arguments = Bundle().apply {
                    val albumJson = Gson().toJson(album)
                    putString("album", albumJson)
                }

            }
            1 -> {
                DetailFragment()
                }
            else -> VideoFragment()
        }
    }

}

 

차근차근 분석해보자.

class AlbumVPAdapter(fragment: Fragment) : FragmentStateAdapter(fragment)

클래스를 만들고 인자로 프래그먼트를 넣는다. FragStateAdapter이라는 추상 클래스를 상속 받아 어댑터 클래스로서의 역할을 하게 만든다.

override fun getItemCount(): Int = 3

    override fun createFragment(position: Int): Fragment {
        return when(position) {
            0 -> SongFragment().apply {
                arguments = Bundle().apply {
                    val albumJson = Gson().toJson(album)
                    putString("album", albumJson)
                }

            }
            1 -> {
                DetailFragment()
                }
            else -> VideoFragment()
        }
    }

일단 저기서 데이터 전달을 다루는 부분은 무시하자. FragStateAdapter이라는 추상 클래스를 상속받았으므로 getItemCount(): Int와 createFragment(position: Int): Fragment 라는 2개의 메서드를 필수로 구현해주어야한다. 뷰페이저에 들어갈 프래그먼트가 총 3개이므로 getItemCount는 3을 반환하도록 한다. 그리고 createFragment메서드는 when문을 사용해 position값에 따라 반환할 프래그먼트 객체를 다르게 한다.

  • position : 뷰페이저의 페이지 번호라고 생각하면 된다, 처음에 getItemCount를 활용해서 뷰 페이저의 페이지 수를 지정하면, 내부적으로 뷰페이저의 첫번째 페이지는 position=0, 두번째는 position=1 세번째는 position=2로 지정된다. 그리고 createFragment메서드에서 when문을 사용해 position=0인 페이지, 즉 첫번째 페이지를 보여지게 할 때 그 페이지에 SongFragment가 나오도록 하는 것이다.

각 페이지에 들어갈 프래그먼트들은 제작해 놓았다고 가정하고 이제 AlbumFragment.kt로 가보자. 여기서 다음과 같은 코드를 작성한다.

//AlbumFragment.kt

val albumAdapter = AlbumVPAdapter(this, album)
binding.albumContentVp.adapter = albumAdapter

이런 식으로 프래그먼트에 어댑터를 등록해 준다.

 

이제 탭 레이아웃과 뷰페이저를 연동해 보자. fragment_album.xml에 탭레이아웃을 만들어 놓았다고 가정한다.

AlbumFragment.kt에 다음과 같은 코드를 작성한다.

//AlbumFragment.kt

private val information = arrayListOf("수록곡", "상세정보", "영상")
//중략
TabLayoutMediator(binding.albumContentTb, binding.albumContentVp) { tap, position ->
            tap.text = information[position]
        }.attach()
  • TabLayoutMediator : 탭레이아웃과 뷰페이저를 연결하는 중재자 클래스. 탭이 선택될 때 뷰페이지를 동기화하고 뷰페이지가 선택될 때 탭레이아웃을 동기화. 인자를 총 3개 받는데, 탭레이아웃 객체, 뷰페이저객체, 탭과 포지션을 어떻게 연결할지 정의하는 함수 이렇게 3개를 받는다. 3번째 인자는 람다식으로 썼다.
  • 람다식 내부는 각 탭이 어떤 텍스트를 가질 지 설정하는 부분이다. position은 위에 설명한 것처럼 뷰페이저의 페이지번호이고, 탭은 해당 위치의 탭 객체이다. 위에 arraylist인 information을 생성해 놓았는데, 여기서 각 탭의 텍스트로 information[position] 값을 설정한다.
  • 그 뒤 TabLayoutMediator의 attach()메서드를 호출하여 탭과 뷰페이저를 연결한다.