Android material : 탭 레이아웃

탭 레이아웃은 탭(tab)으로 구분하는 화면에서 탭 버튼을 배치하는 레이아웃이다. 탭 화면에는 탭 버튼을 선택했을 때 나와야 하는 내용이 있다. 탭 레이아웃은 이 중 탭 버튼을 다양하게 표시하고자 사용하는 뷰이다. 역시 플레이스토어 상단과 하단의 탭이 탭 레이아웃을 이용한 것들이다. 탭 버튼은 스크롤까지 제공한다. 탭 레이아웃은 각 화면별로 정렬을 하는 방식도 지정할 수 있다.

탭 레이아웃 사용하기

<?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="horizontal"
    tools:context=".MainActivity">
    
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    
    <FrameLayout
        android:id="@+id/tabContent"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

TabLayout으로 구성한 탭 버튼을 선택하면 FrameLayout 위치에 탭의 내용을 출력한다.

탭 버튼 만들기

동적으로 만들기

val tabLayout = binding.tabs
val tab1: TabLayout.Tab = tabLayout.newTab()
tab1.text = "Hello1"
tabLayout.addTab(tab1)

탭 레이아웃에 추가되는 탭 버튼은 TabLayout.newTab() 함수로 만들어지는 TabLayout.Tab 객체이며, 이 객체의 text 속성으로 문자열을, icon 속성으로 이미지를 지정한다. 그리고 이 Tab 객체를 TabLayout.addTab() 함수의 매개변수로 지정하여 탭 버튼을 추가한다.

정적으로 만들기

꼭 동적으로 탭 버튼을 만들 필요가 없다면, 레리아웃 XMl 파일의 TabItem 으로 정의해도 된다.

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

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="tab1" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="tab1" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="tab1" />

    </com.google.android.material.tabs.TabLayout>

정적인 탭과 동적인 탭을 섞어쓰면 어떻게 될까?

동적으로 추가한 탭이 정적으로 추가된 탭보다 뒤에 추가된다.

탭으로 화면 전환 구현하기

        tabLayout.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener {

            // 탭 버튼을 선택할 때 이벤트
            override fun onTabSelected(tab: TabLayout.Tab?) {
                val transaction = supportFragmentManager.beginTransaction()
                when(tab?.text) {
                    "tab1" -> transaction.replace(R.id.tabContent, OneFragment() )
                    "tab2" -> transaction.replace(R.id.tabContent, TwoFragment() )
                    "tab3" -> transaction.replace(R.id.tabContent, ThreeFragment() )
                }
                transaction.commit()
            }

            // 다른 탭 버튼을 눌러 선택된 탭 버튼이 해제될 때 이벤트
            override fun onTabUnselected(tab: TabLayout.Tab?) {
                TODO("Not yet implemented")
            }
            
            // 선택된 탭 버튼을 다시 선택할 때 이벤
            override fun onTabReselected(tab: TabLayout.Tab?) {
                TODO("Not yet implemented")
            }
        })
    }

탭 레이아웃 밑에 만들어뒀던 Frame레이아웃에 내용을 프레그먼트로 바꿔넣으므로써 탭이 화면 전환 역할을 하도록 한다. 이때 각 탭을 구분 짓는 것은 tab.text 가 하도록 했다.

탭 버튼 정렬하기

tablayout 에 여러 tabItem 이 존재할 경우 이들을 정렬하는 속성은 tabGravity 이다. 기본값은 fill 이며 탭 버튼을 가로로 등분해서 배치한다. start, center, fill 의값이 있다.

스크롤 설정하기

탭을 스크롤할 지 말지는 tabMode 로 정한다. 기본값은 fixed 로 scrollable 을 하면 좌우로 스크롤이 가능해진다. 이때 tabMode="scrollable" 로 하고 tabGravity="start" 로 되어있어야 tabItem 이 start 방향으로 정렬된다.

뷰 페이저 연동하기

탭 레이아웃으로 탭 화면을 만들면 뷰 페이저와 연동할 수 있다. 탭 레이아웃과 뷰 페이저는 모두 화면을 여러장 제공하는 용도이므로 둘다 연동하는 일이 많다.

탭 레이아웃 밑에 뷰페이저를 위치시킨다.

<?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.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:title="My ToolBar" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabMode="scrollable">

    </com.google.android.material.tabs.TabLayout>

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

</LinearLayout>

그 뒤 액티비티 코드에서 TabLayoutMediator 를 이용해서 TabLayout 과 ViewPager2 를 연동한다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val tabLayout = binding.tabs
        val viewPager = binding.viewpager

        TabLayoutMediator(tabLayout, viewPager) {tab, position ->
            tab.text = "Tab${(position + 1)}"
        }.attach()
    }
}

TabLayoutMediator 생성자의 매개변수로 연동하고자 하는 탭 레이아웃과 뷰 페이저 객체를 전달한다. 이렇게 하면 뷰 페이저의 화면이 3개이면 탭 버튼도 자동으로 3개가 나온다.

또한 사용자가 탭 버튼을 누르는 순간 뷰 페이저 화면이 자동으로 조정되며, 반대로 사용자가 뷰 페이저 화면을 넘기는 순간 탭 버튼도 자동으로 조정된다. 이렇게 뷰 페이저와 연동하면 탭 버튼의 문자열은 3번째 매개변수로 전달하는 함수에서 지정할 수 있다. 이 함수의 매개변수는 탭 버튼 객체(tab)와 화면에서 위치(position)이므로 이를 이용해 탭 버튼의 문자열 등을 지정하면 된다.

위의 코드대로 하면 안되고, 뷰 페이저는 어댑터가 연결되어야한다!

TabLayoutMediator 의 마지막 매개변수는 () 밖에 있는 람다함수이다. 코틀린 람다함수에 대해서는 람다와 고차 함수 포스팅에서 다뤘다.

좋은 웹페이지 즐겨찾기