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

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

by Kim Juhwan 2021. 2. 27.

0. 시작하기 앞서..
1. viewPager 활용

   1-1. Indicator와 같이 사용
   1-2. Fragment와 같이 사용
   1-3. tabLayout과 같이 사용
2. 광고 배너 만들기
   2-1. 현재 배너 위치 표시하기
   2-2. 무한 뷰페이저
   2-3. 자동으로 스크롤하기
3. 예제 파일(깃허브)

 

 

 


 

0. 시작하기 앞서..

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

 

오늘은 viewPager2를 이용해 위와 같은 예시를 만들어 볼 거예요.

 

 

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

1. viewPager2  1-1. viewPager란?  1-2. viewPager의 활용 2. 사용 방법  2-1. 기본 사용법  2-2. 애니메이션 설정  2-3. 여백 설정 1. viewPager2 1-1. viewPager란? 페이지를 넘기듯이 이렇게 슉-슉- 넘..

todaycode.tistory.com

기본적인 개념, 사용 방법은 위 게시물에서 다루고 있습니다.

아직 안 보셨다면 먼저 보고 와주세요!

 

 

1. viewPager 활용

1-1. Indicator와 같이 사용

indicator를 사용한 뷰페이저

 

이렇게 페이지를 넘길 때마다

동그라미가 따라 움직이는 것(?)을 Indicator라고 한다.

뷰페이저랑 보통 많이 쓰이는 편인데 사용 방법은 아~~~주 간단한다.

 

 

tommybuonomo/dotsindicator

Three material Dots Indicators for view pagers in Android ! - tommybuonomo/dotsindicator

github.com

Indicator는 기본으로 제공되는 뷰가 아니므로 라이브러리를 찾아서 사용해야 한다.

나는 이 라이브러리를 사용했다.

 

 

A categorized directory of libraries and tools for Android | Android-Arsenal.com

A categorized directory of libraries and tools for Android

android-arsenal.com

다른 라이브러리를 찾아보고 싶다면 위 사이트에서 검색하면 된다.

 

activity_main.xml
    <com.tbuonomo.viewpagerdotsindicator.SpringDotsIndicator
        android:id="@+id/spring_dots_indicator"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:dampingRatio="0.5"
        app:dotsCornerRadius="20dp"
        app:dotsSize="10dp"
        app:dotsSpacing="6dp"
        app:dotsStrokeWidth="1dp"
        app:stiffness="300"
        android:layout_gravity="center"
        android:layout_marginTop="30dp"/>

indicator를 사용하는 방법, 속성 이름 이런 것들은 라이브러리마다 다르기 때문에

해당 페이지의 설명대로 따라 하면 되고...

indicator를 놓고 싶은 적당한 위치에 놓으면 된다.

 

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)
        val pagerWidth = resources.getDimensionPixelOffset(R.dimen.pagerWidth)
        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(getIconList())
        viewPager_icon.orientation = ViewPager2.ORIENTATION_HORIZONTAL

        spring_dots_indicator.setViewPager2(viewPager_icon) // indicator 설정
    }

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

viewPager에 indicator를 결합해주면.. 끝!

(뷰페이저 사용 방법은 앞서 말했듯이 이전 포스팅에서 확인하면 됩니다)

 

 

1-2. Fragment와 같이 사용

프래그먼트를 사용한 뷰페이저

 

<기본편>에서 알아본 뷰페이저는

똑같은 형식의 페이지만 반복되는 구조였다.

만약 페이지마다 뷰의 종류, 개수, 위치 등을 달리하고 싶다면

프래그먼트를 각각 넣어주는 방법을 사용해야 한다.

 

MyFragment1.kt
class MyFragment1 : Fragment() {

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

프래그먼트는 주제에서 벗어나므로 설명은 스킵..!

원하는 페이지 수만큼 프래그먼트를 생성하고

자신이 꾸미고 싶은 대로 꾸미면 된다.

 

MainActivity.kt
private const val NUM_PAGES = 2 // 페이지 수를 정해둠

class MainActivity : AppCompatActivity() {

    private lateinit var viewPager: ViewPager2

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

        viewPager_icon.adapter = ScreenSlidePagerAdapter(this)
        viewPager_icon.orientation = ViewPager2.ORIENTATION_HORIZONTAL

        spring_dots_indicator.setViewPager2(viewPager_icon)
    }

    override fun onBackPressed() {
        if (viewPager_icon.currentItem == 0) {
            // 사용자가 첫 번째 페이지에서 뒤로가기 버튼을 누르면 finish 하도록 하고
            super.onBackPressed()
        } else {
            // 그렇지 않으면 이전 페이지로 이동하게 한다.
            viewPager_icon.currentItem = viewPager_icon.currentItem - 1
        }
    }

    private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
        override fun getItemCount(): Int = NUM_PAGES // 페이지 수 리턴

        override fun createFragment(position: Int): Fragment {
            return when(position){ // 페이지 포지션에 따라 그에 맞는 프래그먼트를 보여줌
                0 -> MyFragment1()
                else -> MyFragment2()
            }
        }
    }
}

onBackPressed는 뒤로 가기를 눌렀을 때

이전 페이지(이전 프래그먼트)로 이동하게 할 건지

아니면 finish 할 건지를 정의해주는 코드를 넣었다. 원하지 않으면 지워버리면 된다.

 

Adapter를 하나 생성했는데 <기본편>에서는 리사이클러뷰 어댑터를 상속받았지만

프래그먼트를 사용할 때는 FragmentStateAdapter를 상속받아야 함을 유의하자.

 

getItemCount에서는 총 페이지수를 리턴해줘야 하고

createFragment는 현재 position값에 따라 원하는 프래그먼트를 보여주면 된다.

참고로 position은 0부터 시작한다.

 

 

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

화면 슬라이드는 하나의 전체 화면에서 다른 전체 화면으로 전환하는 것으로, 설정 마법사 또는 슬라이드쇼와 같은 UI에서 일반적으로 사용됩니다. 이 과정에서는 지원 라이브러리에서 제공하

developer.android.com

위 코드는 안드로이드 공식문서에 다 나와있는 내용이다.

공식문서 짱짱맨

 

 

1-3. tabLayout과 같이 사용

탭 레이아웃을 사용한 뷰페이저

 

TabLayout을 이용하면 페이지가 바뀔 때마다 위와 같은 효과를 줄 수 있다.

이것도 굉장히 쉽고, 라이브러리도 다양하다.

 

 

Containers - TabLayout

 

안드로이드에서 제공하는 TabLayout을 사용해보자.

보통은 뷰페이저 위쪽에 위치시킨다.

 

activity_main.xml
    <com.google.android.material.tabs.TabLayout // 이 범위 안에
        android:id="@+id/tabLayout_menu"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.tabs.TabItem // 요놈들이 들어간다.
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <com.google.android.material.tabs.TabItem // 요놈들이 들어간다.
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
            
    </com.google.android.material.tabs.TabLayout> // 이 범위 안에

탭 레이아웃 안에는 TabItem이 들어가는데

원하는 메뉴의 개수만큼 생성해주면 된다.

 

MainActivity.kt
private const val NUM_PAGES = 2

class MainActivity : AppCompatActivity() {

    private lateinit var viewPager: ViewPager2
    val tabTextList = arrayListOf("여행지", "내 티켓") // 메뉴에 들어갈 이름 (순서대로)

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

        viewPager_icon.adapter = ScreenSlidePagerAdapter(this)
        viewPager_icon.orientation = ViewPager2.ORIENTATION_HORIZONTAL

        // 뷰페이저와 탭레이아웃을 붙임
        TabLayoutMediator(tabLayout_menu, viewPager_icon) { tab, position ->
            tab.text = tabTextList[position]
        }.attach()
    }

    private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
        override fun getItemCount(): Int = NUM_PAGES

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

[1-2 목차]에서 설명한 프래그먼트를 사용한 코드에서 딱 4줄 추가했다.

메뉴에 들어갈 이름을 리스트로 선언해주고

뷰페이저와 탭 레이아웃을 이어 붙이면 끝이다.

요즘 애들 말마따나 댕 쉬움

 

이렇게 하면 페이지를 스와이프 하면 메뉴가 따라 움직이고

