כדי לדפדף בתוכן בצורה אופקית או אנכית, אתם יכולים להשתמש ברכיבים הניתנים לTex-הרכבה 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 נוצרים באופן עצל ומוצגים לפי הצורך. כשהמשתמש גולל בין הדפים, הפונקציה הקומפוזבילית מסירה את כל הדפים שכבר לא נדרשים.
טעינת דפים נוספים מחוץ למסך
כברירת מחדל, הכלי לדפדוף טוען רק את הדפים שמוצגים במסך. כדי לטעון עוד דפים מחוץ למסך, צריך להגדיר את 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, קובעים איך תנועות הגלילה פועלות עם הכלי לדפדוף. עם זאת, אתם יכולים להתאים אישית ולשנות את ברירות המחדל, כמו 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מציג רשימה של דפים, כל אחד עםTextcomposable שמציג את מספר הדף. המשתנה ממלא את הדף, מגדיר את צבע הרקע מ-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משמש כרכיב הבסיס ומכילRowכדי לסדר את האינדיקטורים של הדפים בצורה אופקית. - אינדיקטור מותאם אישית של דף מוצג כשורה של עיגולים, שבה כל
Boxשחתוך ל-CircleShapeמייצג דף. - העיגול של הדף הנוכחי הוא בצבע
DarkGray, והעיגולים האחרים הם בצבעLightGray. הפרמטרcurrentPageIndexקובע איזה עיגול מוצג באפור כהה.
תוצאה
בסרטון הזה מוצג הכלי הבסיסי לדפדוף עם מעבר אוטומטי מקטעי הקוד הקודמים: