화면 슬라이드는 하나의 전체 화면에서 다른 전체 화면으로 전환하는 것으로, 설정 마법사 또는 슬라이드쇼와 같은 UI에서 일반적으로 사용됩니다. 이 주제에서는 ViewPager2
객체로 화면을 슬라이드하는 방법을 보여줍니다. ViewPager2
객체는 화면 슬라이드에 자동으로 애니메이션을 적용할 수 있습니다. 다음은 콘텐츠의 한 화면에서 다음 화면으로 전환하는 화면 슬라이드의 예입니다.
이를 건너뛰고 실제로 완벽히 작동하는 예를 보려면 GitHub에서 이 샘플 앱을 확인하세요. ViewPager2
를 사용하려면 프로젝트에 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 androidx.fragment.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 androidx.fragment.app.Fragment; ... public class ScreenSlidePageFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return (ViewGroup) inflater.inflate( R.layout.fragment_screen_slide_page, container, false); } }
ViewPager2 추가
ViewPager2
객체에는 페이지 간 전환을 위한 스와이프 동작이 내장되어 있으며 기본적으로 화면 슬라이드 애니메이션을 표시하므로 직접 애니메이션을 만들 필요가 없습니다. ViewPager2
는 표시할 새 페이지의 요소로 FragmentStateAdapter
객체를 사용하므로 FragmentStateAdapter
는 개발자가 이전에 만든 프래그먼트 클래스를 사용합니다.
시작하려면 ViewPager2
객체가 포함된 레이아웃을 만듭니다.
<!-- activity_screen_slide.xml --> <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" />
다음을 실행하는 활동을 만듭니다.
- 콘텐츠 뷰를
ViewPager2
가 있는 레이아웃으로 설정합니다. FragmentStateAdapter
추상 클래스를 확장하는 클래스를 만들고createFragment()
메서드를 구현하여ScreenSlidePageFragment
의 인스턴스를 새 페이지로 제공합니다. 페이저 어댑터는 어댑터에서 만들 페이지 수(예에서는 5개)를 반환하는getItemCount()
메서드도 구현해야 합니다.FragmentStateAdapter
를ViewPager2
객체에 연결합니다.
Kotlin
import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity ... /** * 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 viewPager: ViewPager2 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_screen_slide) // Instantiate a ViewPager2 and a PagerAdapter. viewPager = findViewById(R.id.pager) // The pager adapter, which provides the pages to the view pager widget. val pagerAdapter = ScreenSlidePagerAdapter(this) viewPager.adapter = pagerAdapter } override fun onBackPressed() { if (viewPager.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. viewPager.currentItem = viewPager.currentItem - 1 } } /** * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in * sequence. */ private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { override fun getItemCount(): Int = NUM_PAGES override fun createFragment(position: Int): Fragment = ScreenSlidePageFragment() } }
자바
import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; ... 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 ViewPager2 viewPager; /** * The pager adapter, which provides the pages to the view pager widget. */ private FragmentStateAdapter pagerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_screen_slide); // Instantiate a ViewPager2 and a PagerAdapter. viewPager = findViewById(R.id.pager); pagerAdapter = new ScreenSlidePagerAdapter(this); viewPager.setAdapter(pagerAdapter); } @Override public void onBackPressed() { if (viewPager.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. viewPager.setCurrentItem(viewPager.getCurrentItem() - 1); } } /** * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in * sequence. */ 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; } } }
PageTransformer를 사용하여 애니메이션 맞춤설정
기본 화면 슬라이드 애니메이션과 다른 애니메이션을 표시하려면 ViewPager2.PageTransformer
인터페이스를 구현하여 ViewPager2
객체에 제공합니다. 이 인터페이스는 단일 메서드 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 viewPager: ViewPager2 = findViewById(R.id.pager) ... viewPager.setPageTransformer(ZoomOutPageTransformer())
자바
ViewPager2 viewPager = findViewById(R.id.pager); ... viewPager.setPageTransformer(new ZoomOutPageTransformer());
PageTransformer
의 예와 동영상은 페이지 축소 변환기 및 심도 페이지 변환기 섹션을 참조하세요.
페이지 축소 변환기
이 페이지 변환기는 인접 페이지 사이를 스크롤하면 페이지가 축소되면서 페이드아웃됩니다. 페이지가 중앙에 가까워지면 원래 크기로 다시 커지면서 페이드인됩니다.
ZoomOutPageTransformer
예:Kotlin
private const val MIN_SCALE = 0.85f private const val MIN_ALPHA = 0.5f 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 } } } } }
자바
public class ZoomOutPageTransformer implements ViewPager2.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 @RequiresApi(21) class DepthPageTransformer : ViewPager2.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 translationZ = 0f scaleX = 1f scaleY = 1f } position <= 1 -> { // (0,1] // Fade the page out. alpha = 1 - position // Counteract the default slide transition translationX = pageWidth * -position // Move it behind the left page translationZ = -1f // 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 } } } } }
자바
@RequiresApi(21) public class DepthPageTransformer implements ViewPager2.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.setTranslationZ(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); // Move it behind the left page view.setTranslationZ(-1f); // 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); } } }
참고 자료
ViewPager2
의 자세한 내용은 다음 추가 리소스를 참조하세요.
샘플
- GitHub의 ViewPager2 샘플
동영상
- 페이지 넘기기: ViewPager2로 이전(Android Dev Summit '19)