لغزاندن بین بخش‌ها بااستفاده از ViewPager2

امتحان کردن روش Compose
Jetpack Compose جعبه‌ابزار واسط کاربر توصیه‌شده برای Android است. با نحوه استفاده از «ورق‌زن» در Compose آشنا شوید.

اسلایدهای صفحه انتقال از یک صفحه کامل به صفحه دیگر هستند و در میاناهای کاربر مانند دستیارهای راه‌اندازی و نمایش اسلاید رایج هستند. این موضوع به شما نشان می‌دهد که چگونه با شیء 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>

ایجاد بخش

کلاس Fragment ایجاد کنید که چیدمانی را که در روش onCreateView() ایجاد کرده‌اید برگرداند. سپس می‌توانید هر زمان که به صفحه جدیدی برای نمایش به کاربر نیاز داشتید، نمونه‌هایی از این قطعه را در فعالیت والد ایجاد کنید:

کاتلین

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 به‌عنوان صفحه‌های جدید پیاده‌سازی می‌کند. باید روش getItemCount() را برای آداپتور ورق‌زن پیاده‌سازی کنید، که تعداد صفحاتی را که آداپتور ایجاد می‌کند برمی‌گرداند. در این مثال، پنج مورد وجود دارد.
  • FragmentStateAdapter را به اشیای ViewPager2 متصل می‌کند.

کاتلین

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(). در هر نقطه از انتقال صفحه‌نمایش، این روش یک‌بار برای هر صفحه قابل‌مشاهده فراخوانی می‌شود—معمولاً فقط یک صفحه قابل‌مشاهده وجود دارد—و برای صفحه‌های مجاور خارج از صفحه‌نمایش. برای مثال، اگر صفحه سه نمایان باشد و کاربر به‌سمت صفحه چهار بکشد، transformPage() در هر مرحله از اشاره برای صفحه‌های دو، سه، و چهار فراخوانی می‌شود.

سپس در پیاده‌سازی transformPage()، می‌توانید با تعیین اینکه کدام صفحه‌ها باید براساس موقعیت صفحه در صفحه‌نمایش تبدیل شوند، پویانمایی‌های سفارشی برای اسلاید ایجاد کنید. موقعیت صفحه را از پارامتر position روش transformPage() دریافت کنید.

پارامتر position نشان می‌دهد که صفحه موردنظر نسبت به مرکز صفحه‌نمایش در کجا قرار دارد. این پارامتر یک ویژگی پویا است که با پیمایش کاربر در مجموعه‌ای از صفحه‌ها تغییر می‌کند. وقتی صفحه‌ای کل صفحه‌نمایش را پر می‌کند، مقدار موقعیت آن 0 است. وقتی صفحه‌ای از سمت راست صفحه خارج می‌شود، مقدار موقعیت آن 1 است. اگر کاربر تا نیمه بین صفحه‌های یک و دو پیمایش کند، صفحه یک موقعیت -0.5 و صفحه دو موقعیت 0.5 را دارد. براساس موقعیت صفحه‌ها در صفحه، می‌توانید با تنظیم ویژگی‌های صفحه با روش‌هایی مثل setAlpha()، setTranslationX()، یا setScaleY() پویانمایی‌های اسلاید سفارشی ایجاد کنید.

وقتی پیاده‌سازی PageTransformer دارید، setPageTransformer() را با پیاده‌سازی‌تان فراخوانی کنید تا پویانمایی‌های سفارشی‌تان اعمال شود. برای مثال، اگر PageTransformer با نام ZoomOutPageTransformer دارید، می‌توانید پویانمایی‌های سفارشی‌تان را به این صورت تنظیم کنید:

کاتلین

val viewPager: ViewPager2 = findViewById(R.id.pager)
...
viewPager.setPageTransformer(ZoomOutPageTransformer())

جاوا

ViewPager2 viewPager = findViewById(R.id.pager);
...
viewPager.setPageTransformer(new ZoomOutPageTransformer());

برای نمونه‌هایی از PageTransformer، بخش‌های مبدل صفحه زوم‌پس و مبدل صفحه عمق را ببینید.

تبدیل‌کننده صفحه زوم‌پَس

این تبدیل‌کننده صفحه، صفحه‌ها را هنگام پیمایش بین صفحه‌های مجاور کوچک و محو می‌کند. وقتی صفحه‌ای به مرکز نزدیک‌تر می‌شود، به اندازه عادی خود برمی‌گردد و ظاهر می‌شود.

شکل ۲. ‫ZoomOutPageTransformer مثال.
 

کاتلین

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 منفی، لغزاندن صفحه را خنثی کنید. برای مثال:

کاتلین

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

جاوا

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

مثال زیر نشان می‌دهد که چگونه می‌توان پویانمایی پیش‌فرض لغزش صفحه را در یک تبدیل‌کننده صفحه فعال خنثی کرد:

کاتلین

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، منابع اضافی زیر را ببینید.

نمونه‌ها

ویدیوها