הוספת תמיכה בתנועת החזרה החזויה

איור 1. Mockup של המראה והתחושה של התנועה החזרה החזוי בטלפון

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

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

החל מ-Android 15, האפשרות למפתחים להפעיל אנימציות חיזוי של תנועת החזרה לא זמינה יותר. אנימציות מערכת כמו חזרה למסך הבית, מעבר בין משימות ומעבר בין פעילויות מופיעות עכשיו באפליקציות שהבעלים שלהן הביעו הסכמה לתנועת החזרה החזוי, באופן מלא או ברמת הפעילות.

אתם יכולים לבדוק את האנימציה הזו לחזרה לדף הבית (כפי שמתואר בקטע הבא בדף).

כדי לתמוך בתנועת החזרה החזוי, צריך לעדכן את האפליקציה באמצעות ממשק ה-API OnBackPressedCallback AppCompat 1.6.0-alpha05 (AndroidX) או גרסה מתקדמת יותר, או באמצעות ממשק ה-API החדש של הפלטפורמה OnBackInvokedCallback. רוב האפליקציות משתמשות ב-AndroidX API עם תאימות לאחור.

העדכון הזה מספק נתיב העברה כדי ליירט כראוי את הניווט לאחור. לשם כך, צריך להחליף את ניתוחי הנתונים של KeyEvent.KEYCODE_BACK ואת כל הכיתות עם שיטות onBackPressed, כמו Activity ו-Dialog, בממשקי ה-API החדשים של המערכת לניווט לאחור.

Codelab וסרטון מ-Google I/O

בנוסף לשימוש במסמכי התיעוד שבדף הזה, כדאי לנסות את ה-codelab שלנו. הוא מספק הטמעה של תרחיש לדוגמה של WebView שמטפל בתנועת החזרה החזוי באמצעות ממשקי AndroidX Activity API.

אפשר גם לצפות בסרטון שלנו מ-Google I/O, שבו מפורטות דוגמאות נוספות להטמעת AndroidX וממשקי ה-API של הפלטפורמה.

עדכון אפליקציה שמשתמשת בניווט ברירת המחדל אחורה

התכונה 'חזרה חזוי' מופעלת כברירת מחדל.

אם האפליקציה שלכם משתמשת ב-Fragments או ברכיב הניווט, עליכם לשדרג גם ל-AndroidX Activity 1.6.0-alpha05 ואילך.

עדכון אפליקציה שמשתמשת בניווט חזרה בהתאמה אישית

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

האפליקציה שלכם משתמשת ב-AndroidX איך האפליקציה מטפלת בניווט לאחור נתיב ההעברה המומלץ (קישור בדף הזה)
כן ממשקי API של AndroidX העברת הטמעה קיימת של AndroidX לאחור
ממשקי API של פלטפורמות לא נתמכות העברת אפליקציית AndroidX שמכילה ממשקי API לניווט לאחור שלא נתמכים לממשקי API של AndroidX
לא ממשקי API של פלטפורמות לא נתמכות, אפשר להעביר העברת אפליקציה שמשתמשת בממשקי API לניווט לאחור שלא נתמכים לממשקי API של פלטפורמות
ממשקי API של פלטפורמות לא נתמכות, אבל לא ניתן להעביר אותם כדי לבטל את ההסכמה באופן זמני, מגדירים את המאפיין android:enableOnBackInvokedCallback לערך false בתג <application> או <activity> בקובץ AndroidManifest.xml של האפליקציה.

העברה של הטמעת ניווט אחורה ב-AndroidX

התרחיש לדוגמה הזה הוא הנפוץ ביותר (והמומלץ ביותר). הוא חל על אפליקציות חדשות או קיימות שמטמיעות טיפול בהתאמה אישית של ניווט באמצעות תנועות באמצעות OnBackPressedDispatcher, כפי שמתואר בקטע הוספת ניווט מותאם אישית אחורה.

כדי לוודא שממשקי API שכבר משתמשים ב-OnBackPressedDispatcher (כמו 'קטעי Fragment' ורכיב הניווט) יפעלו בצורה חלקה עם תנועת החזרה החזוי, צריך לשדרג ל-AndroidX Activity 1.6.0-alpha05.

```xml
// In your build.gradle file:
dependencies {

// Add this in addition to your other dependencies
implementation "androidx.activity:activity:1.6.0-alpha05"
```

