본문 바로가기
오늘은 뭘 배울까?/Android

코틀린 viewPager2 : 사용법, 애니메이션 등

by Kim Juhwan 2021. 2. 24.

1. viewPager2
   1-1. viewPager란?
   1-2. viewPager의 활용
2. 사용 방법
   2-1. 기본 사용법
   2-2. 애니메이션 설정
   2-3. 여백 설정

 

 


 

1. viewPager2

1-1. viewPager란?

 

페이지를 넘기듯이 이렇게 슉-슉- 넘기는 것을 viewPager(뷰 페이저)라고 한다.

원래는 사용 방법이 굉장히 복잡했는데 (내 기준에...)

2019년에 구글이 viewPager2를 발표하면서 사용하기 굉장히 쉬워졌다.

그냥 리사이클러뷰 사용하듯이 사용하면 된다.

초기에는 이런 저런 버그가 있어 개발자들이 viewPager2 보다 기존의 viewPager를 선호하는 경향이 있었는데

2년이 지난 지금 viewPager2로 넘어오지 않을 이유가 없을 것 같다.

 

 

ViewPager2  |  Android 개발자  |  Android Developers

스와이프할 수 있는 형식으로 뷰 또는 프래그먼트를 표시합니다. 최근 업데이트 현재 안정화 버전 다음 버전 후보 베타 버전 알파 버전 2020년 4월 1일 1.0.0 - - 1.1.0-alpha01 AndroidX 종속 항목 ViewPager2

developer.android.com

자세한 변동사항은 위 공식문서에서 확인할 수 있다.

 

 

1-2. viewPager의 활용

Play Store

 

뷰페이저는 다방면에서 활용이 되고 있다.

위와 같은 화면을 자주 보았을 것이다.

옆으로 스-윽 스-윽 스와이프를 해서 다음 아이템을 확인하는 기능!

리사이클러뷰를 가로로 쓰면 똑같이 구현할 수 있을 것 같지만

어느 일정 이상 페이지를 넘기면 그다음부터는 손을 떼도 자동으로 페이지가 넘어간다는 점이 다르다.

 

 

배달의 민족

 

배너광고 같은 경우도 뷰페이저를 활용한 대표 예시이다.

 

 

웬지

 

앱 최초실행시 뜨는 소개 페이지도 뷰 페이저를 활용한 예시이다.

 

 

인스타그램

 

마지막으로 이렇게 스와이프를 통해 페이지(메뉴)를 변경하는 것도

뷰 페이저를 활용한 예시이다. 

단, 이 예시는 구글 디자인 정책상 권장되지 않는 방법이다.

 

 

업뎃 카톡 ‘스와이프’ 왜 안 되나… “구글 디자인 정책 영향”

“카카오(035720)톡 스와이프(옆으로 쓸기) 기능 왜 없앴어요? 메뉴 이동하기 편했는데 돌려주세요!(구글 플레이 스토어 의견)”카카오가 지난 6일 카카오톡 8.0 버전을 구글 안드로이드 운영체제(O

www.sedaily.com

예전에는 카카오톡 메뉴 전환이 스와이프로 가능했었다.

어느 날 갑자기 이 기능이 막히면서 불편하다는 의견이 많이 올라오는 일이 있었는데,

자세한 설명은 위 뉴스에서 다루고 있다.

아무튼 결론은 뷰 페이저를 통한 메뉴 전환은 '하지 말아야 할 기능'이라고 구글에서 명시했다는 것!

인스타그램은 아직까지도 사용하고 있는 거 보니 마이웨이인가 보다.

 

 

2. 사용 방법

2-1. 기본 사용법

activity_main.xml
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager_idol"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

사용 방법은 진짜 recyclerView랑 다를 게 없다.

우선 xml 파일에서 ViewPager2를 추가해준다.

주의할 점은 태그가 android가 아닌 androidx로 시작한다는 거!

 

MainActivity.kt
class MainActivity : AppCompatActivity() {

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

        viewPager_idol.adapter = ViewPagerAdapter(getIdolList()) // 어댑터 생성
        viewPager_idol.orientation = ViewPager2.ORIENTATION_HORIZONTAL // 방향을 가로로
    }

    // 뷰 페이저에 들어갈 아이템
    private fun getIdolList(): ArrayList<Int> {
        return arrayListOf<Int>(R.drawable.idol1, R.drawable.idol2, R.drawable.idol3)
    }
}

어댑터를 생성할 때 아이템을 넘겨주고

방향을 가로로 해주면 메인 액티비티에서 할 일은 끝이다.

만약 방향을 xml 파일에서 설정하고 싶다면 android:orientation="vertical"을 넣어주면 된다.

 

ViewPagerAdapter.kt
class ViewPagerAdapter(idolList: ArrayList<Int>) : RecyclerView.Adapter<ViewPagerAdapter.PagerViewHolder>() {
    var item = idolList

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PagerViewHolder((parent))

    override fun getItemCount(): Int = item.size

    override fun onBindViewHolder(holder: PagerViewHolder, position: Int) {
        holder.idol.setImageResource(item[position])
    }

    inner class PagerViewHolder(parent: ViewGroup) : RecyclerView.ViewHolder
        (LayoutInflater.from(parent.context).inflate(R.layout.idol_list_item, parent, false)){

        val idol = itemView.imageView_idol!!
    }
}

어댑터도 리사이클러뷰랑 다를 게 없다.

이너 클래스를 쓰든 따로 파일을 만들든 그런 건 취향껏 만들면 된다.

 

idol_list_item.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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView_idol"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/idol1" />
</androidx.constraintlayout.widget.ConstraintLayout>

페이지에 띄우고 싶은 뷰를 정의하는 xml 파일을 하나 만든다.

이것도 리사이클러뷰랑 똑같다.

나는 일단 이미지 한 개만 보여주도록 만들었다.

 

 

결과물

 

이렇게 뷰페이저가 완성됐다!

 

 

2-2. 애니메이션 설정

 

ViewPager2로 프래그먼트 간 슬라이드  |  Android 개발자  |  Android Developers

화면 슬라이드는 하나의 전체 화면에서 다른 전체 화면으로 전환하는 것으로, 설정 마법사 또는 슬라이드쇼와 같은 UI에서 일반적으로 사용됩니다. 이 주제에서는 ViewPager2 객체로 화면을 슬라이

developer.android.com

공식문서를 읽어보면 페이지를 넘길 때 애니메이션을 주는 방법에 대해 적혀있다.

그래서 한 번 따라 해 보았다.

 

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private val MIN_SCALE = 0.85f // 뷰가 몇퍼센트로 줄어들 것인지
    private val MIN_ALPHA = 0.5f // 어두워지는 정도를 나타낸 듯 하다.

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

        viewPager_icon.adapter = ViewPagerAdapter(getIconList())
        viewPager_icon.orientation = ViewPager2.ORIENTATION_HORIZONTAL
        viewPager_icon.setPageTransformer(ZoomOutPageTransformer()) // 애니메이션 적용
    }

    private fun getIconList(): ArrayList<Int> {
        return arrayListOf<Int>(R.drawable.icon1, R.drawable.icon2, R.drawable.icon3, R.drawable.icon4)
    }

    /* 공식문서에 있는 코드 긁어온거임 */
    inner class ZoomOutPageTransformer : ViewPager2.PageTransformer {
        override fun transformPage(view: View, position: Float) {
            view.apply {
                val pageWidth = width
                val pageHeight = height
                when {
                    position < -1 -> { // [-Infinity,-1)
                        // This page is way off-screen to the left.
                        alpha = 0f
                    }
                    position <= 1 -> { // [-1,1]
                        // Modify the default slide transition to shrink the page as well
                        val scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position))
                        val vertMargin = pageHeight * (1 - scaleFactor) / 2
                        val horzMargin = pageWidth * (1 - scaleFactor) / 2
                        translationX = if (position < 0) {
                            horzMargin - vertMargin / 2
                        } else {
                            horzMargin + vertMargin / 2
                        }

                        // Scale the page down (between MIN_SCALE and 1)
                        scaleX = scaleFactor
                        scaleY = scaleFactor

                        // Fade the page relative to its size.
                        alpha = (MIN_ALPHA +
                                (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))
                    }
                    else -> { // (1,+Infinity]
                        // This page is way off-screen to the right.
                        alpha = 0f
                    }
                }
            }
        }
    }
}

기존의 코드에 SCALE, ALPHA 값을 추가하고

setPageTransformer를 뷰페이저에 적용해주고

공식문서에 있는 코드를 가져와서 붙여 넣기 한 게 전부다. 아주 심플

(ZoomOutPageTransformer는 포스팅하기 편해서 이너 클래스로 넣었다. 실제 코드에선 따로 빼는 게 보기 좋을 듯)

 

 

애니메이션 적용 결과

 

다소 심심했던 뷰페이저에 애니메이션 효과가 생겼다.

공식문서에 다른 애니메이션 예시가 하나 더 있고

찾아보면 다른 사람들이 만든 것도 있을 테니 기회가 되면 사용해봐야겠다.

 

 

2-3. 여백 설정

activity_main.xml
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager_icon"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false" // 이거랑
        android:clipChildren="false" // 이거 추가
        />

[1-2 viewPager의 활용] 목차에서 보여준 예시를 보면

플레이스토어에서 뷰페이저를 사용하고 있는데