메뉴를 탭하면 자동으로 페이지가 이동한다.

 

val tabIconList = arrayListOf(R.drawable.아이콘이름, R.drawable.아이콘이름) // 이미지를 리스트로 선언하고
tab.setIcon(tabIconList[position]) // TabLayoutMediator{} 안에 이걸 넣어주세요

만약 텍스트 옆에 작은 아이콘을 넣어주고 싶다면 위 코드를 추가하면 된다.

 

 

2. 광고 배너 만들기

2-1. 현재 배너 위치 표시하기

배민 따라함

이번엔 위와 같은 배너도 공부해봤는데

나 같은 초보자가 하기엔 은근히 복잡했다.

앞에 소개했던 놈들과는 달리 험난한 여정이 될 테니 멘탈 딱 붙잡으시길

 

치킨 먹고 싶다.

우선 가장 먼저 해야 할 것은 저걸 만드는 일이다.

불투명한 검은색을 바탕으로 하는 타원 모양을 만들어야 한다.

 

gray_ellipse.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    // 색 지정
    <solid android:color="#59000000"/>

    // 코너 둥글게
    <corners
        android:bottomLeftRadius="15dp"
        android:bottomRightRadius="15dp"
        android:topLeftRadius="15dp"
        android:topRightRadius="15dp"
        />

</shape>

drawable 폴더 안에 이 파일을 생성해준다.

 

 

왕초보 개발자의 안드로이드 앱 UI 기본 공부하기 - 1편

0. 시작하기에 앞서... 1. 아이콘  1-1. 아이콘 다운로드  1-2. 아이콘 활용 2. 텍스트  2-1. dimens  2-2. strings  2-3. style  2-4. 텍스트 간격 3. 여백(margin) 4. 높이(elevation) 0. 시작하기에 앞..

todaycode.tistory.com

불투명한 색상을 어떻게 지정해야 하는지 더 알고 싶다면

위 링크의 [목차 2-5]를 참고하자.

 

activity_main.xml
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager_banner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabLayout_menu" />

    <LinearLayout
        android:id="@+id/linear_layout_see_all"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintRight_toRightOf="@+id/viewPager_banner"
        app:layout_constraintBottom_toBottomOf="@+id/viewPager_banner"
        android:layout_marginRight="12dp"
        android:layout_marginBottom="16dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:paddingTop="4dp"
        android:paddingBottom="4dp"
        android:background="@drawable/gray_ellipse">
    </LinearLayout>

헷갈릴 것 같아서 코드의 일부분만 가져왔다.

우선, 만든 타원을 배너 위에 띄우려면 최상단에서 LinearLayout 같은 걸 사용하면 안 된다.

(뷰가 겹치는 것을 허락하지 않으므로)

그래서 나는 ConstraintLayout을 사용했다.

 

아까 만든 타원은 LinearLayout의 배경으로 설정했다.

그 이유는 타원 안에 가로방향으로 텍스트를 집어넣을 것이기 때문!

그리고 margin값과 padding값을 적절히 조절해가며 위치를 잡아주면 된다.

 

* ConstraintLayout, margin, padding에 대한 개념이 잡혀있지 않다면 여기서 막힐 수도 있어요.

 

    <LinearLayout // 방금 앞에서 보여줬던 LinearLayout
        android:id="@+id/linear_layout_see_all"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintRight_toRightOf="@+id/viewPager_banner"
        app:layout_constraintBottom_toBottomOf="@+id/viewPager_banner"
        android:layout_marginRight="12dp"
        android:layout_marginBottom="16dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:paddingTop="4dp"
        android:paddingBottom="4dp"
        android:background="@drawable/gray_ellipse">

        <TextView // LinearLayout 안에다가 텍스트를 차곡차곡 넣어준다.
            android:id="@+id/textView_current_banner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="1"
            android:textSize="10dp"
            android:textColor="#FFFFFF" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:text="/"
            android:textSize="10dp"
            android:textColor="#FFFFFF" />

        <TextView
            android:id="@+id/textView_total_banner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="15"
            android:textSize="10dp"
            android:textColor="#FFFFFF" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="4dp"
            android:text="모두보기"
            android:textSize="10dp"
            android:textColor="#FFFFFF" />
    </LinearLayout>

