[Android/Kotlin] TabLayout + ViewPager2 - Slide하여 화면 전환

Notepad96

·

2022. 8. 18. 23:35

300x250

 

 

1. 요약

 

결과

 

이번 글에서는 TabLayout과 ViewPager2를 사용하여서 Tab 할 수 있는 메뉴가 있으며 이를 클릭하면 화면이 전환되며, 마찬가지로 좌우로 Slide 하였을 시 화면이 전환되도록 구성하는 방법에 관하여 알아본다.

 

 

이를 위해서는 ViewPager의 Adapter와 각 Page를 구성하는 Fragment를 구현하여야 한다.

 

 

Tab 메뉴에서는 Text만을 사용할 수도 있으며 위 결과처럼 Text + Icon을 같이 사용하여 표현할 수 있다.

 

추가적으로 메뉴를 클릭하였을 경우 Text, Icon의 Color와 같이 스타일을 변경하기 위한 속성들과 방법에 대해서도 알아본다.

 

 

 

 

2. 레이아웃

 

2-1. activity_main.xml

메인 레이아웃으로서 ViewPager와 TabLayout으로 구성된다. TabLayout을 하단의 배치하였으며 ViewPager에서는 각 Tab 메뉴의 대한 화면을 Fragment로 나타낸다.

 

activity_main.xml

 

 

TabLayout은 Tab 메뉴를 나타내며 Text Color, Icon Color, Indicator Color 등을 적절하게 적용해주어야만 직관적으로 어느 페이지를 보고 있는지 스타일을 구성할 수 있으며 아래는 그 속성들을 정리한 내용이다.

 

background : Tab Layout Color
tabRippleColor : 탭 메뉴 클릭 중 보이는 백그라운드 Color
tabIconTint : 탭 아이템 아이콘 Color
tabGravity : 탭 메뉴간 배치, fill일 경우 일정 간격으로 화면을 채움 [fill | start | center] 

= Text =
tabTextColor : 탭 텍스트 Color
tabSelectedTextColor : 선택된 탭 메뉴 텍스트 Color

= Indicator =
tabIndicatorColor : 선택된 탭 표시하는 지표[Indicator](안내 바) Color
tabIndicatorHeight : Indicator 높이(크기) 지정
tabIndicatorGravity : Indicator 위치 지정(Default : Bottom)

여기서 Indicator는 Tab 메뉴에서 보이는 안내 바를 나타내며 Gravity를 통하여 위치를 지정할 수 있다.

 

Gravity의 Default 값은 Bottom으로 아래쪽으로 되어 있으며, 해당 레이아웃에서는 Tab 메뉴가 아래쪽에 배치되어 있기 때문에 위쪽에 안내 바가 위치한 것이 더욱 자연스럽게 보인다.

 

만약 Tab 메뉴가 위쪽에 있다면 아래쪽에 배치하는 것이 더욱 자연스럽게 보일 것이다. (Tab 메뉴를 위쪽에 배치하기 위해서는 Layout을 TabLayout, ViewPager2 순서로 구성하면 된다.)

 

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager01"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout01"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:background="#F6E4E4"
        app:tabRippleColor="#FF1313"
        app:tabGravity="fill"

        app:tabIconTint="@color/menu_selected"
        app:tabTextColor="@color/menu_selected"

        app:tabIndicatorColor="@color/select"
        app:tabIndicatorHeight="5dp"
        app:tabIndicatorGravity="top"
        />

</LinearLayout>

 


 

여기서 속성을 보면 Text Color와 달리 Icon Color 같은 경우 Selected 되었을 경우 설정하는 속성이 없다. 따라서 이상태로는 Icon의 Color는 선택이 되었던 되지 않았던가 상관없이 처음 설정한 상태로만 보이게 된다.

 

따라서 이를 간단하게 해결하기 위해서는 Drawable/color의 Color 파일을 생성해주어야 한다.

 

● menu_selected.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/select" android:state_selected="true" />
    <item android:color="@color/unselect" />
</selector>

코드를 보면 state_selected = true로서 선택되어 있을 경우 select의 Color를 그렇지 않을 경우 unselect의 Color를 보여주도록 정의되어 있다.

 

(color 파일이 없을 경우 "res 우클릭 > New > Android Resource Directory"로 생성)

 


 

2-2. fragment_page01.xml (02, 03 구성 동일)

Fragment는 3개의 각 메뉴에서 보여질 화면으로 동일한 구성으로 Page01, 02, 03 이름으로 3개의 Fragment를 생성하였다.

 

레이아웃은 구분하기 편하도록 각각 다른 Background Color, Text, Icon을 사용하여 각각 구성하였다.

 

fragment_page01.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:gravity="center"
    android:orientation="vertical"
    android:background="#5f00"
    tools:context=".Page01">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Page 01"
        android:textSize="32sp" />

    <ImageView
        android:layout_width="300px"
        android:layout_height="300px"
        android:src="@drawable/icon_profile"
        />

</LinearLayout>

 

 

 

 

3. 코드 및 설명

 

3-1. Page01.kt (02/03)

Fragment 파일에서는 Layout을 Binding 해주었을 뿐 추가적인 코드 작성은 하지 않았다.

 

package com.notepad96.tablayout

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.notepad96.tablayout.databinding.FragmentPage01Binding

class Page01 : Fragment() {
    lateinit var binding: FragmentPage01Binding

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

        return binding.root
    }

}

 


 

3-2. ViewPagerAdapter.kt

ViewPager의 Adapter로서 getItemCount 함수서는 Tab 메뉴의 개수를 반환하며 createFragment 함수에서는 메뉴의 순서대로 보여줄 레이아웃인 Fragment를 반환해 준다.

 

package com.notepad96.tablayout

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

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

    override fun getItemCount(): Int = 3

    override fun createFragment(position: Int): Fragment {
        return when(position) {
            0 -> Page01()
            1 -> Page02()
            else -> Page03()
        }
    }
}

 


 

3-3. MainActivity.kt

tabTextList와 tabIconList는 각각 Tab 메뉴에서 보여줄 Text와 Icon의 리스트이다. Icon의 경우 Android Studio서 지원해주는 Icon을 사용하였으며 "New > Vector Asset"을 통하여 추가 가능하다.

 

ViewPager의 adapter의 앞서 생성하였던 ViewPagerAdapter로 초기화해준다.

 

TabLayoutMediator를 사용하여서 각 tab의 Text와 Icon을 매칭 시킨 후 attach 하면 TabLayout과 ViewPager2를 활용하여 Slide 되며 Tab 메뉴가 있는 레이아웃이 만들어진다.

 

package com.notepad96.tablayout

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.tabs.TabLayoutMediator
import com.notepad96.tablayout.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private val binding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    private val tabTextList = listOf("Profile", "Search", "Setting")
    private val tabIconList = listOf(R.drawable.icon_profile, R.drawable.icon_search, R.drawable.icon_setting)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        binding.viewPager01.adapter = ViewPagerAdapter(this)

        TabLayoutMediator(binding.tabLayout01, binding.viewPager01) { tab, pos ->
            tab.text = tabTextList[pos]
            tab.setIcon(tabIconList[pos])
        }.attach()
    }
}

 

 

 

 

4. 전체 파일

 

 

GitHub - Notepad96/BlogExample02

Contribute to Notepad96/BlogExample02 development by creating an account on GitHub.

github.com

 

 

300x250