ViewPager로 프래그먼트 간 슬라이드

화면 슬라이드는 하나의 전체 화면에서 다른 전체 화면으로 전환하는 것으로, 설정 마법사 또는 슬라이드쇼와 같은 UI에서 일반적으로 사용됩니다. 이 과정에서는 지원 라이브러리에서 제공하는 ViewPager로 화면 슬라이드를 실행하는 방법을 보여줍니다. ViewPager 객체는 화면 슬라이드에 자동으로 애니메이션을 적용할 수 있습니다. 다음은 콘텐츠의 한 화면에서 다음 화면으로 전환되는 화면 슬라이드입니다.

화면 슬라이드 애니메이션
 

ViewPager는 AndroidX의 일부입니다. 자세한 내용은 AndroidX 사용을 참조하세요.

뷰 만들기

나중에 프래그먼트 콘텐츠에 사용할 레이아웃 파일을 만듭니다. 프래그먼트 콘텐츠에 대한 문자열도 정의해야 합니다. 다음 예제에는 텍스트를 표시하는 텍스트 뷰가 포함되어 있습니다.

    <!-- fragment_screen_slide_page.xml -->
    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <TextView style="?android:textAppearanceMedium"
            android:padding="16dp"
            android:lineSpacingMultiplier="1.2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/lorem_ipsum" />
    </ScrollView>
    

프래그먼트 만들기

onCreateView() 메서드에서 방금 만든 레이아웃을 반환하는 Fragment 클래스를 만듭니다. 그러면 사용자에게 표시할 새 페이지가 필요할 때마다 상위 활동에서 이 프래그먼트의 인스턴스를 만들 수 있습니다.

Kotlin

    import android.support.v4.app.Fragment

    class ScreenSlidePageFragment : Fragment() {

        override fun onCreateView(
                inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?
        ): View = inflater.inflate(R.layout.fragment_screen_slide_page, container, false)
    }
    

자바

    import android.support.v4.app.Fragment;
    ...
    public class ScreenSlidePageFragment extends Fragment {

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            ViewGroup rootView = (ViewGroup) inflater.inflate(
                    R.layout.fragment_screen_slide_page, container, false);

            return rootView;
        }
    }
    

ViewPager 추가

ViewPager 객체에는 페이지 간 전환을 위한 스와이프 동작이 내장되어 있으며 기본적으로 화면 슬라이드 애니메이션을 표시하므로 직접 애니메이션을 만들 필요가 없습니다. ViewPager는 표시할 새 페이지의 요소로 PagerAdapter 객체를 사용하므로 PagerAdapter는 개발자가 이전에 만든 프래그먼트 클래스를 사용합니다.

시작하려면 ViewPager가 포함된 레이아웃을 만듭니다.

    <!-- activity_screen_slide.xml -->
    <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" />
    

다음을 실행하는 활동을 만듭니다.

  • 콘텐츠 뷰를 ViewPager가 있는 레이아웃으로 설정합니다.
  • FragmentStatePagerAdapter 추상 클래스를 확장하는 클래스를 만들고 getItem() 메서드를 구현하여 ScreenSlidePageFragment의 인스턴스를 새 페이지로 제공합니다. 페이저 어댑터는 어댑터에서 만들 페이지 수(예에서는 5개)를 반환하는 getCount() 메서드도 구현해야 합니다.
  • PagerAdapterViewPager에 연결합니다.

Kotlin

    import android.support.v4.app.Fragment
    import android.support.v4.app.FragmentManager
    ...
    /**
     * The number of pages (wizard steps) to show in this demo.
     */
    private const val NUM_PAGES = 5

    class ScreenSlidePagerActivity : FragmentActivity() {

        /**
         * The pager widget, which handles animation and allows swiping horizontally to access previous
         * and next wizard steps.
         */
        private lateinit var mPager: ViewPager

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

            // Instantiate a ViewPager and a PagerAdapter.
            mPager = findViewById(R.id.pager)

            // The pager adapter, which provides the pages to the view pager widget.
            val pagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager)
            mPager.adapter = pagerAdapter
        }

        override fun onBackPressed() {
            if (mPager.currentItem == 0) {
                // If the user is currently looking at the first step, allow the system to handle the
                // Back button. This calls finish() on this activity and pops the back stack.
                super.onBackPressed()
            } else {
                // Otherwise, select the previous step.
                mPager.currentItem = mPager.currentItem - 1
            }
        }

        /**
         * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
         * sequence.
         */
        private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
            override fun getCount(): Int = NUM_PAGES

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

자바

    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    ...
    public class ScreenSlidePagerActivity extends FragmentActivity {
        /**
         * The number of pages (wizard steps) to show in this demo.
         */
        private static final int NUM_PAGES = 5;

        /**
         * The pager widget, which handles animation and allows swiping horizontally to access previous
         * and next wizard steps.
         */
        private ViewPager mPager;

        /**
         * The pager adapter, which provides the pages to the view pager widget.
         */
        private PagerAdapter pagerAdapter;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_screen_slide);

            // Instantiate a ViewPager and a PagerAdapter.
            mPager = (ViewPager) findViewById(R.id.pager);
            pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
            mPager.setAdapter(pagerAdapter);
        }

        @Override
        public void onBackPressed() {
            if (mPager.getCurrentItem() == 0) {
                // If the user is currently looking at the first step, allow the system to handle the
                // Back button. This calls finish() on this activity and pops the back stack.
                super.onBackPressed();
            } else {
                // Otherwise, select the previous step.
                mPager.setCurrentItem(mPager.getCurrentItem() - 1);
            }
        }

        /**
         * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
         * sequence.
         */
        private 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;
            }
        }
    }
    

PageTransformer를 사용하여 애니메이션 맞춤설정

기본 화면 슬라이드 애니메이션과 다른 애니메이션을 표시하려면 ViewPager.PageTransformer 인터페이스를 구현하여 뷰 페이저에 제공합니다. 이 인터페이스는 단일 메서드 transformPage()를 노출합니다. 화면 전환의 각 지점에서 이 메서드는 표시되는 페이지(일반적으로 단 한 개의 페이지만 표시됨)마다, 그리고 화면에 표시되지 않는 인접 페이지에 대해 한 번씩 호출됩니다. 예를 들어, 페이지 3이 표시되고 사용자가 페이지 4로 드래그하면 동작의 각 단계에서 페이지 2, 3, 4에 대해 transformPage()가 호출됩니다.

그런 다음 transformPage() 구현에서 화면의 페이지 위치에 따라 변환해야 하는 페이지를 결정하여 맞춤 슬라이드 애니메이션을 만들 수 있습니다. 이 위치는 transformPage() 메서드의 position 매개변수에서 가져옵니다.

position 매개변수는 화면 중앙을 기준으로 특정 페이지의 위치를 나타냅니다. 이 매개변수는 사용자가 페이지를 스크롤할 때 변경되는 동적 속성입니다. 페이지가 화면을 채우면 위치 값은 0입니다. 페이지가 화면 오른쪽에서 벗어나면 위치 값은 1입니다. 사용자가 페이지 1과 페이지 2의 중간으로 스크롤하면 페이지 1의 위치 값은 -0.5이고 페이지 2의 위치 값은 0.5입니다. 화면의 페이지 위치에 따라 setAlpha(), setTranslationX() 또는 setScaleY()와 같은 메서드로 페이지 속성을 설정하여 맞춤 슬라이드 애니메이션을 만들 수 있습니다.

PageTransformer를 구현한 경우 구현과 함께 setPageTransformer()를 호출하여 맞춤 애니메이션을 적용합니다. 예를 들어, ZoomOutPageTransformer라는 이름의 PageTransformer가 있다면 다음과 같이 맞춤 애니메이션을 설정할 수 있습니다.

Kotlin

    val mPager: ViewPager = findViewById(R.id.pager)
    ...
    mPager.setPageTransformer(true, ZoomOutPageTransformer())
    

자바

    ViewPager mPager = (ViewPager) findViewById(R.id.pager);
    ...
    mPager.setPageTransformer(true, new ZoomOutPageTransformer());
    

PageTransformer의 예와 동영상은 페이지 축소 변환기심도 페이지 변환기 섹션을 참조하세요.

