העברת CoordinatorLayout לכתיבה

CoordinatorLayout הוא ViewGroup שמאפשר להשתמש בפריסות מורכבות, חופפות ומטמיעות. הוא משמש כמאגר שמאפשר אינטראקציות ספציפיות של Material Design, כמו הרחבה או כיווץ של סרגי כלים ודפים תחתונים, בתצוגות שמכילות אותו.

ב-Compose, הערך הקרוב ביותר ל-CoordinatorLayout הוא Scaffold. Scaffold מספק משבצות תוכן שאפשר לשלב בהן רכיבי Material למתבנים ולאינטראקציות נפוצים במסכים. בדף הזה נסביר איך להעביר את ההטמעה של CoordinatorLayout כך שתשתמש ב-Scaffold ב-Compose.

שלבי ההעברה

כדי להעביר את CoordinatorLayout אל Scaffold, מבצעים את השלבים הבאים:

  1. בקטע הקוד הבא, ה-CoordinatorLayout מכיל AppBarLayout שמכיל ToolBar,‏ ViewPager ו-FloatingActionButton. מסירים את הרכיב CoordinatorLayout ואת הצאצאים שלו מההיררכיה של ממשק המשתמש ומוסיפים רכיב ComposeView כדי להחליף אותו.

    <!--  <androidx.coordinatorlayout.widget.CoordinatorLayout-->
    <!--      android:id="@+id/coordinator_layout"-->
    <!--      android:layout_width="match_parent"-->
    <!--      android:layout_height="match_parent"-->
    <!--      android:fitsSystemWindows="true">-->
    
    <!--    <androidx.compose.ui.platform.ComposeView-->
    <!--        android:id="@+id/compose_view"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="match_parent"-->
    <!--        app:layout_behavior="@string/appbar_scrolling_view_behavior" />-->
    
    <!--    <com.google.android.material.appbar.AppBarLayout-->
    <!--        android:id="@+id/app_bar_layout"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="wrap_content"-->
    <!--        android:fitsSystemWindows="true"-->
    <!--        android:theme="@style/Theme.Sunflower.AppBarOverlay">-->
    
        <!-- AppBarLayout contents here -->
    
    <!--    </com.google.android.material.appbar.AppBarLayout>-->
    
    <!--  </androidx.coordinatorlayout.widget.CoordinatorLayout>-->
    
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
  2. ב-Fragment או ב-Activity, מוצאים הפניה ל-ComposeView שהוספתם עכשיו וקוראים ל-method setContent עבורו. בגוף ה-method, מגדירים את Scaffold כתוכן שלו:

    composeView.setContent {
        Scaffold(Modifier.fillMaxSize()) { contentPadding ->
            // Scaffold contents
            // ...
        }
    }

  3. בתוכן של Scaffold, מוסיפים את התוכן הראשי של המסך שבו. מכיוון שהתוכן הראשי ב-XML שלמעלה הוא ViewPager2, נשתמש ב-HorizontalPager, שהוא המקבילה שלו ב-Compose. ה-lambda content של Scaffold מקבל גם מופע של PaddingValues שצריך להחיל על הרמה הבסיסית (root) של התוכן. אפשר להשתמש ב-Modifier.padding כדי להחיל את אותו PaddingValues על HorizontalPager.

    composeView.setContent {
        Scaffold(Modifier.fillMaxSize()) { contentPadding ->
            val pagerState = rememberPagerState {
                10
            }
            HorizontalPager(
                state = pagerState,
                modifier = Modifier.padding(contentPadding)
            ) { /* Page contents */ }
        }
    }

  4. משתמשים במיקומי תוכן אחרים ש-Scaffold מספק כדי להוסיף עוד רכיבי מסך ולהעביר את שאר תצוגות הצאצאים. אפשר להשתמש ב-slot‏ topBar כדי להוסיף TopAppBar, וב-slot‏ floatingActionButton כדי לספק FloatingActionButton.

    composeView.setContent {
        Scaffold(
            Modifier.fillMaxSize(),
            topBar = {
                TopAppBar(
                    title = {
                        Text("My App")
                    }
                )
            },
            floatingActionButton = {
                FloatingActionButton(
                    onClick = { /* Handle click */ }
                ) {
                    Icon(
                        Icons.Filled.Add,
                        contentDescription = "Add Button"
                    )
                }
            }
        ) { contentPadding ->
            val pagerState = rememberPagerState {
                10
            }
            HorizontalPager(
                state = pagerState,
                modifier = Modifier.padding(contentPadding)
            ) { /* Page contents */ }
        }
    }

