티스토리 뷰

Android App Coding

탭레이아웃 TabLayout

IT Knowledge Share 2021. 7. 9. 14:16
반응형

앱 위쪽에 탭이 있고, 탭을 클릭 시 아래나 위쪽의 화면이 전환되는 뷰를 본적이 있으시죠?

 

이러한 기능은 탭레이아웃(TabLayout)을 통해서 구현이 가능합니다.

 

탭레이아웃을 사용 시 Adapter와 Pager가 같이 사용됩니다. 

- TabLayout: 탭레이아웃을 구성하는 역할로 탭을 담당합니다.

- Adapter: 페이저를 위해 필요하며, 페이지를 연달아 보여줄 수 있도록 합니다.

- Pager: 탭 아래 화면을 넘겨주는 역할을 합니다.

 

이렇게 세 가지가 같이 상호작용으로 사용되며, 리스너를 달아서 탭레이아웃과 페이저를 서로 연결시켜 주면 됩니다.

 

그럼 탭레이아웃을 어떻게 사용하는지 알아봅니다.

 

먼저 액티비티를 만들어줍니다. 액티비티가 생성되면, 레이아웃으로 이동후, ViewPager와 TabLayout을 하위 뷰로 넣어줍니다. TabLayout은 기본으로 제공하지 않는 뷰이므로, Build.gradle의 모듈 단위에서 implemetation을 해줘야 합니다.

  
<?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=".TabActivity">


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


    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

TabLayout의 implementation 코드는 아래와 같습니다.

반응형
build.gradle(Module app) 부분

dependencies {
	implementation 'com.google.android.material:material:1.0.0'
}

 

레이아웃이 준비되면, 페이저를 리스트뷰로 나오도록 도와주는 어댑터(adapter) 클래스를 코딩합니다.

액티비티 파일에 새로운 어댑터 클래스를 만들어 줍니다.

 

아래 코드를 보면, FragPagerAdapter 어댑터 클래스는 fragmentManager, tabCount 파라미터를 받으며, FragmentStatePagerAdapter( )를 상속받습니다. 그리고 ( ) 부분에 fragmentManager를 넘겨주게 됩니다. FragmentStatePagerAdapter( )를 상속받기 때문에 리스트뷰가 프래그먼트로 구성되어 나타낸다는 것을 의미합니다. 부모의 메소드를 오버라이드 시키기 위해, getItem, getCount 메소드를 implement 시켜 줍니다.

 

getItem은 아이템 하나를 찾는 경우 호출되는 메소드입니다. 아이템 리스트 중에서 아이템 하나를 리턴해주면 되는 식이므로, if 문이나 when 조건절을 이용합니다. getItem의 리턴 타입은 Fragment 이므로, 포지션에 해당하는 프래그먼트를 리턴해줘야 합니다. 따라서, when (position) 이 0, 1, 2, else 일 때의 경우, Fragment 1~3을 리턴합니다.

 

getCount는 사이즈를 리턴해주면 되므로, 미리 선언해준 Int 형의 tabCount를 리턴하도록 합니다.

class FragmentPagerAdapter(
    fragmentManager: FragmentManager,
    val tabCount: Int
) : FragmentStatePagerAdapter(fragmentManager) {
    override fun getItem(position: Int): Fragment {
        when (position) {
            0 -> {
                return Fragment1()
            }
            1 -> {
                return Fragment2()
            }
            2 -> {
                return Fragment3()
            }
            else -> return Fragment1()
        }

    }

    override fun getCount(): Int {
        return tabCount
    }
}

 

리턴으로 쓰였던 프래그먼트 액티비티 3개를 만들어 줍니다. inflater는 뷰를 그리는 역할을 하며, container는 부모 뷰를 의미합니다. attachToRoot는 지금 바로 뷰에 붙일거냐는 의미인데, 당장은 아니기에 false로 둡니다. 나머지 프래그먼트도 똑같이 만들어줍니다.

* Fragment1.kt

package com.example.myapplication

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment


class Fragment1 : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }
}
* Fragment2.kt

package com.example.myapplication

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment


class Fragment2 : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_two, container, false)
    }
}

 

* Fragment3.kt

package com.example.myapplication

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment


class Fragment3 : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_three, container, false)
    }
}

 

