본문 바로가기
KUIT-앱 개발 프로젝트 동아리

5주차 실습(2)-TabLayout

by 농농씨 2023. 10. 27.

지난시간까지는 뷰페이저에서 Glide 라이브러리로 사진 불러오기 햇음

이번시간에는 TabLayout 연습할 예정.

1. 프로젝트 만들기. TabLayoutPractice

2. 뷰바인딩 설정

3. dependencies에서 material 때문에 오류날수도 있는데 그때는 1.8.0으로 버전 낮춰주기

4. 오늘은 glide 사용 안할거라서 아무 이미지 파일 drawable 파일에 넣어주기

5. mainActivity에 레이아웃바인딩 해주기

// MainActivity
package com.iyr.tablayoutpractice

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.iyr.tablayoutpractice.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

---여기까지 기본설정

 

6. activity_main.xml에서

tabLayout 태그 넣어주고

tabIndicatorAnimationMode 와 같은 여러 속성 있음

알아서 해보기

tabIndicatorColor = "@color/black"

// 선택된 탭 밑에 줄 그어지는거 색깔

tabIndicatorFullWidth="true"

// 선택된 탭 밑의 줄 꽉차게 할지 설정

tabRippleColor = "#BDBDBD"

// 탭 선택할때 색깔 번지는 효과

tabTextColor

// 탭 글자 색깔

 

7. activity_main.xml에서 ViewPager2 추가해주기

// activity_main.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="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/main_tb"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:tabIndicatorAnimationMode="linear"
        app:tabIndicatorColor="@color/black"
        app:tabIndicatorFullWidth="true"
        app:tabRippleColor="#BDBDBD"
        app:tabSelectedTextColor="@color/black" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/main_vp"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/main_tb"

</androidx.constraintlayout.widget.ConstraintLayout>

 

이제 뷰페이저 내용으로 들어갈 프래그먼트를 만들어보자

8. 프래그먼트 만들기

액티비티에서 바인딩할때는 

binding = ActivityMainBinding.inflate(layoutinflater)

프래그먼트에서 바인딩할때는

binding = FragmentImageBinding.inflate(inflater, container, false)
// 프래그먼트 바인딩만 먼저 해놓은 코드

package com.iyr.tablayoutpractice

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.iyr.tablayoutpractice.databinding.FragmentImageBinding

class ImageFragment : Fragment() {

    private lateinit var binding: FragmentImageBinding

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


}

 

9. 프래그먼트 레이아웃 간단하게 만들기

텍스트뷰 넣을 프래그먼트랑 이미지뷰 넣을 프래그먼트 각각 만들어줌

// 간단한 프래그먼트 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".ImageFragment">

    <TextView
        android:id="@+id/text_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="text"
        android:textSize="30sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

❗️이론 복습

뷰페이저 사용 위해서는 뷰페이저 어댑터가 있어야 하고, 탭레이아웃에 뷰페이저를 접목시키기 위해서는 TabLayout Mediator 라는 것을 사용함

 

10. 탭레이아웃 뷰페이저 어댑터 만들기

❗️뷰페이저 어댑터는 리사이클러뷰의 어댑터를 상속받는 형식이었고,

이번에는 FragmentStateAdpater 를 상속받도록 함

class TabLayoutVPAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
}

탭레이아웃VP어댑터는 인자로 FragmentActivity를 받고 그 activity를 FragmentStateAdapter에 생성자로 넣어줌.

 

FragmentStateAdapter 클릭해보면,

추상 클래스로서, RecyclerView의 Adapter를 상속받고 있고, 

내부에 Fragment를 관리하는 생성자들이 정의되어 있음.

예를 들어,

activity에 tabLayout이 존재하면 FragmentStateAdpater에 FragmentActivity를 넣어주면 되고

Fragment 안에 TabLayout이 있고 그 아래의 뷰페이저와 연동하고싶으면 인자로 Fragment를 주면 됨

이 두 생성자들은 모두 세번째 생성자를 실행하면서 진행되는데,

여기서는 Fragment를 관리하는 FragmentManager 객체와, 생명주기를 관리하는 lifeCycle 인자를 객체로 받음

옵저버 패턴과 관련이 있지만 여기서는 생략.

 

다시 돌아와서, 결국 추상클래스라는 말은

FragmentStateAdapter를 상속받은 클래스는 그 안의 메서드들도 override 해야한다는 뜻이다.

 

10-1. override fun getItemCount(): Int = 2

 

10-2. override fun createFragment(position: Int): Fragment{}

TabLayout에 어떤 Fragment를 출력할 것인가에 대한 메서드.

when 구문 이용해서 position  값을 받아서

