Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

ViewPager에서 ViewPager2로 이전

ViewPager2ViewPager 라이브러리의 개선된 버전으로, 향상된 기능을 제공하며 ViewPager 사용 시 발생하는 일반적인 문제를 해결합니다. 앱에서 ViewPager를 이미 사용하고 있는 경우 이 페이지에서 ViewPager2로 이전하는 방법을 자세히 알아보세요.

현재 ViewPager를 사용하고 있지 않으나 앱에서 ViewPager2를 사용하려는 경우 ViewPager2로 프래그먼트 간 슬라이드ViewPager2를 사용하여 탭으로 스와이프 뷰 만들기에서 자세한 내용을 알아보세요.

ViewPager2로 이전하여 얻을 수 있는 이점

이전하는 주된 이유는 ViewPager2는 적극적인 개발 지원을 받고 있지만 ViewPager는 그러지 않기 때문입니다. 또한, ViewPager2에는 몇 가지 구체적인 이점이 있습니다.

세로 방향 지원

ViewPager2는 기존 가로 페이징은 물론 세로 페이징도 지원합니다. android:orientation 속성을 설정하여 ViewPager2 요소의 세로 페이징을 사용 설정할 수 있습니다.

<androidx.viewpager2.widget.ViewPager2
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:orientation="vertical" />
    

setOrientation() 메서드를 사용하여 프로그래밍 방식으로 이 속성을 설정할 수도 있습니다.

오른쪽에서 왼쪽 지원

ViewPager2는 오른쪽에서 왼쪽(RTL) 페이징을 지원합니다. RTL 페이징은 언어에 따라 해당하는 경우 자동으로 사용 설정되지만 android:layoutDirection 속성을 설정하여 ViewPager2 요소의 RTL 페이징을 수동으로 사용 설정할 수도 있습니다.

<androidx.viewpager2.widget.ViewPager2
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layoutDirection="rtl" />
    

setLayoutDirection() 메서드를 사용하여 프로그래밍 방식으로 이 속성을 설정할 수도 있습니다.

수정 가능한 프래그먼트 컬렉션

ViewPager2는 수정 가능한 프래그먼트 컬렉션을 통해 페이징을 지원하며 기본 컬렉션이 변경되면 notifyDatasetChanged()를 호출하여 UI를 업데이트합니다.

즉, 앱은 런타임 시 프래그먼트 컬렉션을 동적으로 수정할 수 있고, 그러면 ViewPager2는 수정된 컬렉션을 올바르게 표시합니다.

DiffUtil

ViewPager2RecyclerView를 기반으로 빌드되므로 DiffUtil 유틸리티 클래스에 액세스할 수 있습니다. 이로 인해 여러 이점을 얻을 수 있는데, 무엇보다도 ViewPager2 객체는 기본적으로 RecyclerView 클래스의 데이터세트 변경 애니메이션을 활용할 수 있습니다.

ViewPager2로 앱 이전

앱의 ViewPager 객체를 ViewPager2로 업데이트하려면 다음 단계를 따르세요.

XML 레이아웃 파일 업데이트

먼저 XML 레이아웃 파일의 ViewPager 요소를 ViewPager2 요소로 바꿉니다.

<!-- A ViewPager element -->
    <android.support.v4.view.ViewPager
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- A ViewPager2 element -->
    <androidx.viewpager2.widget.ViewPager2
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    

어댑터 클래스 업데이트

ViewPager를 사용하는 경우 객체로 새 페이지를 제공하는 어댑터 클래스를 확장해야 합니다. ViewPager는 사용 사례에 따라 세 가지 추상 클래스를 사용합니다. ViewPager2는 두 가지 추상 클래스만 사용합니다.

ViewPager2 객체로 변환하는 ViewPager 객체마다 다음과 같이 어댑터 클래스를 업데이트하여 적절한 추상 클래스를 확장합니다.

생성자 매개변수

FragmentPagerAdapter 또는 FragmentStatePagerAdapter에서 상속되는 프래그먼트 기반 어댑터 클래스에서는 항상 단일 FragmentManager 객체가 생성자 매개변수로 허용됩니다. ViewPager2 어댑터 클래스의 FragmentStateAdapter를 확장하면 생성자 매개변수에 관해 다음 옵션을 선택할 수 있습니다.

  • ViewPager2 객체가 있는 FragmentActivity 객체 또는 Fragment 객체. 대부분의 경우 이 옵션을 사용하는 것이 더 좋습니다.
  • FragmentManager 객체 및 Lifecycle 객체.

RecyclerView.Adapter에서 직접 상속되는 뷰 기반 어댑터 클래스에는 생성자 매개변수가 필요하지 않습니다.

메서드 재정의

어댑터 클래스에서 재정의해야 하는 ViewPager2의 메서드도 ViewPager의 메서드와 다릅니다.

  • getCount()가 아닌 getItemCount()를 재정의합니다. 이 메서드에서는 이름만 변경됩니다.
  • 프래그먼트 기반 어댑터 클래스에서는 getItem()가 아닌 createFragment()를 재정의합니다. 함수가 호출될 때마다 새 createFragment() 메서드가 인스턴스를 재사용하는 것이 아니라 항상 새 프래그먼트 인스턴스를 제공하는지 확인하세요.

요약

요약하면 ViewPager 어댑터 클래스를 ViewPager2와 함께 사용할 수 있도록 변환하려면 다음과 같이 변경해야 합니다.

  1. 슈퍼클래스는 뷰를 통해 페이징하는 경우 RecyclerView.Adapter로, 프래그먼트를 통해 페이징하는 경우 FragmentStateAdapter로 변경합니다.
  2. 프래그먼트 기반 어댑터 클래스에서는 생성자 매개변수를 변경합니다.
  3. getCount()가 아닌 getItemCount()를 재정의합니다.
  4. 프래그먼트 기반 어댑터 클래스에서는 getItem()가 아닌 createFragment()를 재정의합니다.