페이지 축소 변환기

이 페이지 변환기는 인접 페이지 사이를 스크롤하면 페이지가 축소되면서 페이드아웃됩니다. 페이지가 중앙에 가까워지면 원래 크기로 다시 커지면서 페이드인됩니다.

ZoomOutPageTransformer 예:
 

Kotlin

    private const val MIN_SCALE = 0.85f
    private const val MIN_ALPHA = 0.5f

    class ZoomOutPageTransformer : ViewPager.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
                    }
                }
            }
        }
    }
    

자바

    public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE = 0.85f;
        private static final float MIN_ALPHA = 0.5f;

        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();

            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0f);

            } else if (position <= 1) { // [-1,1]
                // Modify the default slide transition to shrink the page as well
                float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
                float vertMargin = pageHeight * (1 - scaleFactor) / 2;
                float horzMargin = pageWidth * (1 - scaleFactor) / 2;
                if (position < 0) {
                    view.setTranslationX(horzMargin - vertMargin / 2);
                } else {
                    view.setTranslationX(-horzMargin + vertMargin / 2);
                }

                // Scale the page down (between MIN_SCALE and 1)
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);

                // Fade the page relative to its size.
                view.setAlpha(MIN_ALPHA +
                        (scaleFactor - MIN_SCALE) /
                        (1 - MIN_SCALE) * (1 - MIN_ALPHA));

            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0f);
            }
        }
    }
    

심도 페이지 변환기

이 페이지 변환기에서는 페이지를 왼쪽으로 넘길 때에는 기본 슬라이드 애니메이션을 사용하고, 페이지를 오른쪽으로 넘길 때에는 '심도' 애니메이션을 사용합니다. 이 심도 애니메이션은 페이지를 페이드아웃하고 선형으로 축소합니다.

DepthPageTransformer 예:
 

심도 애니메이션에서는 기본 애니메이션(화면 슬라이드)이 여전히 있기 때문에, 음수의 X 변환을 지정하여 화면 슬라이드를 차단해야 합니다. 예를 들면 다음과 같습니다.

Kotlin

    view.translationX = -1 * view.width * position
    

자바

    view.setTranslationX(-1 * view.getWidth() * position);
    

다음은 작동하는 페이지 변환기에서 기본 화면 슬라이드 애니메이션을 차단하는 방법을 보여주는 예제입니다.

Kotlin

    private const val MIN_SCALE = 0.75f

    class DepthPageTransformer : ViewPager.PageTransformer {

        override fun transformPage(view: View, position: Float) {
            view.apply {
                val pageWidth = width
                when {
                    position < -1 -> { // [-Infinity,-1)
                        // This page is way off-screen to the left.
                        alpha = 0f
                    }
                    position <= 0 -> { // [-1,0]
                        // Use the default slide transition when moving to the left page
                        alpha = 1f
                        translationX = 0f
                        scaleX = 1f
                        scaleY = 1f
                    }
                    position <= 1 -> { // (0,1]
                        // Fade the page out.
                        alpha = 1 - position

                        // Counteract the default slide transition
                        translationX = pageWidth * -position

                        // Scale the page down (between MIN_SCALE and 1)
                        val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)))
                        scaleX = scaleFactor
                        scaleY = scaleFactor
                    }
                    else -> { // (1,+Infinity]
                        // This page is way off-screen to the right.
                        alpha = 0f
                    }
                }
            }
        }
    }
    

자바

    public class DepthPageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE = 0.75f;

        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();

            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0f);

            } else if (position <= 0) { // [-1,0]
                // Use the default slide transition when moving to the left page
                view.setAlpha(1f);
                view.setTranslationX(0f);
                view.setScaleX(1f);
                view.setScaleY(1f);

            } else if (position <= 1) { // (0,1]
                // Fade the page out.
                view.setAlpha(1 - position);

                // Counteract the default slide transition
                view.setTranslationX(pageWidth * -position);

                // Scale the page down (between MIN_SCALE and 1)
                float scaleFactor = MIN_SCALE
                        + (1 - MIN_SCALE) * (1 - Math.abs(position));
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);

            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0f);
            }
        }
    }