המשתנים verticalScroll ו-horizontalScroll הם הדרך הפשוטה ביותר לאפשר למשתמש לגלול רכיב כשהגבולות של התוכן שלו גדולים יותר מהמגבלות של הגודל המקסימלי. כשמשתמשים במאפייני ה-modifier verticalScroll ו-horizontalScroll, לא צריך להזיז את התוכן או לשנות את המיקום שלו.
@Composable private fun ScrollBoxes() { Column( modifier = Modifier .background(Color.LightGray) .size(100.dp) .verticalScroll(rememberScrollState()) ) { repeat(10) { Text("Item $it", modifier = Modifier.padding(2.dp)) } } }
האובייקט ScrollState מאפשר לשנות את מיקום הגלילה או לקבל את המצב הנוכחי שלה. כדי ליצור אותו עם פרמטרים שמוגדרים כברירת מחדל, משתמשים בפקודה rememberScrollState().
@Composable private fun ScrollBoxesSmooth() { // Smoothly scroll 100px on first composition val state = rememberScrollState() LaunchedEffect(Unit) { state.animateScrollTo(100) } Column( modifier = Modifier .background(Color.LightGray) .size(100.dp) .padding(horizontal = 8.dp) .verticalScroll(state) ) { repeat(10) { Text("Item $it", modifier = Modifier.padding(2.dp)) } } }
שינוי אזור שניתן לגלול
המשתנה scrollableArea הוא אבן בניין בסיסית ליצירת קונטיינרים מותאמים אישית שאפשר לגלול בהם. הוא מספק הפשטה ברמה גבוהה יותר של העיבוד scrollable, ומטפל בדרישות נפוצות כמו פרשנות של דלתא של תנועה, חיתוך תוכן ואפקטים של גלילה מעבר לקצה.
אמנם משתמשים ב-scrollableArea להטמעות בהתאמה אישית, אבל בדרך כלל עדיף להשתמש בפתרונות מוכנים כמו verticalScroll, horizontalScroll או רכיבים קומפוזביליים כמו LazyColumn לרשימות רגילות שניתן לגלול בהן. הרכיבים האלה ברמה גבוהה יותר, פשוטים יותר לתרחישים נפוצים, והם עצמם נבנים באמצעות scrollableArea.
ההבדל בין משתני ה-modifier scrollableArea ו-scrollable
ההבדל העיקרי בין scrollableArea ל-scrollable הוא באופן שבו הם מפרשים את תנועות הגלילה של המשתמש:
scrollable(דלתא גולמית): הדלתא משקפת ישירות את התנועה הפיזית של הקלט של המשתמש (למשל, גרירת הסמן) במסך.-
scrollableArea(דלתא שמתמקדת בתוכן): הערך שלdeltaמתהפך מבחינה סמנטית כדי לייצג את השינוי שנבחר במיקום הגלילה, כך שהתוכן נראה כאילו הוא זז עם התנועה של המשתמש, שבדרך כלל הפוכה לתנועה של הסמן.
אפשר לחשוב על זה ככה: scrollable אומר איך הסמן זז, ו-scrollableArea מתרגם את התנועה של הסמן לאופן שבו התוכן צריך לזוז בתצוגה רגילה שאפשר לגלול בה. ההיפוך הזה הוא הסיבה לכך ש-scrollableArea
מרגיש טבעי יותר כשמטמיעים מאגר סטנדרטי שניתן לגלילה.
הטבלה הבאה מסכמת את סימני הדלתא בתרחישים נפוצים:
תנועה של המשתמש |
ההפרש שדווח ל- |
ההפרש שדווח ל- |
|---|---|---|
הסמן זז למעלה |
שלילי |
חיובי |
הסמן זז למטה |
חיובי |
שלילי |
הסמן זז שמאלה |
שלילי |
חיובי (שלילי ב-RTL) |
הסמן זז ימינה |
חיובי |
שלילי (חיובי ב-RTL) |
(*) הערה לגבי הסימן של הדלתא של scrollableArea: הסימן של הדלתא של scrollableArea הוא לא רק היפוך פשוט. הוא מביא בחשבון בצורה חכמה את:
- כיוון: אנכי או אופקי.
-
LayoutDirection: משמאל לימין או מימין לשמאל (חשוב במיוחד לגלילה אופקית). - הדגל
reverseScrolling: אם כיוון הגלילה הפוך.
בנוסף להיפוך של דלתת הגלילה, scrollableArea גם חותך את התוכן לגבולות הפריסה ומטפל ברינדור של אפקטים של גלילה מעבר לקצה. כברירת מחדל, המערכת משתמשת באפקט שסופק על ידי LocalOverscrollFactory.
כדי להתאים אישית או להשבית את האפשרות הזו, אפשר להשתמש בעומס היתר של scrollableArea שמקבל פרמטר OverscrollEffect.
מתי כדאי להשתמש במגביל scrollableArea
כדאי להשתמש ב-modifier scrollableArea כשצריך ליצור רכיב גלילה בהתאמה אישית, שלא מתאים ל-modifiers horizontalScroll או verticalScroll או לפריסות Lazy. בדרך כלל מדובר במקרים שבהם:
- לוגיקה של פריסה בהתאמה אישית: כשסידור הפריטים משתנה באופן דינמי בהתאם למיקום הגלילה.
- אפקטים חזותיים ייחודיים: שינוי צורה, שינוי קנה מידה או אפקטים אחרים שמופעלים על רכיבי הצאצא בזמן הגלילה.
- שליטה ישירה: אם אתם צריכים שליטה מדויקת במנגנון הגלילה, מעבר למה שמוצג ב-
verticalScrollאו בפריסות Lazy.
יצירת רשימות מותאמות אישית דמויות גלגל באמצעות scrollableArea
בדוגמה הבאה מוצג שימוש ב-scrollableArea כדי ליצור רשימה אנכית בהתאמה אישית, שבה הפריטים מצטמצמים ככל שהם מתרחקים מהמרכז, וכך נוצר אפקט חזותי דמוי גלגל. סוג כזה של שינוי שתלוי בגלילה הוא תרחיש שימוש מושלם ל-scrollableArea.
scrollableArea.
@Composable private fun ScrollableAreaSample() { // ... Layout( modifier = Modifier .size(150.dp) .scrollableArea(scrollState, Orientation.Vertical) .background(Color.LightGray), // ... ) { measurables, constraints -> // ... // Update the maximum scroll value to not scroll beyond limits and stop when scroll // reaches the end. scrollState.maxValue = (totalHeight - viewportHeight).coerceAtLeast(0) // Position the children within the layout. layout(constraints.maxWidth, viewportHeight) { // The current vertical scroll position, in pixels. val scrollY = scrollState.value val viewportCenterY = scrollY + viewportHeight / 2 var placeableLayoutPositionY = 0 placeables.forEach { placeable -> // This sample applies a scaling effect to items based on their distance // from the center, creating a wheel-like effect. // ... // Place the item horizontally centered with a layer transformation for // scaling to achieve wheel-like effect. placeable.placeRelativeWithLayer( x = constraints.maxWidth / 2 - placeable.width / 2, // Offset y by the scroll position to make placeable visible in the viewport. y = placeableLayoutPositionY - scrollY, ) { scaleX = scaleFactor scaleY = scaleFactor } // Move to the next item's vertical position. placeableLayoutPositionY += placeable.height } } } } // ...
שינוי הגלילה
המשתנה scrollable שונה ממשתני הגלילה בכך ש-scrollable מזהה את תנועות הגלילה ומתעד את השינויים, אבל לא מקזז את התוכן באופן אוטומטי. במקום זאת, ההרשאה הזו מועברת למשתמש באמצעות ScrollableState, שנדרש כדי שהמשתנה הזה יפעל כמו שצריך.
כשיוצרים ScrollableState, צריך לספק פונקציית consumeScrollDelta שתופעל בכל שלב של גלילה (באמצעות קלט של תנועה, גלילה חלקה או החלקה מהירה) עם הדלתא בפיקסלים. הפונקציה הזו צריכה להחזיר את מרחק הגלילה שבוצע, כדי לוודא שהאירוע מופץ בצורה תקינה במקרים שבהם יש רכיבים בתצוגת עץ עם המשנה scrollable.
קטע הקוד הבא מזהה את התנועות ומציג ערך מספרי של היסט, אבל לא מזיז אף רכיב:
@Composable private fun ScrollableSample() { // actual composable state var offset by remember { mutableFloatStateOf(0f) } Box( Modifier .size(150.dp) .scrollable( orientation = Orientation.Vertical, // Scrollable state: describes how to consume // scrolling delta and update offset state = rememberScrollableState { delta -> offset += delta delta } ) .background(Color.LightGray), contentAlignment = Alignment.Center ) { Text(offset.toString()) } }
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- הסבר על תנועות
- העברת
CoordinatorLayoutל-Compose - שימוש ב-Views בפיתוח נייטיב