[Android] 5(5). 뷰 페이저와 탭 레이아웃

5 minute read


뷰 페이저와 탭 레이아웃

스마트폰에서 가장 많이 사용되는 메뉴의 형태는 탭이나 스와이프로 화면을 전환하는 형태입니다.

안드로이드에서는 스와이프로 화면을 전환할 수 있도록 컨테이너인 뷰페이저를 제공하고, 탭 메뉴 구성을 위해 탭 레이아웃을 제공합니다.


뷰 페이저에서 프래그먼트 사용하기


탭 메뉴와 함께 4개의 화면을 프래그먼트로 구성하는 방법에 대해 알아봅니다.

뷰 페이저의 아이템으로 프래그먼트를 사용한다는 것은 각 화면에 독립적인 것을 보여준다는 의미입니다.


프래그먼트 4개 생성

1. 프래그먼트 생성

[app] - [java] - 패키지명 우클릭 - [New] - [Fragment] - [Fragment(Blank)] 에서 FragmentA, FragmentB, FragmentC, FragmentC의 이름으로 4개의 프래그먼트를 생성합니다.

그리고 각 4개의 프래그먼트에 대해 텍스트뷰를 가운데에 배치하고 text 속성을 각각 ‘프래그먼트 A/B/C/D’ 로 지정합니다.


뷰 페이저 배치 및 소스코드 연결

뷰페이저는 리사이클러뷰와 구현 방식이 비슷한데, 한 화면에 하나의 하나의 아이템만 보이는 리사이클러뷰라고 생각하면 됩니다.

2. xml 파일에 뷰 페이저 배치

팔레트의 Containers 카테고리에 있는 ViewPager2 를 드래그해서 추가하고 컨스트레인트를 모두 연결합니다.

id에는 ‘viewPager’를 입력합니다.


프래그먼트 어댑터 생성

3. 프래그먼트 어댑터 생성

FragmentAdapter라는 클래스를 하나 생성합니다.

FragmentAdapter 는 프래그먼트를 담을 수 있는 FragmentStateAdapter를 상속받습니다. 상속 받은 후에 FragmentAdapter 아래에 빨간 줄이 생기는데, 글자를 클릭 후 [Alt + Enter] 를 눌러 목록에서 [Add constructor parameters … (FragmentActivity)] 를 선택해 생성자를 추가합니다.

// 뷰 페이저와 연결하기 위한 프래그먼트 어댑터
class FragmentAdapter(fragmentActivity: FragmentActivity) :
                      FragmentStateAdapter(fragmentActivity) {

}


4. 프래그먼트 어댑터 구현

FragmentAdapter 클래스 스코프 내에서 [Ctrl + I] 를 눌러 총 2개의 메서드를 구현합니다.

class FragmentAdapter(fragmentActivity: FragmentActivity) :
                      FragmentStateAdapter(fragmentActivity) {

    // 리사이클러뷰 어댑터처럼 페이저어댑터도 화면에 표시할 아이템의 목록 필요
    // 뷰 페이저의 화면 아이템은 대부분 중간에 개수가 늘거나 줄지 않고
    // 처음 정해진 개수대로 사용하기 때문에 listOf 메서드가 효율적
    var fragmentList = listOf<Fragment>()

    // 어댑터가 화면에 보여줄 전체 프래그먼트의 개수를 반환해야 합니다.
    override fun getItemCount(): Int {
        return fragmentList.size
    }

    // 현재 페이지의 position이 파라미터로 넘어옵니다. position에 해당하는
    // 위치의 프래그먼트를 만들어서 안드로이드에 반환해야 합니다.
    override fun createFragment(position: Int): Fragment {
        // 페이지가 요청될 때 getItem으로 요청되는 페이지의 position 값이 넘어옴.
        // position 값을 이용해서 프래그먼트 목록에서 해당 position에 있는 프래그먼트
        // 1개를 리턴함.
        return fragmentList.get(position)
    }

}
  • fragmentList: 프래그먼트 목록 프로퍼티 생성.
  • getItemCount(): 전체 프래그먼트 개수 반환.
  • createFragment(): 페이지가 요청될 때 넘어오는 position 값을 이용해 해당 position에 있는 프래그먼트 1개를 반환.


프래그먼트 어댑터와 뷰 페이저 연결

이제 생성된 프래그먼트와 어댑터를 MainActivity.kt 에서 연결합니다. 먼저 바인딩 전달부터 완료합니다.


5. 프래그먼트 목록 변수 생성

메인 액티비티에서는 항상 어댑터에 사용할 목록 변수를 넘겨줘야 합니다.

val fragmentList = listOf(FragmentA(), FragmentB(), FragmentC(), FragmentD())


6. 어댑터 생성하고 목록 변수 전달

프래그먼트어댑터를 생성하고 앞에서 만든 목록 변수를 넘겨줍니다.

어댑터의 첫번째 파라미터에는 항상 supportFragmentManager(여기서는 this)를 사용합니다.

val adapter = FragmentAdapter(this)
adapter.fragmentList = fragmentList


7. 뷰 페이저와 프래그먼트어댑터 연결

레이아웃의 뷰 페이저에 프래그먼트 어댑터를 연결(전달)합니다.

binding.viewPsger.adapter = adapter


에뮬레이터를 실행해보면 양 옆으로 스와이프 되는 뷰 페이저를 확인할 수 있습니다.


탭 레이아웃 적용

이제 앞에서 만든 화면의 상단에 탭 메뉴를 배치하고 탭 메뉴 클릭 시 해당 프래그먼트로 이동하는 코드를 작성합니다.


1. 레이아웃에 TabLayout 배치

레이아웃 파일에 팔레으의 Container에 있는 TabLayout을 뷰 페이저의 위쪽에 배치합니다. 뷰 페이저의 위쪽 컨스트레인트를 삭제한 후 작업하는 것이 편합니다.


2. 탭 레이아웃 목록 변수 생성

탭 레이아웃을 생성할 때도 마찬가지로 목록 변수를 전달해줘야 합니다.

val tabTitles = listOf<String>("A", "B", "C", "D")


3. 탭레이아웃과 뷰페이저 연결

탭 레이아웃과 뷰 페이저를 상호 연결시킬 때는 TabLayoutMediator를 사용합니다.

다음의 코드로 연결하니 아래 코드는 외워두는 것이 좋습니다.

코드 블록의 끝에서는 attach( ) 메서드를 호출해야 적용이 됩니다.

TabLayoutMediator(binding.tabLayout, binding.viewPager){ tab, position ->
    tab.text = tabTitles[position]
}.attach()


[결과 화면]

image-20210808182517352



뷰 페이저에서 뷰 사용하기


앞에서 뷰 페이저의 아이템으로 프래그먼트를 사용한다는 것은 각 화면이 독립적으로 구성되어야 한다는 것을 의미한다고 했습니다.

이에 반해 아이템이 반복적으로 동일한 구조의 텍스트나 이미지를 보여주는 용도(사진 갤러리 앱 등)라면 프래그먼트보다는 를 사용합니다.


아이템 레이아웃 생성

1. 아이템 레이아웃 생성

[res] - [layout] - [New] - [Layout Resource File] 에서 File name에 ‘item_viewPager’라고 입력하고 레이아웃 파일을 생성합니다.

레이아웃의 한 가운데 텍스트뷰를 하나 가져다 놓고 모든 컨스트레인트를 연결합니다. text 는 ‘여기 제목’, id는 ‘textView’를 입력합니다.


뷰 어댑터와 뷰 홀더 생성

뷰 페이저에서 아이템으로 뷰를 사용할 때는 리사이클러뷰 어댑터를 상속하는 어댑터를 생성합니다. 뷰페이저에 리사이클러뷰 어댑터를 사용하면 기존에 세로로 출력되는 것을 가로로 출력된다고 생각하면 이해가 쉽습니다.


2. CustomPagerAdapter와 Holder 생성

CustomPagerAdapter 클래스를 생성합니다.

CustomPagerAdapter 클래스는 RecyclerView.Adapter< Holder >를 상속합니다.

CustomPagerAdapter 클래스 아래에 Holder 클래스를 생성합니다.

Holder 클래스는 RecyclerView.viewHolder 클래스를 상속합니다. Holder의 파라미터로는 연결할 레이아웃 바인딩을, ViewHolder의 파라미터로는 binding.root를 전달합니다.

/*리사이클러 뷰를 사용하는 방법과 같습니다. 뷰페이저에 리사이클러뷰 어댑터를 사용하면
* 기존에 세로로 출력되는 것을 가로로 출력되도록 해준다고 생각하면 이해하기 쉽습니다.
* */

// 뷰 어댑터
class CustomPagerAdapter: RecyclerView.Adapter<Holder>() {

}

// 뷰 홀더 클래스
class Holder(val binding: ItemViewpagerBinding): RecyclerView.ViewHolder(binding.root){

}


4. 뷰 홀더 구현

