0. ViewPager 에 대해서 알아보자.

  • ViewPager?

    각 View 가 책의 페이지처럼 보여지고 슬라이드 시, 마치 책을 넘기는 듯한 효과를 나타내고자 할때 쓰이는 Component 이다.
    한, 화면에 모든것을 다 보여주지 못하고 여러 페이지로 나누어 보여주고자할때 사용하면 된다.

  • about. ViewPager Version

    현재 시점에서 ViewPager를 사용하려면 Android Studio에서 기본으로 ViewPager2를 사용하도록 되어있다.
    버전 히스토리를 살펴보자면 현재는 ViewPager2:1.1.0 까지 업데이트 됬다.
    이전 ViewPager를 사용해본적은 없으나 문서를 찾아보면 사용하기가 다소 까다롭고 버그 이슈도 있었던듯하다. 해서 구글에서 라이브러리가 Androidx로 바뀌는 과정에서 ViewPager2가 나오게 됬고 첫 1.0.0-alpha 버전이 2019.02.07에 발표되어 지금까지 업데이트가 되었음을 알수 있다.
    -> ViewPager2 Version 내역 확인 참고 링크는 여기!!

  • ViewPager2 에서 개선된 점 (관련 링크 -> Migrating from ViewPager to ViewPager2)
    1. Vertical scrolling : 기존 가로 페이징은 물론 세로 페이징도 지원한다.
    2. Right-to-left support : 언어에 따라 LTR, RTL이 자동 설정되고, android:layoutDirection 속성을 통해 수동으로도 설정가능하다.
    3. modifiable Fragment collenctions : 프래그먼트의 변화를 동적으로 반영하는 것이 가능하다. 기본 컬렉션이 변경되면 notifyDatasetChanged()를 호출하여 UI를 업데이트한다. 즉, 앱은 런타임 시 프래그먼트 컬렉션을 동적으로 수정할 수 있고, 그러면 ViewPager2는 수정된 컬렉션을 올바르게 표시한다.
    4. DiffUtil : Recyclerview의 DiffUtil을 사용 가능하다. ViewPager2는 RecyclerView를 기반으로 빌드되므로 DiffUtil 유틸리티 클래스에 액세스할 수 있고 이에 따른 애니메이션을 활용할 수 있다.
    5. 마지막으로 가장중요한점으로 기존 ViewPager는 더이상 관리,개발되지 않는다. 즉 더이상의 업데이트는 없다는 말! 고로 앞으로는 지속 업데이트, 개발중인 ViewPager2 만 사용하도록 하자.

  

1. 기능 구현

  • 화면 상단에 TabLayout을 배치하고 Icon을 넣어보자
  • 나머지 화면을 ViewPager2를 이용한 Fragment로 구성해보자.

 
 

2. Android Studio에서 기본 프로젝트(with empty activity) 생성하자!

생성시 'Empty Activity'로 기본 생성

 
 

3. ViewBinding 사용을 위한 build.gradle 설정

android {
        // 뷰 바인딩 옵션 활성화
        viewBinding {
            enabled = true
        }
    }

 
 

4. activity_main.xml

  • 화면구성 (Component Tree) 를 다음과 같이 만들어보자
  1. 메인화면(ConstraintLayout) 상단에 TabLayout 배치 (3개의 TabItem-Monday,Tuesday,Wednesday 을 가진다)
  2. TabLayout 하단 나머지 화면에 ConstraintLayout 추가
  3. 추가한 ConstraintLayout 에 ViewPager2 배치 (Fragment를 통해 화면 전환될 영역)
<?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/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@+id/constraint_pages"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

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

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

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

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraint_pages"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tab_layout">

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
  • 결과물

  

5-1. 프래그먼트 별 화면 구성

  • tabItem 갯수에 맞게 간단하게 구별 가능한 화면(3개)을 만들어보자 (frag_monday, frag_tuesday, frag_wednesday)
  • Ex) frag_monday
<?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">

    <TextView
        android:id="@+id/frag_monday"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="겨울시인 Frag (1)"
        android:textColor="#0971EF"
        android:textSize="34sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

  

5-2. 프래그먼트 별 화면 구성

  • 기존에 만들어놓은 Fragment UI xml 파일을 ViewBinding을 통해 kt 파일과 매핑시켜 놓자.
    (FragMondy.kt, FragTuesday.kt, FragWednesday.kt)
  • Ex) FragMonday.kt
class FragMonday : Fragment() {
    private  lateinit var binding : FragMondayBinding
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = FragMondayBinding.inflate(inflater, container, false)
        return binding.root
    }
}

  