프래그먼트 액티비티가 준비되면, 각각에 해당하는 fragment_one ~ three 레이아웃을 만들어 줍니다. 동일한 코드에 배경색만 다르게 하여 만들어줬습니다. 다른 아이디의 black, white, yellow의 색상의 프래그먼트를 준비합니다.

* fragment_one.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/Black"
    android:orientation="vertical">


    <Button
        android:id="@+id/frag_one"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="프래그먼트1 " />

</LinearLayout>
* fragment_two.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/White"
    android:orientation="vertical">


    <Button
        android:id="@+id/frag_two"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="프래그먼트2 " />

</LinearLayout>
* fragment_three.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/Yellow"
    android:orientation="vertical">


    <Button
        android:id="@+id/frag_three"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="프래그먼트3 " />

</LinearLayout>

 

이후로 탭레이아웃과 페이저를 연결시켜주는 작업이 필요합니다. 액티비티에서 먼저 레이아웃의 탭레이아웃 뷰 id값인 tab_layout을 가져와 설정해줍니다. 액티비티의 onCreate 부분에서 addTab을 이용해, 원하는 수 만큼의 탭을 추가합니다. setText에 Number 1~3을 넣어서 3개의 탭을 추가했습니다.

 

onCreate에서 사용될 어댑터를 생성해줍니다. val pagerAdapter = FragPagerAdapter(supportFragmentManager, 3) 부분처럼, 파라미터에 supportFragmentManager를 넣고, 탭이 세 개이므로 3을 넣습니다. 그리고 레이아웃에서 만들었던 view_pager에 어댑터를 할당시켜 줍니다. 위에서 생성한 pagerAdapter가 view_pager의 어댑터로 들어갑니다.

 

그리고 탭레이아웃과 아래의 페이지를 연동해주는 작업이 필요합니다. tab_layout.addOnTabSelectedListener( ) 부분을 보면, 탭이 선택되었을 때 페이지가 바꾸도록 도와주는 리스너인데, 파라미터로 object : TabLayout.OnTabSelectedListener를 넣어줍니다. 그리고 메소드를 implement 하여, onTabReselected, onTabUnselected, onTabSelected를 완성합니다. 위의 메소드는 말그대로 탭이 다시 선택되었을 때, 선택되지 않을 때, 선택되었을 때를 의미합니다.

 

저희는 탭이 선택되는 상황을 나타내고 싶으므로, onTabSelected 부분에 코드를 작성합니다.

setCurrentItem 부분에는 널값이 오지 못하므로, tab.position 부분을 tab!!.position으로 해줘야 합니다. !!는 널값이 될 수 없다는 것을 단언하는 연산자입니다.

 

마지막으로 탭을 이동시킬 때, 같이 페이지가 이동시켜질 수 있도록, 코드를 작성합니다.

view_pager에 addOnPageChangeListener 리스너를 달아줍니다. 리스너에는 TabLayoutOnPageChangeListener를 넣어주고, 인자로 들어간 리스너의 안에는 초반 탭레이아웃의 id값인 tab_layout을 할당합니다.

마지막의 이 코드가 없다면, 탭이 넘어갈 때 페이지가 같이 넘어가지 않습니다.

class TabActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_tab)


        tab_layout.addTab(tab_layout.newTab().setText("Number1"))
        tab_layout.addTab(tab_layout.newTab().setText("Number2"))
        tab_layout.addTab(tab_layout.newTab().setText("Number3"))

        val pagerAdapter = FragPagerAdapter(supportFragmentManager, 3)
        view_pager.adapter = pagerAdapter

        tab_layout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabReselected(tab: TabLayout.Tab?) {
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
            }

            override fun onTabSelected(tab: TabLayout.Tab?) {
                view_pager.setCurrentItem(tab!!.position)
            }
        })


        view_pager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tab_layout))


    }
}

 

반응형

'Android App Coding' 카테고리의 다른 글

안드로이드 데이터베이스(DB) Shared Preference  (0) 2021.07.10
탭레이아웃 2편 TabLayout  (0) 2021.07.09
리사이클러뷰 RecyclerView  (0) 2021.07.09
뷰 홀더 View Holder  (0) 2021.07.09
토스트 메시지 Toast  (0) 2021.07.09
댓글