0일 때는 ImageFragment 실행,

나머지일 때는 TextFragment 실행

 

position을 어디서 받는지는? 모르겠음

 

 

// TabLayoutVPAdapter

package com.iyr.tablayoutpractice

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

class TabLayoutVPAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
    override fun getItemCount(): Int = 2

    override fun createFragment(position: Int): Fragment {
        return when(position) {
            0 -> ImageFragment()
            else -> TextFragment()
        }
    }

}

 

뷰페이저 구성이 끝났으니 메인액티비티에서 합쳐주자

 

11. MainActivity

 

11-1. onCreate(){} 안에 initView() 호출 해주고

onCreate() 밖에 initView 내용 구현.

 

일단 뷰페이저와 탭레이아웃을 연동하기 이전에 어댑터를 반드시 부착해줘야 함

binding.mainVp.adapter = TabLayoutVPAdapter(this)

// 바인딩한 뷰페이저 어댑터에 내가 만든 탭레이아웃 뷰페이저 어댑터 넣어줌

// 이때 TabLayoutVPAdapter는 생성자로 FragmentActivity를 필요로 하기 때문에 그냥 this 라고 해줌

 

(탭레이아웃 뷰페이저 어댑터는 뷰페이저의 어댑터란 걸까?흠... 탭레이아웃의 어댑터란걸까?

흠....

둘을 연결하는 어댑터란 건가?

흠........)

 

(❓ fragmentActivity는 프래그먼트란걸까 액티비티란걸까 둘다된단걸까?

FragmentActivity는 Activity 내부에서 fragment도 다룰 수 있게 하는 클래스...라네요

클래스를 생성자로 받는다라...........

class MainActivity : AppCompatActivity() {} 이렇게 mainActivity도 어떤 액티비티를 상속뱓듯이... ㅇㅇ)

 

11-2.

아무튼 어댑터 연결해준 다음에

tabLayoutMediator를 통해 뷰페이저와 탭레이아웃을 연동함

TabLayoutMediator(binding.mainTb, binding.mainVp)

// 첫번째 인자로 탭레이아웃, 두번째 인자로 뷰페이저 넣어주고,

// 세번째 인자로 TabLayoutMediator.TabConfigurationStrategy 를 넣어줘야 하는데, 함수가 들어가야함

탭레이아웃과 뷰페이저를 어떻게 연결할지에 대한 전략

 

그래서 람다함수를 옆에 넣어줘야 하는데, 왜 생성자 안에 들어가지 않는지... 잘모르겠군요

아무튼 마지막 매개변수가 함수일 때는 중괄호를 열어서 익명함수를 넣어줌으로써 구현할 수 있음

 

// initView 밖에 tabLayout 에 들어갈 정보 먼저 (전역변수로) 세팅해주기
private val tabList = arrayListOf("이미지", "텍스트")

private fun initView() {
...

    TabLayoutMediator(binding.mainTb, binding.mainVp) { tab, position ->
        tab.text = tabList[position]
    }.attach()
}

{tab, position -> ... }

에서 tab은 뷰를 의미함(왜?)

tab.text = tabList[position]

탭의 뷰의 텍스트정보를 tabList라는 리스트의 position 값으로 세팅하겠다.

 

그리고 탭레이아웃 구현했으면 꼭 끝에 attach 메서드 구현해줘야 정상적으로 적용됨

 

// MainActivity 완성

package com.iyr.tablayoutpractice

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

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private val tabList = arrayListOf("이미지", "텍스트")

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

        initView()
    }

    private fun initView() {
        binding.mainVp.adapter = TabLayoutVPAdapter(this)
        
        TabLayoutMediator(binding.mainTb, binding.mainVp) { tab, position ->
            tab.text = tabList[position]
        }.attach()
    }
}

 

 

12. 이미지프래그먼트에 이미지 세팅해주기

 

// ImageFragment.kt 이미지 세팅
package com.iyr.tablayoutpractice

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.iyr.tablayoutpractice.databinding.FragmentImageBinding

class ImageFragment : Fragment() {

    private lateinit var binding: FragmentImageBinding

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initImageView()
    }

    private fun initImageView() {
        binding.imageIv.setImageResource(R.drawable.bear)
    }


}

 

 

'KUIT-앱 개발 프로젝트 동아리' 카테고리의 다른 글

6주차 실습-Thread  (1) 2023.10.31
6주차 이론-Thread  (0) 2023.10.30
5주차 실습(1)-Glide 라이브러리, ViewPager2  (0) 2023.10.27
4주차 실습 복습  (1) 2023.10.27
4주차 실습-RecyclerView  (0) 2023.10.04