Kotlin

    // A simple ViewPager adapter class for paging through fragments
    class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
        override fun getCount(): Int = NUM_PAGES

        override fun getItem(position: Int): Fragment = ScreenSlidePageFragment()
    }

    // An equivalent ViewPager2 adapter class
    class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
        override fun getItemCount(): Int = NUM_PAGES

        override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment()
    }
    

자바

    // A simple ViewPager adapter class for paging through fragments
    public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return new ScreenSlidePageFragment();
        }

        @Override
        public int getCount() {
            return NUM_PAGES;
        }
    }

    // An equivalent ViewPager2 adapter class
    private class ScreenSlidePagerAdapter extends FragmentStateAdapter {
        public ScreenSlidePagerAdapter(FragmentActivity fa) {
            super(fa);
        }

        @Override
        public Fragment createFragment(int position) {
            return new ScreenSlidePageFragment();
        }

        @Override
        public int getItemCount() {
            return NUM_PAGES;
        }
    }
    

TabLayout 인터페이스 리팩터링

ViewPager2에서는 TabLayout 통합이 변경되었습니다. ViewPagerTabLayout 객체와 함께 사용하여 탐색용 가로 탭을 표시하고 있는 경우 ViewPager2와 통합하려면 TabLayout 객체를 리팩터링해야 합니다.

TabLayoutViewPager2에서 분리되었으며, 이제 머티리얼 구성요소의 일부로 사용할 수 있습니다. 즉, 이 요소를 사용하려면 다음과 같이 적합한 종속성을 build.gradle 파일에 추가해야 합니다.

    implementation "com.google.android.material:material:1.1.0-beta01"
    

XML 레이아웃 파일 계층 구조에서 TabLayout 요소의 위치도 변경해야 합니다. ViewPager에서는 TabLayout 요소가 ViewPager 요소의 하위 요소로 선언되지만 ViewPager2에서는 TabLayout 요소가 ViewPager2 요소 바로 위에 동일한 수준으로 선언됩니다.

<!-- A ViewPager element with a TabLayout -->
    <androidx.viewpager.widget.ViewPager
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </androidx.viewpager.widget.ViewPager>

    <!-- A ViewPager2 element with a TabLayout -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1" />

    </LinearLayout>
    

마지막으로 TabLayout 객체를 ViewPager 객체에 첨부하는 코드를 업데이트해야 합니다. TabLayoutViewPager와 통합하는 데는 자체 setupWithViewPager() 메서드를 사용하지만 ViewPager2와 통합하는 데는 TabLayoutMediator 인스턴스가 있어야 합니다.

TabLayoutMediator 객체는 TabLayout 객체의 페이지 제목을 생성하는 작업도 처리합니다. 따라서 어댑터 클래스가 getPageTitle()를 재정의할 필요가 없습니다.

Kotlin

    // Integrating TabLayout with ViewPager
    class CollectionDemoFragment : Fragment() {
        ...
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            val tabLayout = view.findViewById(R.id.tab_layout)
            tabLayout.setupWithViewPager(viewPager)
        }
        ...
    }

    class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {

        override fun getCount(): Int  = 4

        override fun getPageTitle(position: Int): CharSequence {
            return "OBJECT ${(position + 1)}"
        }
        ...
    }

    // Integrating TabLayout with ViewPager2
    class CollectionDemoFragment : Fragment() {
        ...
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            val tabLayout = view.findViewById(R.id.tab_layout)
            TabLayoutMediator(tabLayout, viewPager) { tab, position ->
                tab.text = "OBJECT ${(position + 1)}"
            }.attach()
        }
        ...
    }
    

자바

    // Integrating TabLayout with ViewPager
    public class CollectionDemoFragment extends Fragment {
        ...
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            TabLayout tabLayout = view.findViewById(R.id.tab_layout);
            tabLayout.setupWithViewPager(viewPager);
        }
        ...
    }

    public class DemoCollectionPagerAdapter extends FragmentStatePagerAdapter {
        ...
        @Override
        public int getCount() {
            return 4;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "OBJECT " + (position + 1);
        }
        ...
    }

    // Integrating TabLayout with ViewPager2
    public class CollectionDemoFragment : Fragment() {
        ...
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            TabLayout tabLayout = view.findViewById(R.id.tab_layout);
            new TabLayoutMediator(tabLayout, viewPager,
                    (tab, position) -> tab.setText("OBJECT " + (position + 1))
            ).attach();
        }
        ...
    }
    

중첩된 스크롤 가능 요소 지원

스크롤 뷰와 이 스크롤 뷰를 포함하는 ViewPager2 객체의 방향이 같은 경우 ViewPager2는 기본적으로 중첩된 스크롤 뷰를 지원하지 않습니다. 예를 들어 세로 방향 ViewPager2 객체 내의 세로 스크롤 뷰에서는 스크롤이 작동하지 않습니다.

방향이 동일한 ViewPager2 객체 내의 스크롤 뷰를 지원하려면 중첩된 요소 스크롤이 예상되는 경우 ViewPager2 객체의 requestDisallowInterceptTouchEvent()를 호출해야 합니다. ViewPager2 중첩 스크롤 샘플은 다목적 맞춤 래퍼 레이아웃을 사용하여 이 문제를 해결하는 한 가지 방법을 보여줍니다.

추가 리소스

ViewPager2에 관해 자세히 알아보려면 다음 추가 리소스를 참조하세요.

샘플

동영상