6. ViewPager 어댑터 구현(Adapter)

  • Adapter의 역할 : 보여질 메인화면에 Fragment를 ViewPager 형식으로 매핑시켜준다.
  • getItemCount() : 총 페이징될 갯수를 return
  • createFragment(position) : when 구문을 통해 분기, 각 Fragment로의 화면 전환이 실행된다.
private const val FRAG_NUMS = 3

class ViewPagerAdapter(fragmentActivity: FragmentActivity): FragmentStateAdapter(fragmentActivity) {
    override fun getItemCount(): Int {
        return FRAG_NUMS
    }

    override fun createFragment(position: Int): Fragment {
        return when(position){
            0 -> return FragMonday()
            1 -> return FragTuesday()
            else -> return FragWednesday()
        }
    }
}

참고 : 기존 ViewPager 와 비교해 ViewPager2 에서의 Adapter 구현 시, 변경된 사항은 다음과 같다.

  1. 상속
    1. 기존 PagerAdapter(deprecated) 를 사용했을 경우 -> RecyclerView.Adapter 를 사용
    2. 기존 FragmentPagerAdapter(deprecated) 를 사용했을 경우 -> FragmentStateAdapter 를 사용
    3. 기존 FragmentStatePagerAdapter(deprecated) 를 사용했을 경우 -> FragmentStateAdapter 를 사용
  2. Get. 페이징 갯수 : getCount(deprecated) -> getItemCount
  3. Get .페이지: getItem(deprecated) -> createFragment

  

7. MainActivity.kt

  • tabTitleArray: TabLayout 의 Title 정보를 가진 array 준비
  • tabIconArray :TabLayout 에 들어갈 Icon 가진 array 준비
    sample icon 만드는법 링크 참고 -> 4. BottomNavigation 아이콘 만들기 (Vertor Asset-기본 제공)
  • viewPager.adapter = ViewPagerAdapter(this) : 만들어 놓은 Adapter를 ViewPager2 의 Adapter로 설정
  • TabLayoutMediator : TabLayout과 ViewPager2의 연동을 도와주는 객체로 attach()를 통해 연동 완료
    (구문 내 구현해야 할 것은 준비된 Array에서 Title 과 Icon을 가져와서 Mapping 작업)
class MainActivity : AppCompatActivity() {

    private var mBinding : ActivityMainBinding? = null
    private val binding get() = mBinding!!
    private val tabTitleArray = arrayOf(
        "Monday", "Tuesday", "Wednesday"
    )
    private val tabIconArray = arrayOf(
        R.drawable.ic_baseline_arrow_back_24,
        R.drawable.ic_baseline_arrow_downward_24,
        R.drawable.ic_baseline_arrow_forward_24
    )


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

        var viewPager = binding.viewpager
        var tabLayout = binding.tabLayout

        viewPager.adapter = ViewPagerAdapter(this)

        TabLayoutMediator(tabLayout, viewPager){
            tab, position -> tab.text = tabTitleArray[position]
            tab.icon = getDrawable(tabIconArray[position])
        }.attach()
    }
}

  

8. 추가 기능 (Scrollable)

  • 본 예제에서는 Item(page) 항목이 3개라 상관없지만 Item이 엄청 많아진다면 한 화면에 표시되기 어렵다
    해서 다수의 Item을 페이징할 경우, TabLayout이 스크롤이 가능하도록 할 수 있는데 방법은 다음과 같다.
  • activity_main.xml 의 TabLayout 에 속성값(tabMode)을 주도록하자.
    기본값인 fixed -> Scrollable 로 바꿔주기만 하면 끝!

  

9. 실행결과

페이징 방법(1) : TabLayout 의 Item 을 선택하기
페이징 방법(2) : ViewPager 영역을 슬라이드로 넘기기

  

10. Reference

유투버 홍드로이드님의 안드로이드 앱 만들기 #35 뷰페이저

  • 주의할점
    참고한 동영상 강의는 Java 로 진행된 강의입니다. Kotlin 을 공부하기 위한 목적으로 Java-Kotlin 간의 다른 부분에 대해서
    직접 찾아가며 예제 작성을 하였고 영상이 올라온 시점이 3년 전이라 현재의 버전, API 들이 deprecated 된 경우도 있어
    이 또한 현재 권장하는 API로 변경/적용 한 부분이 있어 Code가 다를 수 있으니 보시는 분들은 참고 바람.

+ Recent posts