Android Developer

Androidx ViewPager2 정리

졸려질려 2022. 6. 21. 20:00
반응형

목차

더보기

0. Dependency

1. ViewPager2 in XML

2. ViewPager2 Adapter

3. ViewPager2 in Code

4. Disable Touch Swipe

5. OnPageChangeCallback

6. Move Page Programmatically


0. Dependency

dependencies {
    implementation "androidx.viewpager2:viewpager2:1.0.0"
}

 ViewPager2 를 사용하려는 프로젝트가 Androidx 패키지를 전반적으로 사용하고 있다면 ViewPager2 의 Dependency 를 따로 추가해줄 필요는 없다. 만약, ViewPager2 가 인식되지 않는다면 Dependency 를 추가한다.


1. ViewPager2 in XML

...

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/my_viewpager2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    
...

 ViewPager2 를 넣고 싶은 XML 파일 내 위치에 위와 같이 넣어주면된다. 기본적인 위젯 설정(id, padding, margin 등등) 은 개발 목적에 맞게 추가로 넣어주면 된다.


2. ViewPager2 Adapter

class ViewPager2Adapter(
    private val context: FragmentActivity,
    private val fragmentList: List<Fragment>
) : FragmentStateAdapter(context) {
    override fun getItemCount(): Int = fragmentList.size

    override fun createFragment(position: Int): Fragment = fragmentList[position]
}

 ViewPager2 를 사용해서 일련의 Fragment 들을 슬라이드 형식으로 보여주고 싶다면, FragmentStateAdapter 를 상속받아서 Adapter 를 구현한다. 부모 클래스인 FragmentStateAdapter 는 FragmentActivity 형의 인자값이 필요하므로, 구현하고자 하는 Adapter 의 생성자를 통해서 부모 클래스로 전달할 인자값을 받아준다. FragmentActivity 는 AppCompatActivity 의 부모 클래스이므로, Adapter 를 사용하는 Activity 가 AppCompatActivity 를 상속받았다면 Activity 를 그대로 인자로 전달한다.

AppCompatActivity 의 부모클래스인 FragmentActivity

 FragmentStateAdapter 를 상속받았다면, "getItemCount()" 와 "createFragment(position: Int)" 메소드를 구현해줘야한다. "getItemCount" 는 RecyclerView 의 getItemCount 와 같이 Adatper 에서 보여주고자 하는 화면의 개수(List 의 크기) 를 반환해주도록 한다. 그리고 "createFragment()" 는 현재 페이지에서 출력하고자 하는 Fragment 를 반환해주도록 구현한다.


3. ViewPager2 in Code

 1, 2 단계를 거쳤다면, 이제 Code 에서 View 의 ViewPager2 와 Adapter 를 연결해준다. 위에서 구현한 ViewPager2Adapter 클래스는 FragmentActivity 형의 인자값과 List<Fragment> 형의 인자가 필요하다.

3-1) ViewPager2 변수 선언 및 초기화

...
val viewPagerAdapter = ViewPager2Adapter(this@MyActivity, fragmentList)
...

 위에서 언급했듯이, AppCompatActivity 의 부모클래스가 FragmentActivity 이므로, Adapter 를 선언하는 Activity 가 AppCompatActivity 를 상속받았다면 그대로 this 를 써주면 된다. 그리고 List<Fragment> 형의 fragmentList 변수를 넣어준다. 두번째 인자값은 필수는 아니므로, 만약 List<Fragment> 를 Adapter 내부에서만 사용하고 싶다면 Adapter 내부에 fragmentList 를 선언 및 초기화 해주고, 인자값은 전달하지 않아도 된다. 즉, 첫번째 인자값인 FragmentAcitivty 변수는 필수적이고, 그 외에는 Adapter 를 생성하면서 필요한 값들을 전달하면 된다.

 만약 Adapter 를 전역 변수로 사용하고 싶은데, val 로 선언하고 싶다면 lazy 를 사용하여 선언해주면 된다.

...
private val viewPagerAdapter by lazy {
    ViewPager2Adapter(this@MyActivity, fragmentList)
}
...

3-2) ViewPager2 View 와 Adapter 연결

// No ViewBinding 
val viewPager2 = findViewById(R.id.my_viewpager2)
viewPager2.adapter = viewPager2Adapter

// ViewBinding
binding.myViewpager2.adapter = viewPager2Adapter

 ViewBinding 을 사용하지 않을 경우 위쪽 코드를 사용하고, ViewBinding 이 적용된 상태라면 아래 코드를 사용하면 된다. 이 정도까지 구현하면, fragmentList 에 있는 Fragment 들을 슬라이드 방식으로 볼 수 있다. 이제 4번째 파트부터는 ViewPager2 를 직접 사용하면서 추가로 구현한 정보들을 정리한다.


4. Disable Touch Swipe

// No ViewBinding 
val viewPager2 = findViewById(R.id.my_viewpager2)
viewPager2.isUserInputEnabled = false
...

// ViewBinding
binding.myViewpager2.isUserInputEnabled = false
...

 ViewPager2 는 기본적으로 터치 스와이프 방식으로 페이지 이동이 가능하다. 그러나, 회원가입 같이 특정 조건을 만족해야 페이지를 넘겨야하는 경우, 터치 스와이프 방식보단 버튼으로 페이지를 넘기는 방식이 더 좋을 것이다. 그럴 때는 코드 내 ViewPager2 위젯의 변수를 사용해서 "isUserInputEnabled" 값을 false 로 설정해주면 된다.


5. OnPageChangeCallback

val viewPager2 = findViewById(R.id.my_viewpager2)
viewPager2.registerOnPageChangeCallback(object: ViewPager2.OnPageChangeCallback() {
    override fun onPageScrollStateChanged(state: Int) {
        super.onPageScrollStateChanged(state)
    }

    override fun onPageScrolled(
        position: Int,
        positionOffset: Float,
        positionOffsetPixels: Int
    ) {
        super.onPageScrolled(position, positionOffset, positionOffsetPixels)
    }
    
    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
    }
})
...

 ViewPager2 의 Callback 은 Interface 타입이 아니라 abstract class 타입이다. 따라서, 익명 클래스를 구현해서 "registerOnPageChangeCallback()" 메소드의 인자값으로 전달한다. OnPageChangeCallback 내부의 "onPageScrollStateChanged()", "onPageScrolled()", "onPageSelected()" 메소드들은 Default 로 구현되어있으므로, 무조건 3개의 함수를 override 하지 않아도 된다. 필요한 메소드만 override 하면 된다.

5-1) onPageScrollStateChanged

public void onPageScrollStateChanged(@ViewPager2.ScrollState int state)

 Scroll 상태가 변경될 때마다 호출되는 메소드이다.

[ ViewPager2 Scroll State ]
- SCROLL_STATE_IDLE : 0
- SCROLL_STATE_DRAGGING : 1
- SCROLL_STATE_SETTLING : 2

5-2) onPageScrolled

public void onPageScrolled(
    int position,
    float positionOffset,
    @Px int positionOffsetPixels
)

 현재 페이지가 스크롤 되었을 때 호출되는 메소드이다. 코드로 스크롤되거나, 유저가 터치 스와이프를 통해 스크롤 했을 때 모두 콜백된다.

- position : 현재 보여지고 있는 페이지의 Position Index 값이다. 
- positionOffset : 0과 1 사이의 float 값으로, position 기준에서 페이지의 offset 값을 뜻한다.
- positionOffsetPixels : positionOffset 값을 Pixel 단위로 나타낸 값이다.

 만약 페이지가 "Index 0" 에서 "Index 1" 으로 이동할 경우 Log 는 다음과 같다.

onPageScrolled: 0 / 0.0 / 0
onPageScrolled: 0 / 0.03010279 / 82
onPageScrolled: 0 / 0.04992658 / 136
onPageScrolled: 0 / 0.088472836 / 241
....... < 생략 > ......
onPageScrolled: 0 / 0.99779737 / 2718
onPageScrolled: 0 / 0.9988987 / 2721
onPageScrolled: 0 / 0.9996329 / 2723
onPageScrolled: 1 / 0.0 / 0

5-3) onPageSelected

public void onPageSelected(int position)

 새로운 페이지가 선택되었을 때 호출되는 메소드이다. Transform 애니메이션이 완료되지 않아도 호출된다.


6. Move Page Programmatically

val viewPager2 = findViewById(R.id.my_viewpager2)

viewPager2.currentItem = [TARGET_PAGE_POSITION]

 "4. Disable Touch Swipe" 를 적용할 경우, ViewPager2 의 페이지를 전환할 방법은 코드로 구현하는 것 밖에 없다. 이 때, ViewPager2 의 currentItem 값을 바꾸는 것으로 페이지를 이동시킬 수 있다. 코드로 페이지를 전환해도 OnPageChangeCallback 은 호출된다.


위 내용 외에 ViewPager2 를 직접 사용하면서 추가로 필요했던 부분들을 본 글에서 계속 업데이트할 예정이다.

작성일 : 2022.06.21

반응형