תרחישים נפוצים לדוגמה

כיווץ והרחבה של סרגי כלים

במערכת View, כדי לכווץ ולהרחיב את סרגל הכלים באמצעות CoordinatorLayout, משתמשים ב-AppBarLayout כמאגר בסרגל הכלים. לאחר מכן תוכלו לציין Behavior באמצעות layout_behavior ב-XML בתצוגה הנגללת המשויכת (כמו RecyclerView או NestedScrollView) כדי להצהיר איך סרגל הכלים יתכווץ או יתרחב בזמן הגלילה.

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

  1. כדי ליצור TopAppBarScrollBehavior, צריך להתקשר למספר TopAppBarDefaults.enterAlwaysScrollBehavior().
  2. צריך לספק את TopAppBarScrollBehavior שנוצר ל-TopAppBar.
  3. מחברים את NestedScrollConnection דרך Modifier.nestedScroll ב-Scaffold כדי ש-Scaffold יוכל לקבל אירועי גלילה בתצוגת עץ כשהתוכן שאפשר לגלול בו גוללים למעלה או למטה. כך סרגל האפליקציות המכיל יכול להתכווץ או להתרחב בהתאם בזמן הגלילה בתוכן.

    // 1. Create the TopAppBarScrollBehavior
    val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
    
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("My App")
                },
                // 2. Provide scrollBehavior to TopAppBar
                scrollBehavior = scrollBehavior
            )
        },
        // 3. Connect the scrollBehavior.nestedScrollConnection to the Scaffold
        modifier = Modifier
            .fillMaxSize()
            .nestedScroll(scrollBehavior.nestedScrollConnection)
    ) { contentPadding ->
        /* Contents */
        // ...
    }

התאמה אישית של אפקט הגלילה בזמן הצמצום/ההרחבה

אפשר לספק כמה פרמטרים ל-enterAlwaysScrollBehavior כדי להתאים אישית את אפקט האנימציה של התכווץ/ההרחבה. TopAppBarDefaults גם מספק TopAppBarScrollBehavior אחר כמו exitUntilCollapsedScrollBehavior, שמרחיב את סרגל האפליקציה רק כשגוללים את התוכן עד הסוף.

כדי ליצור אפקט מותאם אישית לחלוטין (למשל, אפקט פרלקס), תוכלו גם ליצור NestedScrollConnection משלכם ולהיסט את סרגל הכלים באופן ידני תוך כדי גלילה בתוכן. דוגמה לקוד זמינה בדוגמה של גלילה בתוך רכיב ב-AOSP.

מגירות

בתצוגות, כדי להטמיע חלונית הזזה לניווט, משתמשים ב-DrawerLayout כתצוגת הרמה הבסיסית (root). בתורו, ה-CoordinatorLayout הוא תצוגת הצאצא של ה-DrawerLayout. השדה DrawerLayout מכיל גם תצוגת צאצא נוספת, כמו NavigationView, שמציגה את אפשרויות הניווט בחלונית ההזזה.

ב-Compose, אפשר להטמיע תפריט ניווט באמצעות הרכיב הניתן לקיבוץ ModalNavigationDrawer. ב-ModalNavigationDrawer יש חריץ drawerContent למגירה וחריץ content לתוכן המסך.

ModalNavigationDrawer(
    drawerContent = {
        ModalDrawerSheet {
            Text("Drawer title", modifier = Modifier.padding(16.dp))
            HorizontalDivider()
            NavigationDrawerItem(
                label = { Text(text = "Drawer Item") },
                selected = false,
                onClick = { /*TODO*/ }
            )
            // ...other drawer items
        }
    }
) {
    Scaffold(Modifier.fillMaxSize()) { contentPadding ->
        // Scaffold content
        // ...
    }
}

מידע נוסף זמין במאמר תיבות.

סרגלים אינטראקטיביים

Scaffold מספק חריץ snackbarHost, שיכול להכיל רכיב SnackbarHost שאפשר ליצור ממנו Snackbar.

val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
    snackbarHost = {
        SnackbarHost(hostState = snackbarHostState)
    },
    floatingActionButton = {
        ExtendedFloatingActionButton(
            text = { Text("Show snackbar") },
            icon = { Icon(Icons.Filled.Image, contentDescription = "") },
            onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar("Snackbar")
                }
            }
        )
    }
) { contentPadding ->
    // Screen content
    // ...
}

מידע נוסף זמין במאמר סרגל צד.

מידע נוסף

למידע נוסף על העברת CoordinatorLayout ל-Compose, תוכלו לעיין במקורות המידע הבאים: