כדי לדפדף בתוכן מימין לשמאל או מלמעלה למטה, אפשר להשתמש ברכיבי ה-Composable HorizontalPager
ו-VerticalPager
, בהתאמה. לרכיבים האלה יש פונקציות דומות ל-ViewPager
במערכת התצוגה. כברירת מחדל, הרכיב HorizontalPager
תופס את כל רוחב המסך, הרכיב VerticalPager
תופס את כל הגובה, והרכיבים של החלפת הדפים מעבירים רק דף אחד בכל פעם. אפשר להגדיר את כל ברירות המחדל האלה.
HorizontalPager
כדי ליצור רכיב החלקה שמאפשר גלילה אופקית ימינה ושמאלה, משתמשים ב-HorizontalPager
:
HorizontalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
VerticalPager
כדי ליצור רכיב החלפה שניתן לגלול בו למעלה ולמטה, משתמשים ב-VerticalPager
:
VerticalPager
// Display 10 items val pagerState = rememberPagerState(pageCount = { 10 }) VerticalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier.fillMaxWidth() ) }
יצירה עצלה
הפריסה של הדפים גם ב-HorizontalPager
וגם ב-VerticalPager
מורכבת באופן עצלני לפי הצורך. כשהמשתמש גולל בדפים, רכיב ה-Composable מסיר את כל הדפים שלא נדרשים יותר.
טעינה של דפים נוספים מחוץ למסך
כברירת מחדל, רכיב הניווט בין דפים טוען רק את הדפים שמוצגים במסך. כדי לטעון עוד דפים מחוץ למסך, מגדירים את beyondBoundsPageCount
לערך גבוה מאפס.
גלילה לפריט במנגנון ההחלפה
כדי לגלול לדף מסוים ברכיב הניווט בין דפים, יוצרים אובייקט PagerState
באמצעות rememberPagerState()
ומעבירים אותו כפרמטר state
לרכיב הניווט בין דפים. אפשר להתקשר אל
PagerState#scrollToPage()
במדינה הזו, בתוך CoroutineScope
:
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.scrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
אם רוצים להוסיף אנימציה לדף, משתמשים בפונקציה
PagerState#animateScrollToPage()
:
val pagerState = rememberPagerState(pageCount = { 10 }) HorizontalPager(state = pagerState) { page -> // Our page content Text( text = "Page: $page", modifier = Modifier .fillMaxWidth() .height(100.dp) ) } // scroll to page val coroutineScope = rememberCoroutineScope() Button(onClick = { coroutineScope.launch { // Call scroll to on pagerState pagerState.animateScrollToPage(5) } }, modifier = Modifier.align(Alignment.BottomCenter)) { Text("Jump to Page 5") }
קבלת התראות על שינויים במצב הדף
ל-PagerState
יש שלושה מאפיינים עם מידע על דפים:
currentPage
,
settledPage
,
ו-
targetPage
.
-
currentPage
: הדף הכי קרוב למיקום ההצמדה. כברירת מחדל, מיקום ההצמדה הוא בתחילת הפריסה. -
settledPage
: מספר הדף כשלא מופעלת אנימציה או גלילה. המאפיין הזה שונה מהמאפייןcurrentPage
בכך שהמאפייןcurrentPage
מתעדכן מיד אם הדף קרוב מספיק למיקום ההצמדה, אבל המאפייןsettledPage
נשאר ללא שינוי עד שכל האנימציות מסיימות לפעול. -
targetPage
: המיקום המוצע לעצירה של תנועת גלילה.
אפשר להשתמש בפונקציה snapshotFlow
כדי לעקוב אחרי השינויים במשתנים האלה ולהגיב להם. לדוגמה, כדי לשלוח אירוע Analytics בכל שינוי בדף, אפשר לעשות את הפעולות הבאות:
val pagerState = rememberPagerState(pageCount = { 10 }) LaunchedEffect(pagerState) { // Collect from the a snapshotFlow reading the currentPage snapshotFlow { pagerState.currentPage }.collect { page -> // Do something with each page change, for example: // viewModel.sendPageSelectedEvent(page) Log.d("Page change", "Page changed to $page") } } VerticalPager( state = pagerState, ) { page -> Text(text = "Page: $page") }
הוספת אינדיקטור דף
כדי להוסיף אינדיקטור לדף, משתמשים באובייקט PagerState
כדי לקבל מידע על הדף שנבחר מתוך מספר הדפים, ומציירים את האינדיקטור המותאם אישית.
לדוגמה, אם רוצים ליצור עיגול פשוט כמחוון, אפשר לחזור על מספר העיגולים ולשנות את צבע העיגול בהתאם לבחירת הדף, באמצעות pagerState.currentPage
:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, modifier = Modifier.fillMaxSize() ) { page -> // Our page content Text( text = "Page: $page", ) } Row( Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pagerState.pageCount) { iteration -> val color = if (pagerState.currentPage == iteration) Color.DarkGray else Color.LightGray Box( modifier = Modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } }

החלת אפקטים של גלילה בפריט על התוכן
תרחיש נפוץ לדוגמה הוא שימוש במיקום הגלילה כדי להחיל אפקטים על פריטים של מכשיר החלפה בין דפים. כדי לדעת כמה דפים מפרידים בין דף מסוים לבין הדף שנבחר כרגע, אפשר להשתמש בPagerState.currentPageOffsetFraction
.
לאחר מכן תוכלו להחיל על התוכן אפקטים של טרנספורמציה על סמך המרחק מהדף שנבחר.
לדוגמה, כדי לשנות את השקיפות של פריטים בהתאם למרחק שלהם מהמרכז, משנים את alpha
באמצעות Modifier.graphicsLayer
בפריט בתוך רכיב הניווט:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager(state = pagerState) { page -> Card( Modifier .size(200.dp) .graphicsLayer { // Calculate the absolute offset for the current page from the // scroll position. We use the absolute value which allows us to mirror // any effects for both directions val pageOffset = ( (pagerState.currentPage - page) + pagerState .currentPageOffsetFraction ).absoluteValue // We animate the alpha, between 50% and 100% alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - pageOffset.coerceIn(0f, 1f) ) } ) { // Card content } }
גודלי דפים בהתאמה אישית
כברירת מחדל, הרכיבים HorizontalPager
ו-VerticalPager
תופסים את הרוחב המלא או את הגובה המלא, בהתאמה. אפשר להגדיר את המשתנה pageSize
כך שיכיל את הערך Fixed
, את הערך Fill
(ברירת מחדל) או חישוב מותאם אישית של הגודל.
לדוגמה, כדי להגדיר דף ברוחב קבוע של 100.dp
:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(100.dp) ) { page -> // page content }
כדי לשנות את הגודל של הדפים בהתאם לגודל אזור התצוגה, צריך להשתמש בחישוב מותאם אישית של גודל הדף. יוצרים אובייקט PageSize
בהתאמה אישית ומחלקים את availableSpace
בשלוש, תוך התחשבות ברווחים בין הפריטים:
private val threePagesPerViewport = object : PageSize { override fun Density.calculateMainAxisPageSize( availableSpace: Int, pageSpacing: Int ): Int { return (availableSpace - 2 * pageSpacing) / 3 } }
מרווח פנימי של תוכן
גם HorizontalPager
וגם VerticalPager
תומכים בשינוי הריווח הפנימי של התוכן, כך שאפשר להשפיע על הגודל המקסימלי של הדפים ועל היישור שלהם.
לדוגמה, הגדרת הריווח start
מיישרת את הדפים לסוף:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(start = 64.dp), ) { page -> // page content }
אם מגדירים את אותו ערך לשני מאפייני הריווח הפנימי, start
ו-end
, הפריט ימוקם במרכז בצורה אופקית:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 32.dp), ) { page -> // page content }
הגדרת הריווח הפנימי end
מיישרת את הדפים לכיוון ההתחלה:
val pagerState = rememberPagerState(pageCount = { 4 }) HorizontalPager( state = pagerState, contentPadding = PaddingValues(end = 64.dp), ) { page -> // page content }
אפשר להגדיר את הערכים top
ו-bottom
כדי להשיג השפעות דומות על VerticalPager
. הערך 32.dp
משמש כאן רק כדוגמה. אתם יכולים להגדיר כל ערך לכל אחד מהמאפיינים של הריווח הפנימי.
התאמה אישית של התנהגות הגלילה
רכיבי ה-composable HorizontalPager
ו-VerticalPager
שמוגדרים כברירת מחדל מציינים איך מחוות גלילה פועלות עם רכיב ה-pager. עם זאת, אפשר להתאים אישית ולשנות את ברירות המחדל, כמו pagerSnapDistance
או flingBehavior
.
מרחק ההצמדה
כברירת מחדל, HorizontalPager
ו-VerticalPager
מגדירים את המספר המקסימלי של דפים שאפשר לדפדף בהם באמצעות תנועת החלקה, כך שכל החלקה תעביר אתכם לדף אחד. כדי לשנות את זה, צריך להגדיר את pagerSnapDistance
בflingBehavior
:
val pagerState = rememberPagerState(pageCount = { 10 }) val fling = PagerDefaults.flingBehavior( state = pagerState, pagerSnapDistance = PagerSnapDistance.atMost(10) ) Column(modifier = Modifier.fillMaxSize()) { HorizontalPager( state = pagerState, pageSize = PageSize.Fixed(200.dp), beyondViewportPageCount = 10, flingBehavior = fling ) { PagerSampleItem(page = it) } }
יצירת דפדפן עם מעבר אוטומטי בין דפים
בקטע הזה מוסבר איך ליצור רכיב החלפה אוטומטית של דפים עם אינדיקטורים של הדפים ב-Compose. הפריטים נגללים אוטומטית אופקית, אבל המשתמשים יכולים גם להחליק בין הפריטים באופן ידני. אם משתמש יוצר אינטראקציה עם הכלי להחלפת דפים, ההתקדמות האוטומטית נעצרת.
דוגמה בסיסית
ביחד, קטעי הקוד הבאים יוצרים הטמעה בסיסית של רכיב החלפת דפים אוטומטית עם אינדיקטור ויזואלי, שבו כל דף מוצג בצבע אחר:
@Composable fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { val pagerState = rememberPagerState(pageCount = { pageItems.size }) val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState() val pageInteractionSource = remember { MutableInteractionSource() } val pageIsPressed by pageInteractionSource.collectIsPressedAsState() // Stop auto-advancing when pager is dragged or one of the pages is pressed val autoAdvance = !pagerIsDragged && !pageIsPressed if (autoAdvance) { LaunchedEffect(pagerState, pageInteractionSource) { while (true) { delay(2000) val nextPage = (pagerState.currentPage + 1) % pageItems.size pagerState.animateScrollToPage(nextPage) } } } HorizontalPager( state = pagerState ) { page -> Text( text = "Page: $page", textAlign = TextAlign.Center, modifier = modifier .fillMaxSize() .background(pageItems[page]) .clickable( interactionSource = pageInteractionSource, indication = LocalIndication.current ) { // Handle page click } .wrapContentSize(align = Alignment.Center) ) } PagerIndicator(pageItems.size, pagerState.currentPage) } }
נקודות עיקריות לגבי הקוד
- הפונקציה
AutoAdvancePager
יוצרת תצוגת החלפה אופקית של דפים עם מעבר אוטומטי. הפונקציה מקבלת כקלט רשימה של אובייקטיםColor
, שמשמשים כצבעי רקע לכל דף. -
pagerState
נוצר באמצעותrememberPagerState
, שמכיל את המצב של רכיב ההחלפה בין דפים. -
pagerIsDragged
ו-pageIsPressed
עוקבים אחרי האינטראקציה של המשתמשים. - הלחצן
LaunchedEffect
מעביר את הדפים אוטומטית כל שתי שניות, אלא אם המשתמש גורר את הדפים או לוחץ על אחד מהם. HorizontalPager
מציג רשימה של דפים, שלכל אחד מהם ישText
שאפשר להרכיב ממנו מספר דף. המשנה ממלא את הדף, מגדיר את צבע הרקע מ-pageItems
והופך את הדף ללחיץ.
@Composable fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) { Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier .wrapContentHeight() .fillMaxWidth() .align(Alignment.BottomCenter) .padding(bottom = 8.dp), horizontalArrangement = Arrangement.Center ) { repeat(pageCount) { iteration -> val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray Box( modifier = modifier .padding(2.dp) .clip(CircleShape) .background(color) .size(16.dp) ) } } } }
נקודות עיקריות לגבי הקוד
- רכיב
Box
ניתן להרכבה משמש כרכיב הבסיס.- בתוך
Box
, רכיבRow
מסדר את אינדיקטורי הדפים בצורה אופקית.
- בתוך
- אינדיקטור מותאם אישית של הדף מוצג כשורה של עיגולים, כאשר כל
Box
עיגולcircle
מייצג דף. - העיגול של הדף הנוכחי צבוע בצבע
DarkGray
, והעיגולים האחרים צבועים בצבעLightGray
. הפרמטרcurrentPageIndex
קובע איזה עיגול יוצג באפור כהה.
התוצאה
בסרטון הזה מוצג רכיב בסיסי של דפדוף אוטומטי בין דפים, שמבוסס על קטעי הקוד הקודמים: