GDSC HUFS 3기/Android with Kotlin Team 6

[6팀] 코틀린 안드로이드 기초강의 35-36 탭메뉴 뷰페이저와프래그먼트, 리사이클러뷰

떠어영 2021. 11. 26. 17:14

이 글은 이것이 안드로이드다 with 코틀린(개정판)를 참고하여 작성하였습니다.

작성자 : 정서영

개발환경은 Windows, Android Studio입니다.

 

1. 탭메뉴 뷰페이저와 프래그먼트 

  • 탭 레이아웃 : 탭 메뉴가 구성된 레이아웃
  • 뷰페이저 : 스와이프하면 화면이 이동되도록 구현해준 것

  → 탭 레이아웃과 뷰페이저를 연결해서 화면을 구성

탭 메뉴 레이아웃과 화면을 보여주는 뷰페이저

1. 프래그먼트 4개 생성

 

Fragment A를 만들고 복사해서 레이아웃 fragment_b, _c, _d와 Fragment B,  C,  D를 생성

2. activity_main에서 Containers의 viewPager를 삽입하고 레이아웃을 match_parent로 설정 ( id는 viewPager )

   

3. 뷰페이저 안에 프래그먼트를 넣기 위해 FragmentPagerAdapter 클래스 생성. ( FragmentStateAdapter를 상속 )

class FragmentPagerAdapter(val fragmentList:List<Fragment>, fragmentActivity: FragmentActivity)
//자식의 생성자에서 fragmentActivity를 받아서 부모 클래스에 넘겨줌
    : FragmentStateAdapter(fragmentActivity){ 
    
    //꼭 구현해야하는 추상 메소드
    override fun getItemCount() =  fragmentList.size //아이템 갯수 가져오기

    override fun createFragment(position: Int) = fragmentList.get(position)
    //목록에서 position에 해당하는 프래그먼트를 반환해서 페이지 그리기 
}

 

4. 화면에 뷰페이저 위젯, 아답터, 프래그먼트 목록을 연결

  • 아답터: 데이터 리스트를 입력받아 각 데이터 항목에 해당하는 뷰를 생성

( build.gradle )

buildFeatures{
        viewBinding true
    }

( MainActivity )

class MainActivity : AppCompatActivity() {
    
    val binding by lazy { ActivityMainBinding.inflate(layoutInflater)} //바인딩 생성
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        //1. 페이지 데이터를 로드
        val list = listOf(FragmentA(),FragmentB(),FragmentC(),FragmentD())
        //2. 아답터를 생성 ( 프래그먼트 리스트, fragmentActivity를 넣어준다 )
        val pagerAdapter = FragmentPagerAdapter(list, this)
        //3. 아답터와 뷰페이저 연결
        binding.viewPager.adapter = pagerAdapter
    }

 

실행화면 (뷰페이저 구성 완료)

5. 탭 레이아웃 추가

 

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

	//4. 탭 메뉴의 개수만큼 제목을 목록으로 생성
        val titles = listOf("A", "B", "C", "D")
        //5. 탭레이아웃과 뷰페이저 연결
        TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
            //tabLayout개수만큼 돌면서 tab을 생성하고 몇번째 position인지 알려줌
            tab.text = titles.get(position)
        }.attach()

 

7. with 스코프 함수 사용, tabIndicatorLayoutGravity 속성을 top으로 변경

with(binding){
            //3. 아답터와 뷰페이저 연결
            viewPager.adapter = pagerAdapter
            //4. 탭 메뉴의 개수만큼 제목을 목록으로 생성
            val titles = listOf("A", "B", "C", "D")
            //5. 탭레이아웃과 뷰페이저 연결
            TabLayoutMediator(tabLayout, viewPager) { tab, position ->
                //tabLayout개수만큼 돌면서 tab을 생성하고 몇번째 position인지 알려줌
                tab.text = titles.get(position)
            }.attach()
        }

 

실행화면 (탭메뉴와 뷰페이저 연결)

 

2. 탭메뉴 뷰페이저와 리사이클러뷰

뷰페이저에 리사이클러뷰 어댑터를 사용 ( 리사이클러뷰를 사용하므로 하나의 레이아웃만으로 구현 가능 )

 

1. 뷰페이저 생성 ( Containers → ViewPager )

 

2. build. gradle 파일에서 뷰바인딩 설정

 

3. item_viewpager 레이아웃 생성, textView 삽입

 

4. MainActivity에서 아이템 레이아웃을 사용하는 어댑터 클래스 생성, 어댑터와 뷰페이저 연결

class MainActivity : AppCompatActivity() {
    //바인딩클래스의 inflate 함수로 xml 파일을 객체화
    val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

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

        //1.데이터 로드
        val list = listOf("월", "화", "수", "목", "금", "토", "일")
        //2.어댑터를 생성해서 데이터를 넘겨줌
        val pagerAdapter = CustomPagerAdapter(list)
        //3.어댑터와 뷰페이저를 연결
        binding.viewPager.adapter = pagerAdapter
    }
}
//어댑터 클래스 생성: RecyclerView의 Adapter를 상속 <사용할 뷰 홀더>
class CustomPagerAdapter(val textList:List<String>) : RecyclerView.Adapter<CustomPagerAdapter.Holder>(){

    //Holder 만들기 (RecyclerView의 ViewHolder를 상속)
    class Holder(val binding: ItemViewpagerBinding) : RecyclerView.ViewHolder(binding.root) {
        fun setItem(text: String) {
            binding.textView.text = text //받아온 바인딩 객체의 textview에 각각의 text를 set
        }
    }
    ///implement members -> 추상 메소드들 구현
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        //ItemViewPagerBinding을 inflate해서 바인딩 객체 생성
        val binding = ItemViewpagerBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }
    override fun onBindViewHolder(holder: Holder, position: Int) {
        //돌면서 각각의 text를 set
        holder.setItem(textList.get(position))
    }
    override fun getItemCount() = textList.size
}

 

실행화면 ( 뷰페이저와 리사이클러뷰 어댑터 연결 )

5. 탭 레이아웃 연결

	//4. 탭 타이틀 목록 생성 = 위의 리스트 사용
        //5. 탭레이아웃과 뷰페이저 연결
        TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
            tab.text = list.get(position)
        }.attach()

 

 

6. 페이지 클래스를 만들어서 구현하기 위해 아이템 레이아웃 변경 ( 두개의 textView : textDay, textWeather )

 

7. 페이지 데이터 클래스를 생성하고 해당 클래스에서 데이터 로드해오기

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

        //1.데이터 로드
        val list = loadData()
        //2.어댑터를 생성, 데이터 넘겨줌
        val pagerAdapter = CustomPagerAdapter(list)
        //3.어댑터와 뷰페이저를 연결
        binding.viewPager.adapter = pagerAdapter

        //4. 탭 타이틀 목록 생성 = 위의 리스트 사용
        val titles = listOf("월", "화", "수", "목", "금", "토", "일")
        //5. 탭레이아웃과 뷰페이저 연결
        TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
            tab.text = titles.get(position)
        }.attach()
    }
    fun loadData() : List<Page> {
        val pageList = mutableListOf<Page>()
        pageList.add(Page(1,"흐림"))
        pageList.add(Page(2,"맑음"))
        pageList.add(Page(3,"구름"))
        pageList.add(Page(4,"비"))
        pageList.add(Page(5,"눈"))
        pageList.add(Page(6,"태풍"))
        pageList.add(Page(7,"안개"))
        return pageList
    }
}
class CustomPagerAdapter(val pageList:List<Page>) : RecyclerView.Adapter<CustomPagerAdapter.Holder>(){

    class Holder(val binding: ItemViewpagerBinding) : RecyclerView.ViewHolder(binding.root) {
        fun setItem(page: Page) {
            with(binding){
                textDay.text = "${page.day} 일"
                textWeather.text = page.weather
            }
        }
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = ItemViewpagerBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        return Holder(binding)
    }
    override fun onBindViewHolder(holder: Holder, position: Int) {
        holder.setItem(pageList.get(position))
    }
    override fun getItemCount() = pageList.size
}
//데이터 클래스 선언
data class Page( val day: Int, val weather: String)

 

실행화면 ( 데이터 클래스 사용)