איך מעבירים אפליקציה של AndroidX שמכילה ממשקי API לניווט לאחור שלא נתמכים לממשקי API של AndroidX

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

כדי להעביר ממשקי API לא נתמכים לממשקי API של AndroidX:

  1. מעבירים את הלוגיקה לטיפול בבקשות Back של המערכת אל OnBackPressedDispatcher של AndroidX באמצעות הטמעה של OnBackPressedCallback. להנחיות מפורטות, ראו הוספת ניווט מותאם אישית אחורה.

  2. משביתים את האפשרות OnBackPressedCallback כשרוצים להפסיק את מניעת התגובה לתנועת החזרה אחורה.

  3. מפסיקים ליירט אירועי חזרה אחורה באמצעות OnBackPressed או KeyEvent.KEYCODE_BACK.

  4. חשוב לשדרג ל-AndroidX Activity 1.6.0-alpha05.

    // In your build.gradle file:
    dependencies {
    
    // Add this in addition to your other dependencies
    implementation "androidx.activity:activity:1.6.0-alpha05"
    

העברת אפליקציה שמשתמשת בממשקי API לא נתמכים של ניווט לאחור לממשקי API של פלטפורמות

אם באפליקציה שלכם אי אפשר להשתמש בספריות AndroidX, ובמקום זאת אתם מטמיעים ניווט מותאם אישית אחורה או מפנים אליו באמצעות ממשקי ה-API שלא נתמכים, תצטרכו לעבור לממשק ה-API של הפלטפורמה OnBackInvokedCallback.

כדי להעביר ממשקי API שלא נתמכים ל-Platform API:

  1. משתמשים ב-API החדש OnBackInvokedCallback במכשירים עם Android מגרסה 13 ואילך, ומשתמשים בממשקי ה-API ללא תמיכה במכשירים עם Android מגרסה 12 ומטה.

  2. רושמים את הלוגיקה המותאמת אישית של החזרה לאחור ב-OnBackInvokedCallback באמצעות onBackInvokedDispatcher. כך הפעילות הנוכחית לא תסתיים, ולפונקציית הקריאה החוזרת תהיה הזדמנות להגיב לפעולה 'הקודם' אחרי שהמשתמש ישלים את הניווט 'הקודם' במערכת.

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

    דוגמה להעברת לוגיקה מ-onBackPressed:

    Kotlin

    @Override
    fun onCreate() {
        if (BuildCompat.isAtLeastT()) {
            onBackInvokedDispatcher.registerOnBackInvokedCallback(
                OnBackInvokedDispatcher.PRIORITY_DEFAULT
            ) {
                /**
                 * onBackPressed logic goes here. For instance:
                 * Prevents closing the app to go home screen when in the
                 * middle of entering data to a form
                 * or from accidentally leaving a fragment with a WebView in it
                 *
                 * Unregistering the callback to stop intercepting the back gesture:
                 * When the user transitions to the topmost screen (activity, fragment)
                 * in the BackStack, unregister the callback by using
                 * OnBackInvokeDispatcher.unregisterOnBackInvokedCallback
                 * (https://developer.android.com/reference/kotlin/android/window/OnBackInvokedDispatcher#unregisteronbackinvokedcallback)
                 */
            }
        }
    }

    Java

    @Override
    void onCreate() {
      if (BuildCompat.isAtLeastT()) {
        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
            OnBackInvokedDispatcher.PRIORITY_DEFAULT,
            () -> {
              /**
               * onBackPressed logic goes here - For instance:
               * Prevents closing the app to go home screen when in the
               * middle of entering data to a form
               * or from accidentally leaving a fragment with a WebView in it
               *
               * Unregistering the callback to stop intercepting the back gesture:
               * When the user transitions to the topmost screen (activity, fragment)
               * in the BackStack, unregister the callback by using
               * OnBackInvokeDispatcher.unregisterOnBackInvokedCallback
               * (https://developer.android.com/reference/kotlin/android/view/OnBackInvokedDispatcher#unregisteronbackinvokedcallback)
               */
            }
        );
      }
    }
  4. הפסקת ניתוב אירועי חזרה באמצעות OnBackPressed או KeyEvent.KEYCODE_BACK במכשירי Android מגרסה 13 ואילך.

אפשר לרשום OnBackInvokedCallback באמצעות PRIORITY_DEFAULT או PRIORITY_OVERLAY, אבל האפשרות הזו לא זמינה ב-OnBackPressedCallback הדומה של AndroidX. במקרים מסוימים, כדאי להירשם לקבלת שיחה חוזרת באמצעות PRIORITY_OVERLAY.

המצב הזה רלוונטי כשעוברים מ-onKeyPreIme() והקריאה החוזרת צריכה לקבל את תנועת החזרה אחורה במקום IME פתוח. כשמפעילים IME, הוא רושם קריאות חזרה באמצעות PRIORITY_DEFAULT. כדי לוודא ש-OnBackInvokedDispatcher יפנה את תנועת החזרה לאחור לשיחה החוזרת במקום ל-IME הפתוח, צריך לרשום את השיחה החוזרת ב-PRIORITY_OVERLAY.

ביטול ההסכמה ל'חזרה חזותית'

כדי לבטל את ההסכמה, מגדירים את הדגל android:enableOnBackInvokedCallback לערך false ב-AndroidManifest.xml, בתג <application>.

<application
    ...
    android:enableOnBackInvokedCallback="false"
    ... >
...
</application>

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

  • השבתת האנימציה של מערכת החיזוי של תנועת החזרה.
  • התעלמות מ-OnBackInvokedCallback, אבל קריאות ל-OnBackPressedCallback ממשיכות לפעול.

איך מסירים את ההסכמה ברמת הפעילות

החל מגרסה 16 של Android, הדגל android:enableOnBackInvokedCallback מאפשר לבטל את ההצגה של אנימציות מערכת חזויות ברמת הפעילות. ההתנהגות הזו מאפשרת להעביר בקלות רבה יותר אפליקציות גדולות עם כמה פעילויות לתנועות חזרה חזויות.

בקוד הבא מוצגת דוגמה ל-enableOnBackInvokedCallback שמוגדר להפעיל את האנימציה של החזרה למסך הבית מה-MainActivity:

<manifest ...>
    <application . . .

        android:enableOnBackInvokedCallback="false">

        <activity
            android:name=".MainActivity"
            android:enableOnBackInvokedCallback="true"
            ...
        </activity>
        <activity
            android:name=".SecondActivity"
            android:enableOnBackInvokedCallback="false"
            ...
        </activity>
    </application>
</manifest>

כשמשתמשים בדגל android:enableOnBackInvokedCallback, חשוב לזכור את הנקודות הבאות:

  • ההגדרה android:enableOnBackInvokedCallback=false משביתה את האנימציות החזויות של החזרה אחורה ברמת הפעילות או ברמת האפליקציה, בהתאם למיקום שבו מגדירים את התג, ומורה למערכת להתעלם מהקריאות ל-API של הפלטפורמה OnBackInvokedCallback. עם זאת, הקריאות ל-OnBackPressedCallback ממשיכות לפעול כי OnBackPressedCallback תואם לאחור וקורא ל-API‏ onBackPressed, שאין תמיכה בו לפני Android 13.
  • הגדרת הדגל enableOnBackInvokedCallback ברמת האפליקציה קובעת את ערך ברירת המחדל לכל הפעילויות באפליקציה. אפשר לשנות את ברירת המחדל לכל פעילות על ידי הגדרת הדגל ברמת הפעילות, כפי שמוצג בדוגמת הקוד הקודמת.

שיטות מומלצות לשיחות חוזרות

ריכזנו כאן כמה שיטות מומלצות לשימוש בקריאות החזרה (callbacks) הנתמכות של המערכת: BackHandler (לכתיבה), OnBackPressedCallback או OnBackInvokedCallback.

איך קובעים את מצב ממשק המשתמש שמפעיל או משבית כל קריאה חוזרת (callback)

מצב ממשק המשתמש הוא מאפיין שמתאר את ממשק המשתמש. מומלץ לפעול לפי השלבים הבאים ברמה גבוהה.

  1. לקבוע את מצב ממשק המשתמש שמפעיל או משבית כל קריאה חוזרת.

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

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

שימוש בהחזרות קריאה (callbacks) למערכת לאחור ללוגיקה של ממשק המשתמש

הלוגיקת ממשק המשתמש קובעת איך להציג את ממשק המשתמש. שימוש בקריאות חזרה (callbacks) למערכת כדי להריץ לוגיקה של ממשק משתמש, כמו הצגת חלון קופץ או הפעלת אנימציה.

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

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

  • במקרים של פעילות לפעילות או מקרים של קטע לפעילות, מתעדים אם isFinishing בתוך onDestroy הוא true במחזור החיים של הפעילות.
  • במקרים של אירועים מ-fragment ל-fragment, מתעדים אם הערך של isRemoving ב-onDestroy הוא true במהלך מחזור החיים של תצוגת ה-Fragment. לחלופין, אפשר להשתמש בשיטות onBackStackChangeStarted או onBackStackChangeCommitted בתוך FragmentManager.OnBackStackChangedListener.

בבקשת התמיכה של Compose, מתעדים בקריאה החוזרת (callback) של ViewModel המשויך ליעד של Compose.onCleared() זהו האות הטוב ביותר לדעת מתי יעד של יצירת תוכן מוסר מהמקבץ האחורי ונמחק.

יצירת קריאות חזרה עם אחריות יחידה

אפשר להוסיף מספר קריאות חוזרות למפנה. פונקציות ה-call back מתווספות ל-stack, שבו פונקציית ה-call back הפעילה האחרונה שהוספתם מטפלת בתנועת החזרה הבאה, עם קריאה חוזרת אחת לכל תנועת חזרה.

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

סדר הקריאות החוזרות (callbacks) בסטאק.
איור 2. תרשים של סטאק קריאה חוזרת.

באיור 2 מוצג איך אפשר להשתמש בכמה פונקציות קריאה חוזרת ב-stack, כל אחת אחראית לדבר אחד. פונקציית קריאה חוזרת (callback) פועלת רק אם הפונקציות החוזרות שמעליה בסטאק מושבתות. בדוגמה הזו, הקריאה החוזרת (callback) 'האם ברצונך להמשיך?' מופעלת כשהמשתמש מזין נתונים בטופס, ומושבתת במקרים אחרים. כשהמשתמש מחליק לאחור כדי לצאת מהטופס, תיפתח תיבת דו-שיח לאישור.

פונקציית ה-callback השנייה יכולה לכלול רכיב Material שתומך בחזרה חזותית חזוי, מעבר ב-AndroidX באמצעות ממשקי ה-API של Progress או פונקציית callback מותאמת אישית אחרת.

פונקציית ה-callback של childFragmentManager פועלת אם פונקציות ה-callback שלמעלה מושבתות וסטאק החזרה לאחור של FragmentManager הזה לא ריק, כאשר childFragmentManager מצורף ל-Fragment. בדוגמה הזו, הקריאה החוזרת הפנימית הזו מושבתת.

באופן דומה, פונקציית ה-callback הפנימית של supportFragmentManager פועלת אם פונקציות ה-callback שלמעלה מושבתות והמצב של ה-stack שלה לא ריק. ההתנהגות הזו עקבית גם אם משתמשים ב-FragmentManager וגם אם משתמשים ב-NavigationComponent לניווט, כי NavigationComponent מסתמך על FragmentManager. בדוגמה הזו, הקריאה החוזרת הזו מופעלת אם המשתמש לא מזין טקסט בטופס, וכתוצאה מכך הקריאה החוזרת 'האם ברצונך להמשיך?' מושבתת.

לסיום, super.onBackPressed() היא קריאה חוזרת ברמת המערכת, שפועלת שוב אם הקריאות החוזרות שלמעלה מושבתות. כדי להפעיל אנימציות מערכת כמו חזרה למסך הבית, מעבר בין פעילויות ומעבר בין משימות, סטאק החזרה של supportFragmentManager צריך להיות ריק כדי שהקריאה החוזרת הפנימית שלו תושבת.

בדיקת אנימציית התנועה לחזרה אחורה

אם אתם עדיין משתמשים ב-Android 13 או ב-Android 14, תוכלו לבדוק את האנימציה של החזרה לדף הבית שמוצגת באיור 1.

כדי לבדוק את האנימציה הזו, מבצעים את השלבים הבאים:

  1. במכשיר, עוברים אל הגדרות > מערכת > אפשרויות למפתחים.

  2. בוחרים באפשרות חיזוי אנימציה של תנועת החזרה.

  3. פותחים את האפליקציה המעודכנת ומשתמשים בתנועת החזרה לאחור כדי לראות איך היא פועלת.