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

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

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

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

NavigableSupportingPaneScaffold הוא רכיב קומפוזבילי שמפשט את ההטמעה של פריסת חלונית תומכת ב-Jetpack פיתוח נייטיב. היא עוטפת את 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 כדי לעבור בין החלוניות. יוצרים מופע של ה-Navigator באמצעות rememberSupportingPaneScaffoldNavigator.

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

מעבירים את ה-navigator ל-scaffold

ה-scaffold דורש ThreePaneScaffoldNavigator, שהוא ממשק שמייצג את המצב של ה-scaffold, את ThreePaneScaffoldValue ואת PaneScaffoldDirective.

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

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

הנה הטמעה מלאה של ה-scaffold:

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 שוב ולבדוק אותן, כדאי לחלץ אותן לקומפוזיציות משלהן. משתמשים בפקודה 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 (אפשר להשוות את הקוד הבא להטמעה המלאה של ה-scaffold בקטע הקודם):

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() },
)

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

הוספה של ThreePaneScaffoldPredictiveBackHandler

מצרפים את ה-handler של חיזוי תנועת החזרה שמקבל מופע של scaffold navigator ומציינים את backBehavior. ההגדרה הזו קובעת איך יעדים מוסרים מה-backstack במהלך ניווט אחורה. לאחר מכן מעבירים את 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() },
)