קרוסלה עם פריסה בתנועה

כדאי לנסות את הדרך של כתיבת אימייל
‫Jetpack Compose היא ערכת הכלים המומלצת לבניית ממשק משתמש ל-Android. כאן מוסבר איך מוסיפים קרוסלה בכתיבת אימייל.

Carousel הוא אובייקט עזר לתנועה שמאפשר ליצור תצוגות קרוסלה בהתאמה אישית שמציגות רשימה של רכיבים שהמשתמש יכול לעיין בהם. בהשוואה לדרכים אחרות להטמעת תצוגות כאלה, כלי העזר הזה מאפשר ליצור במהירות שינויים מורכבים בתנועה ובממד של Carousel באמצעות MotionLayout.

ווידג'ט Carousel תומך ברשימות עם התחלה וסיום, וגם ברשימות מעגליות.

איך קרוסלה עם MotionLayout עובדת

נניח שאתם רוצים ליצור תצוגה אופקית Carousel עם פריט מרכזי מוגדל:

הפריסה הבסיסית הזו כוללת כמה תצוגות שמייצגות את הפריטים Carousel:

יוצרים MotionLayout עם שלושת המצבים הבאים ומקצים להם מזהים:

  • הקודם
  • התחלה
  • הבא

אם מצב ההתחלה תואם לפריסת הבסיס, במצב הקודם ובמצב הבא הפריטים Carousel מוזזים באחד שמאלה וימינה בהתאמה.

לדוגמה, נניח שיש חמש תצוגות כמו באיור 3, ובמצב התחלה, התצוגות B, ‏ C ו-D גלויות, והתצוגות A ו-E לא גלויות. מגדירים את המצב הקודם כך שהמיקומים של A,‏ B,‏ C ו-D יהיו המיקומים של B,‏ C,‏ D ו-E, והתצוגות ינועו משמאל לימין. במצב הבא, צריך לקרות ההפך: B,‏ C,‏ D ו-E עוברים למקום שבו היו A,‏ B,‏ C ו-D, והתצוגות עוברות מימין לשמאל. האיור הזה מוצג באיור 4:

חשוב מאוד שהתצוגות יסתיימו בדיוק במקום שבו התצוגות המקוריות מתחילות. ‫Carousel יוצרת אשליה של אוסף אינסופי של רכיבים על ידי הזזת התצוגות האמיתיות בחזרה למקום שבו הן היו, אבל מאתחלת אותן מחדש עם התוכן החדש התואם. בתרשים הבא מוצג המנגנון הזה. שימו לב לערכים של 'מספר הפריט'):

מעברים.

אחרי שמגדירים את שלושת מערכי האילוצים האלה בקובץ סצנת התנועה, יוצרים שני מעברים – קדימה ואחורה – בין המצבים start ו-next, ובין המצבים start ו-previous. מוסיפים handler של OnSwipe כדי להפעיל את המעברים בתגובה לתנועת אצבע, כמו בדוגמה הבאה:

    <Transition
        motion:constraintSetStart="@id/start"
        motion:constraintSetEnd="@+id/next"
        motion:duration="1000"
        android:id="@+id/forward">
        <OnSwipe
            motion:dragDirection="dragLeft"
            motion:touchAnchorSide="left" />
    </Transition>

    <Transition
        motion:constraintSetStart="@+id/start"
        motion:constraintSetEnd="@+id/previous"
        android:id="@+id/backward">
        <OnSwipe
            motion:dragDirection="dragRight"
            motion:touchAnchorSide="right" />
    </Transition>

אחרי שיוצרים את סצנת התנועה הבסיסית הזו, מוסיפים Carousel helper לפריסה ומפנים את התצוגות באותו סדר שבו מטמיעים את האנימציה הקודמת והאנימציה הבאה.

מגדירים את המאפיינים הבאים עבור תג העזר Carousel:

  • app:carousel_firstView: התצוגה שמייצגת את הרכיב הראשון של Carousel – בדוגמה הזו, C.
  • app:carousel_previousState: מזהה ConstraintSet של המצב הקודם.
  • app:carousel_nextState: מזהה ConstraintSet של המצב הבא.
  • app:carousel_backwardTransition: מזהה Transition שמוחל בין המצבים start ו-previous.
  • app:carousel_forwardTransition: מזהה Transition שמוחל בין המצבים start ו-next.

לדוגמה, יש לכם משהו כזה בקובץ הפריסה של ה-XML:

    <androidx.constraintlayout.motion.widget.MotionLayout ... >

        <ImageView  android:id="@+id/imageView0" .. />
        <ImageView  android:id="@+id/imageView1" .. />
        <ImageView  android:id="@+id/imageView2" .. />
        <ImageView  android:id="@+id/imageView3" .. />
        <ImageView  android:id="@+id/imageView4" .. />

        <androidx.constraintlayout.helper.widget.Carousel
            android:id="@+id/carousel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:carousel_forwardTransition="@+id/forward"
            app:carousel_backwardTransition="@+id/backward"
            app:carousel_previousState="@+id/previous"
            app:carousel_nextState="@+id/next"
            app:carousel_infinite="true"
            app:carousel_firstView="@+id/imageView2"
            app:constraint_referenced_ids="imageView0,imageView1,imageView2,imageView3,imageView4" />

    </androidx.constraintlayout.motion.widget.MotionLayout>

הגדרת מתאם Carousel בקוד:

Kotlin

carousel.setAdapter(object : Carousel.Adapter {
            override fun count(): Int {
              // Return the number of items in the Carousel.
            }

            override fun populate(view: View, index: Int) {
                // Implement this to populate the view at the given index.
            }

            override fun onNewItem(index: Int) {
                // Called when an item is set.
            }
        })

Java

carousel.setAdapter(new Carousel.Adapter() {
            @Override
            public int count() {
                // Return the number of items in the Carousel.
            }

            @Override
            public void populate(View view, int index) {
                // Populate the view at the given index.
            }

            @Override
            public void onNewItem(int index) {
                 // Called when an item is set.
            }
        });

הערות נוספות

בהתאם לפריט הנוכחי ש'נבחר' ב-Carousel, יכול להיות שיהיה צורך להסתיר את התצוגות שמייצגות את הפריטים שלפני או אחרי כדי להסביר בצורה נכונה את ההתחלה והסוף של Carousel. העוזר Carousel מטפל בזה באופן אוטומטי. כברירת מחדל, במקרים כאלה הצפיות מסומנות בסימן View.INVISIBLE, כך שהפריסה הכוללת לא משתנה.

יש מצב חלופי שבו העוזר Carousel מסמן את התצוגות האלה כView.GONE. אפשר להגדיר את המצב הזה באמצעות המאפיין הבא:

app:carousel_emptyViewsBehavior="gone"

דוגמאות

דוגמאות נוספות לשימוש בכלי העזר Carousel זמינות בפרויקטים לדוגמה ב-GitHub.