뷰 어댑터에서는 뷰 홀더의 메서드를 호출하기 때문에 뷰 홀더를 먼저 구현합니다.

여기서는 간단하게 뷰(레이아웃)의 텍스트 뷰에 텍스트를 설정하는 setText( ) 하는 메서드를 하나 생성합니다.

class Holder(val binding: ItemViewpagerBinding): RecyclerView.ViewHolder(binding.root){
    // item_viewpager 레이아웃의 미리 만들어둔 텍스트뷰에 깂을 입력하는 코드
    fun setText(text: String){
        binding.textView.text = text
    }
}


4. 뷰 어댑터 구현

마찬가지로 뷰 어댑터를 구현합니다. 사용할 목록 변수를 생성하고, 메서드를 오버라이딩 해야 합니다.

class CustomPagerAdapter: RecyclerView.Adapter<Holder>() {

    // 어댑터에서 사용할 목록 변수
    // MainActivity에서 어댑터를 생성한 후 textList 변수로 각각의 페이지에서 보여줄 텍스트를 전달
    var textList = listOf<String>()
  
    // 바인딩을 생성한 후 Holder에 전달
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = ItemViewpagerBinding.inflate(LayoutInflater.from(parent.context),
                                                    parent, false)
        return Holder(binding)
    }
		
    // Holder에 만들어둔 setText 메서드를 호출하여 화면에 출력
    override fun onBindViewHolder(holder: Holder, position: Int) {
        val text = textList[position]
        holder.setText(text)
    }
		
    // 아이템 목록의 개수 반환
    override fun getItemCount(): Int {
        return textList.size
    }
}
  • textList: 뷰의 텍스트 뷰에 입력할 텍스트 목록 변수
  • onCreateViewHolder(): 뷰 홀더를 생성할 때 호출. 뷰(레이아웃)를 바인딩하여 뷰 홀더에 전달한다.
  • onBindViewHolder(): 특정 뷰가 화면에 나타날 때 호출. 뷰가 나타나기 전에 전처리해야 하는 작업을 수행한다. 여기서는 간단히 Holder 클래스의 setText() 메서드를 호출하여 텍스트를 화면에 출력한다.
  • getItemCount(): 아이템 목록의 개수를 반환한다.


뷰 어댑터와 뷰 페이저 연결

프래그먼트를 사용할 때와 마찬가지로 MainActivity.kt 에서 뷰 어댑터와 뷰 페이저를 연결합니다.

바인딩을 설정한 후 진행합니다.


5. 프래그먼트 목록 변수 생성

메인 액티비티에서는 항상 어댑터에 사용할 목록 변수를 넘겨줘야 합니다.

// 어댑터에 전달할 목록 변수 생성
val textList = listOf("View A", "View B", "View C", "View D")


6. 어댑터 생성하고 목록 변수 전달

뷰어댑터를 생성하고 앞에서 만든 목록 변수를 넘겨줍니다.

// 커스텀 어댑터 생성
val customAdapter = CustomPagerAdapter()
// 데이터 전달
customAdapter.textList = textList


7. 뷰 페이저와 뷰 어댑터 연결

binding.viewPager.adapter = customAdapter


탭 레이아웃 적용

프래그먼트를 사용할 때와 동일합니다.

1. 레이아웃에 TabLayout 배치

2. 탭 레이아웃 목록 변수 생성

3. 탭레이아웃과 뷰페이저 연결


[결과 화면]

image-20210808185904310

정리


  • 뷰 페이저는 여러 화면을 스와이프 형태로 넘길 수 있도록 해줍니다. 안드로이드에서는 스와이프로 화면을 전환할 수 있도록 컨테이너인 뷰페이저를 제공하고, 탭 메뉴 구성을 위해 탭 레이아웃을 제공합니다.

  • 뷰 페이저의 아이템으로는 프래그먼트 또는 뷰를 사용할 수 있습니다. 프래그먼트는 각 아이템의 화면이 독립적으로 구성되어야 할 때 사용하고 뷰는 각 화면의 구성이 동일할 때 사용합니다.

  • 뷰 페이저를 사용하는 과정은 다음과 같습니다.

    1. 레이아웃(프래그먼트) 생성
    2. 뷰 어댑터(프래그먼트 어댑터) 생성
    3. 뷰 어댑터와 뷰 홀더(프래그먼트 어댑터) 구현
    4. 소스코드에서 어댑터와 뷰 페이저 연결
    5. (탭 레이아웃 적용)

Categories:

Updated:

Leave a comment