للتنقّل بين المحتوى بشكل أفقي أو عمودي، يمكنك استخدام العنصرَين القابلَين للإنشاء
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
لمراقبة التغييرات في هذه المتغيرات
والتفاعل معها. على سبيل المثال، لإرسال حدث إحصاءات عند كل تغيير في الصفحة، يمكنك اتّباع الخطوات التالية:
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
هنا كمثال فقط، ويمكنك ضبط كل سمات المساحة المتروكة على أي قيمة.
تخصيص سلوك التمرير
تحدّد الدالتان القابلتان للإنشاء التلقائيتان 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
قائمة بالصفحات، تحتوي كل منها على عنصر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
الدائرة التي سيتم عرضها باللون الرمادي الداكن.
النتيجة
يعرض هذا الفيديو أداة التنقل الأساسية التي يتم فيها الانتقال تلقائيًا إلى الصفحة التالية من المقتطفات السابقة: