본문 바로가기
공부/안드로이드

Custom TabLayout

by 단순한 프로그래머 2025. 9. 24.

TabLayout과 ViewPager2를 연결하고 각 탭의 레이아웃과 디자인을 임의로 바꾸는 샘플이다.

 

결과이미지

 


 

탭에 사용된 이미지

각 탭별로 선택되었을 때와 선택되지 않았을 때의 이미지를 사용하였고

각 탭별로 이미지 selector와 text컬러를 위한 selector를 구성하였다.

 

res / drawable / selector_tab0_icon.xml 

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

 

res / color / selector_tab_text.xml

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

 


 

Fragment 생성

상속으로 Fragment를 보다 쉽게 생성하기 위해 FragmentBase를 만들고

FragmentA, FragmentB, FragmentC는 상속을 받아 생성하였다.

 

FragmentBase.kt

open class FragmentBase(val _strName:String) : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_base, container, false)
    }
}

 

FragmentA.kt 나머지 동일하고 레이아웃에서 배경색상만 바꿔줬다.

class FragmentA(_strName:String) : FragmentBase(_strName) {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_a, container, false)
    }
}

 


AdapterTab

ViewPager2에 설정되는 Fragment리스트를 가지는 아답터이다.

class AdapterTab(val fragmentList:List<FragmentBase>, fragmentActivity: FragmentActivity)
    : FragmentStateAdapter(fragmentActivity)
{
    override fun getItemCount() = fragmentList.size
    override fun createFragment(position: Int) = fragmentList.get(position)
}

 


ActivityMain의 레이아웃 구성

ViewPager2와 TabLayout 만으로 구성하였으며

탭의 선택된 바의 표시가 상단에 보이도록

tabIndicatorGravity="top"으로 설정하였으며

tabMode는 동일한 사이즈로 화면에 배치되기 위해 fixed로 설정하였다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".ActivityMain">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/tabLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="0dp"
        android:layout_height="100dp"
        app:tabBackground="@color/white"
        app:tabMode="fixed"
        app:tabGravity="fill"
        app:tabIndicatorGravity="top"
        app:tabIndicatorHeight="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">
    </com.google.android.material.tabs.TabLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 


 

탭의 레이아웃

res / layout / tab_custom.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="100dp"
    android:id="@+id/cl_base">

    <ImageView
        android:id="@+id/iv_tab"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_marginTop="5dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/tv_tab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="TextView"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@+id/iv_tab"
        app:layout_constraintStart_toStartOf="@+id/iv_tab"
        app:layout_constraintTop_toBottomOf="@+id/iv_tab" />
</androidx.constraintlayout.widget.ConstraintLayout>

 


ActivityMain

1. Fragment 리스트를 생성

2. 아답터를 생성

3. 아답터와 뷰페이저를 연결

4. 뷰페이저의 수만큼 탭 추가

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

 

위 순서대로 처리하고 처음 실행했을 때 설정탭이 선택되도록 하였다.

class ActivityMain : AppCompatActivity() {

    val _binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
    lateinit var _listFragments:List<FragmentBase>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(_binding.root)

        //.// 1. 페이지 데이터를 로드
        _listFragments = listOf(FragmentA("홈"), FragmentB("프로필"), FragmentC("설정") )

        //.// 2. 아답터 생성
        val pagerAdapter = AdapterTab(_listFragments, this)

        //.// 3. 아답터와 뷰페이저 연결
        _binding.viewPager.adapter = pagerAdapter

        //.// 4. 뷰페이저의 수만큼 탭 추가
        for (i in _listFragments.indices) { _binding.tabLayout.addTab(_binding.tabLayout.newTab()) }

        //.// 5. 탭레이아웃과 뷰페이저 연결
        TabLayoutMediator(_binding.tabLayout, _binding.viewPager, true){ tab: TabLayout.Tab, i: Int ->
            val customView = LayoutInflater.from(this).inflate(R.layout.tab_custom, null)
            val tabText = customView.findViewById<TextView>(R.id.tv_tab)
            val tabIcon = customView.findViewById<ImageView>(R.id.iv_tab)
            tabText.text = _listFragments[i]._strName
            tabText.setTextColor(resources.getColorStateList(R.color.selector_tab_text))
            when(i){
                0 -> tabIcon.setImageResource(R.drawable.selector_tab0_icon)
                1 -> tabIcon.setImageResource(R.drawable.selector_tab1_icon)
                2 -> tabIcon.setImageResource(R.drawable.selector_tab2_icon)
            }
            tab.setCustomView(customView)
            customView.setTag(i)
        }.attach()

        _binding.tabLayout.addOnTabSelectedListener(object:OnTabSelectedListener{
            //.// 탭선택시 이벤트 처리
            override fun onTabSelected(tab: TabLayout.Tab?) {
                tab?.customView?.let {
                    val nIndexFragment = it.getTag() as Int
                    //Toast.makeText(this@ActivityMain, _listFragments[nIndexFragment]._strName, Toast.LENGTH_SHORT).show()
                }
            }

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

        _binding.tabLayout.getTabAt(2)?.select()
    }
}

 


<< 이것이 안드로이드다 with 코틀린 >> 참고하여 작성

'공부 > 안드로이드' 카테고리의 다른 글

Cutom TabLayout - ToggleButton  (0) 2025.09.27
Custom TabLayout - Background, Indicator  (0) 2025.09.26
Custom View  (0) 2025.09.21
Fragment 전환 및 초기 데이타 전달  (0) 2025.09.19
Fragment 간 데이타 전달  (0) 2025.09.18