פיתוח פריסת חלונית תומכת

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

פרטים נוספים זמינים בהנחיות לשימוש בחלונית תמיכה של Material 3.

הטמעת חלונית תומכת באמצעות NavigableSupportingPaneScaffold

NavigableSupportingPaneScaffold הוא רכיב שקל להטמיע באמצעותו פריסה של חלונית תמיכה ב-Jetpack Compose. הוא עוטף את SupportingPaneScaffold ומוסיף ניווט מובנה וטיפול חזוי בלחצן 'הקודם'.

תומך של חלונית תומכת יכול לתמוך בעד שלוש חלוניות:

  • החלונית הראשית: כאן מוצג התוכן הראשי.
  • חלונית תמיכה: מספקת הקשר נוסף או כלים נוספים שקשורים לחלונית הראשית.
  • חלונית נוספת (אופציונלי): משמשת לתוכן משלים במקרה הצורך.

התבנית מתאימה את עצמה בהתאם לגודל החלון:

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

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

הוספת יחסי תלות

NavigableSupportingPaneScaffold הוא חלק מספריית הפריסות המותאמות של Material 3.

מוסיפים את שלושת יחסי התלות הבאים לקובץ build.gradle של האפליקציה או המודול:

Kotlin

implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")

Groovy

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • אדפטיביות: אבני בניין ברמה נמוכה, כמו HingeInfo ו-Posture

  • adaptive-layout: פריסות מותאמות כמו ListDetailPaneScaffold ו-SupportingPaneScaffold

  • adaptive-navigation: רכיבים מורכבים לניווט בתוך חלונות ובין חלונות, וכן פריסות מותאמות שמאפשרות ניווט כברירת מחדל, כמו NavigableListDetailPaneScaffold ו-NavigableSupportingPaneScaffold

מוודאים שהפרויקט כולל את compose-material3-adaptive בגרסה 1.1.0-beta1 ואילך.

איך מביעים הסכמה לשימוש בתנועת החזרה החזוי

כדי להפעיל אנימציות של תנועת החזרה חזוי ב-Android מגרסה 15 ומטה, צריך להביע הסכמה לתמיכה בתנועת החזרה חזוי. כדי להביע הסכמה, מוסיפים את הערך android:enableOnBackInvokedCallback="true" לתג <application> או לתגים נפרדים של <activity> בקובץ AndroidManifest.xml.

אחרי שהאפליקציה שלכם מטרגטת ל-Android 16 (רמת API‏ 36) ואילך, האפשרות 'חזרה חזותית חזרה' מופעלת כברירת מחדל.

יצירת ניווט

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

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

העברת הניווט למבנה העזר

למבנה העזר נדרש ThreePaneScaffoldNavigator, שהוא ממשק שמייצג את המצב של המבנה, את ThreePaneScaffoldValue ואת PaneScaffoldDirective.

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = { /*...*/ },
    supportingPane = { /*...*/ },
)

החלונית הראשית והחלונית התומכת הן רכיבים שאפשר לשלב ביניהם, שמכילים את התוכן שלכם. כדי להחיל את אנימציות ברירת המחדל של החלוניות במהלך הניווט, משתמשים ב-AnimatedPane. משתמשים בערך של התשתית כדי לבדוק אם חלונית התמיכה מוסתרת. אם כן, מציגים לחצן שמפעיל את navigateTo(SupportingPaneScaffoldRole.Supporting) כדי להציג את חלונית התמיכה.

זוהי הטמעה מלאה של התבנית:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        AnimatedPane(
            modifier = Modifier
                .safeContentPadding()
                .background(Color.Red)
        ) {
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
                Button(
                    modifier = Modifier
                        .wrapContentSize(),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
                        }
                    }
                ) {
                    Text("Show supporting pane")
                }
            } else {
                Text("Supporting pane is shown")
            }
        }
    },
    supportingPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            Text("Supporting pane")
        }
    }
)

חילוץ רכיבים שניתנים לקישור בחלונית

כדאי לחלץ את החלונות הנפרדים של SupportingPaneScaffold לרכיבי composable משלהם כדי שאפשר יהיה להשתמש בהם שוב ולבדוק אותם. משתמשים ב-ThreePaneScaffoldScope כדי לגשת ל-AnimatedPane אם רוצים להשתמש באנימציות ברירת המחדל:

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.MainPane(
    shouldShowSupportingPaneButton: Boolean,
    onNavigateToSupportingPane: () -> Unit,
    modifier: Modifier = Modifier,
) {
    AnimatedPane(
        modifier = modifier.safeContentPadding()
    ) {
        // Main pane content
        if (shouldShowSupportingPaneButton) {
            Button(onClick = onNavigateToSupportingPane) {
                Text("Show supporting pane")
            }
        } else {
            Text("Supporting pane is shown")
        }
    }
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.SupportingPane(
    modifier: Modifier = Modifier,
) {
    AnimatedPane(modifier = modifier.safeContentPadding()) {
        // Supporting pane content
        Text("This is the supporting pane")
    }
}

חילוץ החלונות לרכיבים מורכבים מפשט את השימוש ב-SupportingPaneScaffold (אפשר להשוות את הקוד הבא להטמעה המלאה של התבנית בקטע הקודם):

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)

אם אתם רוצים יותר שליטה על היבטים ספציפיים של התבנית, כדאי להשתמש ב-SupportingPaneScaffold במקום ב-NavigableSupportingPaneScaffold. אפשר להזין את הערכים PaneScaffoldDirective ו-ThreePaneScaffoldValue או את הערכים ThreePaneScaffoldState בנפרד. הגמישות הזו מאפשרת לכם להטמיע לוגיקה מותאמת אישית למרווח בין החלונות ולקבוע כמה חלונות יוצגו בו-זמנית. אפשר גם להוסיף את הערך ThreePaneScaffoldPredictiveBackHandler כדי להפעיל תמיכה בחיזוי חזרה.

הוספה של ThreePaneScaffoldPredictiveBackHandler

מחברים את ה-handler לחיזוי תנועת החזרה, שמקבל מכונה של ניווט בסכימה ומציינים את backBehavior. ההגדרה הזו קובעת איך יעברו יעדים מהמצב 'מוּצג' למצב 'מוּסתר' בזמן ניווט לאחור. לאחר מכן מעבירים את scaffoldDirective ו-scaffoldState אל SupportingPaneScaffold. משתמשים בעלות יתר שמקבלת ThreePaneScaffoldState ומעבירים את הערך scaffoldNavigator.scaffoldState.

מגדירים את החלוניות הראשית והתומכת ב-SupportingPaneScaffold. משתמשים ב-AnimatedPane לאנימציות ברירת המחדל של חלוניות.

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

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

ThreePaneScaffoldPredictiveBackHandler(
    navigator = scaffoldNavigator,
    backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
)

SupportingPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    scaffoldState = scaffoldNavigator.scaffoldState,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane() },
)