다음에 올 아이템 항목이 빼꼼하고 머리를 내밀고 있는 걸 구현해보려고 한다.

 

이 디자인은 특별한 문구나 안내 없이도

사용자가 "확인할 아이템 항목이 더 있구나"라고 생각하게끔 만들어준다.

 

 

[안드로이드] 리사이클러뷰 android:clipToPadding="false"

리사이클러뷰에 패딩을 줄 경우 위아래에 패딩공간이 있을 것이다. 그런데 사람마다 다르겠지만 스크롤을 한 경우 패딩공간을 활용하면 좀더 뷰를 활용할수있고 자연스러운(?) 뷰를 보여줄 수

youngest-programming.tistory.com

 

 

안드로이드 View의 clipChildren에 대하여 - Hansol’s Blog

기본적으로 안드로이드의 모든 view는 자신이 물리적으로 차지하는 영역 만큼만 그릴 수 있다. 즉, 전체 화면을 기준으로 (left=100, top=100, right=200, bottom=200)만한 영역을 차지하는 view가 (left=200, top=2

giantsol.github.io

우선 viewPager에 새로운 옵션을 2개 넣어주었는데

그 이유는 말이 길어질 것 같아 위 링크로 대체한다.

 

 

MainActivity.kt
class MainActivity : AppCompatActivity() {

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

        /* 여백, 너비에 대한 정의 */
        val pageMarginPx = resources.getDimensionPixelOffset(R.dimen.pageMargin) // dimen 파일 안에 크기를 정의해두었다.
        val pagerWidth = resources.getDimensionPixelOffset(R.dimen.pageWidth) // dimen 파일이 없으면 생성해야함
        val screenWidth = resources.displayMetrics.widthPixels // 스마트폰의 너비 길이를 가져옴
        val offsetPx = screenWidth - pageMarginPx - pagerWidth

        viewPager_icon.setPageTransformer { page, position ->
            page.translationX = position * -offsetPx
        }

        viewPager_icon.offscreenPageLimit = 1 // 몇 개의 페이지를 미리 로드 해둘것인지
        viewPager_icon.adapter = ViewPagerAdapter()
        viewPager_icon.orientation = ViewPager2.ORIENTATION_HORIZONTAL
    }
}

우선... 여백과 너비를 이용하여 페이지가 어디서 얼마큼 이동할 건지를 계산한다.

이때 R.dimen.pageMargin 이 값은 따로 dimen 폴더에 정의해둔 값이므로

폴더가 없다면 values 폴더 밑에 생성해서 사용해야 한다.

 

이 pageMargin값과 pageWidth값은 아이템의 크기가 어느 정도인지

간격은 얼마큼으로 주고 싶은지 등등에 따라 달라지기 때문에

직접 값을 조정해가며 적당한 값을 사용하면 된다.

 

그리고 setPageTransformer를 이용해 page의 X축 값에 변화를 주면 끝이다.

 

 

결과물

 

이렇게 다음 아이템이 빼꼼 머리를 내밀고 있는

뷰페이저가 완성됐다!

 

 

offsceenPageLimit 값을 설정하지 않았을 때

 

참고로 offscreenPageLimit 값을 설정해두지 않으면 위와 같은 결과물이 나온다.

페이지가 넘겨질 때 그제야 다음 페이지를 로드하기 때문에

빼꼼 하지 않고 갑자기 어디서 툭! 튀어나오는 것처럼 보이는 것이다.

나는 다음 페이지가 미리 보이길 원했으므로 '1'을 넣어주었다.

 

만약 한 화면에 여러 개의 페이지가 보이는 상황이라면 (play store처럼)

그만큼 더 값을 늘려주면 된다.

 

 

 


 

 

분량이 너무 긴 관계로 <활용편>은 아래 게시물로 이어집니다

 

 

[Kotlin] 뷰페이저2 활용 예제 : tabLayout, indicator, fragment, 자동 스크롤, 무한스크롤, 배너 등

0. 시작하기 앞서.. 1. viewPager 활용  1-1. Indicator와 같이 사용  1-2. Fragment와 같이 사용  1-3. tabLayout과 같이 사용 2. 광고 배너 만들기  2-1. 현재 배너 위치 표시하기  2-2. 무한 뷰페이저  ..

todaycode.tistory.com

 

 

▼ 기본편, 활용편 예제 전체 코드는 깃허브에서 확인해주세요 ▼

 

GitHub - juhwankim-dev/SelfStudy: 코틀린으로 공부한 것들을 올리는 공간입니다.

코틀린으로 공부한 것들을 올리는 공간입니다. Contribute to juhwankim-dev/SelfStudy development by creating an account on GitHub.

github.com

 

반응형

댓글