הוספת תמיכה באנימציות של חיזוי החזרה בתצוגות

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

הוספת מעברים בהתאמה אישית באמצעות Progress API

ב-AndroidX Activity מגרסה 1.8.0-alpha01 ואילך, אתם יכולים להשתמש בממשקי Predictive Back Progress API כדי לפתח אנימציות בהתאמה אישית לתנועת החזרה החזויה באפליקציה. ממשקי Progress API שימושיים להנפשת תצוגות, אבל יש להם מגבלות כשמנפישים מעברים בין מקטעים. ב-OnBackPressedCallback הוספנו את השיטות handleOnBackProgressed, ‏ handleOnBackCancelled ו-handleOnBackStarted כדי להוסיף אנימציה לאובייקטים כשהמשתמש מחליק אחורה. השיטות האלה מתאימות אם אתם צריכים להתאים אישית יותר מהאנימציות שמוגדרות כברירת מחדל במערכת, או מהאנימציות של Material Component.

אנחנו מצפים שרוב האפליקציות ישתמשו בממשקי ה-API של AndroidX שתואמים לדורות קודמים, אבל יש גם ממשקי API דומים של פלטפורמות בממשק OnBackAnimationCallback שזמינים לבדיקה ב-Android מגרסה 14 ואילך.

שימוש בממשקי Progress API עם AndroidX Transitions

אפשר להשתמש בממשקי ה-API של ההתקדמות עם AndroidX Transitions מגרסה 1.5.0-alpha01 ואילך ב-Android מגרסה 14 ואילך כדי ליצור מעברים של חיזוי החזרה.

  1. כדי להפעיל מעברים כשהמשתמש מחליק אחורה, צריך להשתמש ב-TransitionManager#controlDelayedTransition במקום ב-beginDelayedTransition.
  2. יוצרים את המעבר ב-handleOnBackStarted.
  3. מפעילים את המעבר עם אירוע החזרה בתוך handleOnBackProgressed על ידי קישור של currentFraction אל BackEvent.progress, שחושף את המרחק שהמשתמש החליק אחורה.
  4. מסיימים את המעבר אחרי שהמשתמש ביצע את תנועת החזרה ב-handleOnBackPressed.
  5. לבסוף, מאפסים את מצב המעבר בתוך handleOnBackCancelled.

בסרטון, בקוד Kotlin וב-XML הבאים מוצג מעבר בהתאמה אישית בין שתי תיבות שמוטמעות באמצעות OnBackPressedCallback:

    class MyFragment : Fragment() {

    val transitionSet = TransitionSet().apply {
        addTransition(Fade(Fade.MODE_OUT))
        addTransition(ChangeBounds())
        addTransition(Fade(Fade.MODE_IN))
    }
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val callback = object : OnBackPressedCallback(enabled = false) {

            var controller: TransitionSeekController? = null

            @RequiresApi(34)
            override fun handleOnBackStarted(backEvent: BackEvent) {
                // Create the transition
                controller = TransitionManager.controlDelayedTransition(
                    binding.card,
                    transitionSet
                )
                changeTextVisibility(ShowText.SHORT)
            }

            @RequiresApi(34)
            override fun handleOnBackProgressed(backEvent: BackEvent) {
                // Play the transition as the user swipes back
                if (controller?.isReady == true) {
                    controller?.currentFraction = backEvent.progress
                }
            }

            override fun handleOnBackPressed() {
                // Finish playing the transition when the user commits back
                controller?.animateToEnd()
                this.isEnabled = false
            }

            @RequiresApi(34)
            override fun handleOnBackCancelled() {
                // If the user cancels the back gesture, reset the state
                transition(ShowText.LONG)
            }
        }

        binding.shortText.setOnClickListener {
            transition(ShowText.LONG)
            callback.isEnabled = true
        }

        this.requireActivity().onBackPressedDispatcher.addCallback(callback)
    }

    private fun transition(showText: ShowText) {
        TransitionManager.beginDelayedTransition(
            binding.card,
            transitionSet
        )
        changeTextVisibility(showText)
    }

    enum class ShowText { SHORT, LONG }
    private fun changeTextVisibility(showText: ShowText) {
        when (showText) {
            ShowText.SHORT -> {
                binding.shortText.isVisible = true
                binding.longText.isVisible = false
            }
            ShowText.LONG -> {
                binding.shortText.isVisible = false
                binding.longText.isVisible = true
            }
        }
    }
}
  
<?xml version="1.0" encoding="utf-8"?>
...
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ...>

        <TextView
            android:id="@+id/short_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            ... />

        <TextView
            android:id="@+id/long_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            .../>

    </androidx.constraintlayout.widget.ConstraintLayout>

כשעובדים עם מעברים של חיזוי החזרה, חשוב לזכור את הדברים הבאים:

  • משתמשים ב-isSeekingSupported כדי לבדוק אם המעבר תומך בחיזוי החזרה.
  • צריך לשנות את isSeekingSupported כדי להחזיר את הערך true למעברים בהתאמה אישית.
  • יוצרים בקר אחד לכל אנימציה.
  • יש תמיכה במעברים של חיזוי החזרה עם מעברים של AndroidX, אבל לא עם מעברים של Framework. צריך להפסיק להשתמש במעברים של Framework ולהשתמש במקום זאת במעברים של Animator ו-AndroidX.
  • התכונה "חיזוי החזרה" נתמכת במכשירים עם Android מגרסה 14 ואילך, והיא לא תואמת לדורות קודמים.
  • יש גם תמיכה במעברים שנוצרו באמצעות סצנות XML. ב-handleOnBackStarted, מגדירים את TransitionSeekController לתוצאה של TransitionManager.createSeekController במקום לתוצאה של controlDelayedTransition.

מקורות מידע נוספים