그다음엔 타원 안에 들어갈 텍스트뷰를 하나씩 넣어주면 된다.

위 코드는 앞서 보여주었던 activity_main.xml 안에 있는 LinearLayout에다가

텍스트 뷰를 4개 추가한 모습이다.

 

MainActivity.kt
class MainActivity : AppCompatActivity() {
    
    private var numBanner = 3 // 배너 갯수

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

        viewPager_banner.adapter = ViewPagerAdapter(getBannerList())
        viewPager_banner.orientation = ViewPager2.ORIENTATION_HORIZONTAL

        textView_total_banner.text = numBanner.toString()

        // 현재 몇번째 배너인지 보여주는 숫자를 변경함
        viewPager_banner.apply {
            registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback(){
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)
                    textView_current_banner.text = "${position+1}" // position이 0부터 시작해서 +1
                }
            })
        }

        linear_layout_see_all.setOnClickListener {
            Toast.makeText(this, "모두 보기 클릭했음", Toast.LENGTH_SHORT).show()
        }
    }

    private fun getBannerList(): ArrayList<Int> {
        return arrayListOf<Int>(R.drawable.banner1, R.drawable.banner2, R.drawable.banner3)
    }
}

이제 메인 액티비티에서 페이지가 변경될 때마다

몇 번째 배너인지 텍스트를 변경해주면 끝이다.

 

모두 보기를 클릭했을 때 페이지를 띄우거나 하는 작업은

리니어 레이아웃에 setOnClickListener를 달아주면 된다.

 

 

2-2. 무한 뷰페이저

무한 뷰페이저

 

배달의 민족 앱을 보면 배너의 끝에 도달했을 때 페이지를 넘기면

다시 맨 처음부터 배너가 시작되는 걸 볼 수 있다.

정확한 명칭은 없고 무한 뷰페이저, infinite viewPager 정도로 불리는 것 같다.

아무튼 이번엔 이걸 적용해볼 것이다.

 

우선 이걸 지원하는 메서드는 따로 없고 약간의 편법을 사용해야 한다.

외국 사이트도 뒤져봤는데 이 방법밖엔 없는 듯하다.

 

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

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

    override fun getItemCount(): Int = Int.MAX_VALUE	// 아이템의 갯수를 임의로 확 늘린다.

    override fun onBindViewHolder(holder: PagerViewHolder, position: Int) {
        holder.banner.setImageResource(item[position%3])	// position에 3을 나눈 나머지 값을 사용한다.
    }

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

        val banner = itemView.imageView_banner!!
    }

}

기존의 코드에 추가한 부분은 딱 2줄이다.

원래는 getItemCount에서 리스트의 사이즈를 리턴해주었는데

이걸 Int가 표현할 수 있는 가장 큰 숫자인 (몇이더라... 몇억 됐던 거 같은데) 억 자리 숫자를 리턴한다.

즉, 그만큼 아이템 개수를 늘리겠다는 것이다. 마치 무한으로 스크롤이 되는 것처럼!

내가 알기론 화면에 보이지 않는 아이템은 아직 생성이 되지 않기 때문에 이 방법이 가능한 것 같다.

또한 리사이클러뷰는 destroy를 알아서 하기 때문에 문제가 없지만,

만약 fragment를 사용할 거라면 destory를 구현해주어야 할 것 같다. (이건 아직 안 해봐서..)

 

그리고 position 값을 사용할 때는 원하는 배너의 개수만큼 나눈 나머지 값을 사용한다.

예를 들어 배너가 3개라고 했을 때

 

0 % 3 = 0  -  첫 번째 배너

1 % 3 = 1  -  두 번째 배너

2 % 3 = 2  -  세 번째 배너

 

(여기서 계속 페이지를 넘기면)

 

3 % 3 = 0  -  첫 번째 배너

4 % 3 = 1  -  두 번째 배너

5 % 3 = 2  -  세 번째 배너

 

이렇게 페이지를 계속 넘김에 따라 position은 계속 늘어나지만

3으로 나눔으로써 배너를 반복해서 사용할 수 있는 것이다.

 

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private var numBanner = 3
    private var currentPosition = Int.MAX_VALUE / 2

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

        viewPager_banner.adapter = ViewPagerAdapter(getBannerList())
        viewPager_banner.orientation = ViewPager2.ORIENTATION_HORIZONTAL
        viewPager_banner.setCurrentItem(currentPosition, false) // 현재 위치를 지정
        textView_total_banner.text = numBanner.toString()

        // 현재 몇번째 배너인지 보여주는 숫자를 변경함
        viewPager_banner.apply {
            registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback(){
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)
                    textView_current_banner.text = "${(position%3)+1}" // 여기서도 %3
                }
            })
        }
    }

    private fun getBannerList(): ArrayList<Int> {
        return arrayListOf<Int>(R.drawable.banner1, R.drawable.banner2, R.drawable.banner3)
    }
}

메인 액티비티에서도 position을 사용할 때 '%3' 이런 식으로 사용하면 된다.

중요한 건 currentPosition이다. 현재의 위치를 Int.MAX_VALUE / 2로 정해주었는데

왼쪽으로도 무한 스크롤, 오른쪽으로도 무한 스크롤을 할 수 있도록

현재 위치를 딱 중간으로 설정해 준 것이다.

이때 사용한 메서드가 현재 위치를 설정하는 setCurrentItem이다.

 

 

true 값을 주면 이렇게 됨

setCurrentItem에 두번째 매개변수로 true값을 주면

이렇게 맨 첫번째 배너로 미끄러지듯이 이동하면서 시작하기 때문에

false값을 넣어주면 된다.

 

 

2-3. 자동으로 스크롤 하기

배달의 민족 배너는 사용자가 움직이지 않아도 일정 시간이 지나면

스스로 다음 페이지로 넘어가면서 배너를 보여준다.

여기서 3가지 규칙이 있는 걸 알 수 있다.

 

  • 사용자가 터치를 하지 않으면 자동으로 페이지가 넘어간다.
  • 사용자가 배너를 터치해서 드래그하면 페이지가 움직이지 않는다.
  • 사용자가 배너를 터치한 상태로 드래그를 하지 않으면 페이지가 넘어간다.

이렇게 3가지 규칙을 똑같이 적용하고 싶었다.

인터넷에 올라와 있는 글들은 이런 규칙과 상관없이 무조건 자동으로 페이지가 넘어가게 되어있어서

이것저것 시도해본다고 밤을 꼬박 새웠다 ㅠㅠ

 

 

pzienowicz/androidx-auto-scroll-view-pager

Auto scroll viewpager working with Androidx libraries - pzienowicz/androidx-auto-scroll-view-pager

github.com

열심히 글을 찾아보다가 안 되겠다 싶어서

autoScroll을 지원하는 위 라이브러리 코드를 분석해서 일부 참고했다.

 

MainActivity.kt
class MainActivity : AppCompatActivity() {

    private var numBanner = 3
    private var currentPosition = Int.MAX_VALUE / 2
    private var myHandler = MyHandler()
    private val intervalTime = 1500.toLong() // 몇초 간격으로 페이지를 넘길것인지 (1500 = 1.5초)

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

        textView_total_banner.text = numBanner.toString()
        viewPager_banner.adapter = ViewPagerAdapter(getBannerList())
        viewPager_banner.orientation = ViewPager2.ORIENTATION_HORIZONTAL
        viewPager_banner.setCurrentItem(currentPosition, false)

        viewPager_banner.apply {
            registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)
                    textView_current_banner.text = "${(position % 3) + 1}"
                }

                override fun onPageScrollStateChanged(state: Int) {
                    super.onPageScrollStateChanged(state)
                    when (state) {
                        // 뷰페이저에서 손 떼었을때 / 뷰페이저 멈춰있을 때
                        ViewPager2.SCROLL_STATE_IDLE -> autoScrollStart(intervalTime)
                        // 뷰페이저 움직이는 중
                        ViewPager2.SCROLL_STATE_DRAGGING -> autoScrollStop()
                    }
                }
            })
        }
    }

    private fun autoScrollStart(intervalTime: Long) {
        myHandler.removeMessages(0) // 이거 안하면 핸들러가 1개, 2개, 3개 ... n개 만큼 계속 늘어남
        myHandler.sendEmptyMessageDelayed(0, intervalTime) // intervalTime 만큼 반복해서 핸들러를 실행하게 함
    }

    private fun autoScrollStop(){
        myHandler.removeMessages(0) // 핸들러를 중지시킴
    }

    private inner class MyHandler : Handler() {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)

            if(msg.what == 0) {
                viewPager_banner.setCurrentItem(++currentPosition, true) // 다음 페이지로 이동
                autoScrollStart(intervalTime) // 스크롤을 계속 이어서 한다.
            }
        }
    }

    // 다른 페이지 갔다가 돌아오면 다시 스크롤 시작
    override fun onResume() {
        super.onResume()
        autoScrollStart(intervalTime)
    }

    // 다른 페이지로 떠나있는 동안 스크롤이 동작할 필요는 없음. 정지
    override fun onPause() {
        super.onPause()
        autoScrollStop()
    }

    private fun getBannerList(): ArrayList<Int> {
        return arrayListOf<Int>(R.drawable.banner1, R.drawable.banner2, R.drawable.banner3)
    }
}

시간이 지남에 따라 페이지를 넘기는 작업은 동적으로 진행되어야 해서 핸들러를 사용했다.

onPageScrollStateChanged 메서드에 들어오는 state값을 이용하면 뷰페이저의 상태를 알 수 있다.

이 상태에 따라 자동 스크롤을 시작할지, 정지할지를 정해준다.

 

만약 자동 스크롤이 시작되어 페이지가 한 번 넘어갔다고 치자

그러면 잠시 멈췄다가 또 다음 페이지로 넘어가게 되는데

페이지가 넘어갈 때마다 매번 멈추다 보니

매번 autoScrollStart를 호출해서 핸들러가 중첩돼버린다.

그래서 removeMessages를 넣어주어 핸들러를 삭제해준 것이다.

 

sendEmptyMessageDelayed는 메시지 큐에 메시지를 보내는 메서드이다.

0번째 위치에 '1500'이라는 메시지를 보냈고

이 메시지는 MyHandler 이너 클래스에서 받아서 처리한다.

 

또 중요한 것이 onResume에서 autoScrollStart를

onPause에서 autoScrollStop을 해주는 것이다.

그 이유는 주석으로 달아 놓았고

이렇게 라이프 사이클에 맞게 적절히 시작과 정지를 해주어야 한다.

 

 

안드로이드 스레드. 핸들러와 메시지. (Android Thread. Handler and Message)

1. 안드로이드 메인 스레드. [안드로이드 스레드]에서 설명한 내용 중에서 메인 스레드와 관련된 내용을 간단히 정리해볼까요? 스레드란, 프로세스 내에서 "순차적으로 실행되는 실행 흐름"의 최

recipes4dev.tistory.com

만약 핸들러와 메시지에 대한 이해가 부족하면 위에 글을

 

 

안드로이드 액티비티 생명주기 (Activity Life cycle)

1. 생명 주기  1-1. 생명 주기란?  1-2. onCreate  1-3. onStart  1-4. onResume  1-5. onPause  1-6. onStop  1-7. onRestart  1-8. onDestroy 2. 실행 영상  2-1. 앱을 실행시킬 때  2-2. 홈 화면으..

todaycode.tistory.com

생명주기에 대한 이해가 부족하면 위에 글을 읽어보시길 추천드립니다!

 

이렇게 총 두 편의 게시물에 거쳐 viewPager2에 대해 공부해봤습니다.

처음엔 쉽다 싶었는데 점점 어려웠어요 ㅠㅠ

혹시 잘 안 되는 거 있으면 댓글 남겨주세요!

 

 

3. 예제 파일(깃허브)

 

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

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

github.com

포스팅에 올리기엔 코드가 너무 많아서 깃허브에 업로드 해두었습니다.

ViewPager 폴더를 다운받으셔서 앱 실행해보시면 기본편, 활용편 총 10가지의 예제를 확인하실 수 있습니다 :)

 

 

반응형

댓글