التمرير المتداخل هو نظام تعمل فيه مكوّنات تمرير متعدّدة مضمّنة معًا من خلال الاستجابة لإيماءة تمرير واحدة وتوصيل التغييرات في التمرير (الزيادات والنقصان).
يسمح نظام التمرير المتداخل بالتنسيق بين المكوّنات القابلة للتمرير والمرتبطة بشكل هرمي (غالبًا من خلال مشاركة العنصر الرئيسي نفسه). يربط هذا النظام حاويات التمرير ويسمح بالتفاعل مع التغييرات في التمرير التي يتم نشرها ومشاركتها بينها.
توفّر Compose طرقًا متعدّدة للتعامل مع التمرير المتداخل بين العناصر المركّبة. من الأمثلة النموذجية على التمرير المتداخل قائمة داخل قائمة أخرى، ومن الحالات الأكثر تعقيدًا شريط أدوات قابل للطي.
التمرير المتداخل التلقائي
لا يتطلّب التمرير المتداخل البسيط أي إجراء من جانبك. يتم نشر الإيماءات التي تبدأ إجراء تمرير من العناصر الفرعية إلى العناصر الرئيسية تلقائيًا، بحيث عندما لا يتمكّن العنصر الفرعي من التمرير أكثر، يعالج العنصر الرئيسي الإيماءة.
تتوفّر ميزة التمرير المتداخل التلقائي في بعض مكوّنات
Compose ومعدِّلاتها، مثل
verticalScroll و
horizontalScroll و
scrollable وواجهات برمجة التطبيقات
Lazy وTextField. هذا يعني أنّه عندما يمرّر المستخدم عنصرًا فرعيًا داخليًا لمكوّنات متداخلة، تنشر المعدِّلات السابقة التغييرات في التمرير إلى العناصر الرئيسية التي تتوافق مع التمرير المتداخل.
يعرض المثال التالي عناصر تم تطبيق المعدِّل
verticalScroll
عليها داخل حاوية تم تطبيق المعدِّل verticalScroll
عليها أيضًا.
@Composable private fun AutomaticNestedScroll() { val gradient = Brush.verticalGradient(0f to Color.Gray, 1000f to Color.White) Box( modifier = Modifier .background(Color.LightGray) .verticalScroll(rememberScrollState()) .padding(32.dp) ) { Column { repeat(6) { Box( modifier = Modifier .height(128.dp) .verticalScroll(rememberScrollState()) ) { Text( "Scroll here", modifier = Modifier .border(12.dp, Color.DarkGray) .background(brush = gradient) .padding(24.dp) .height(150.dp) ) } } } } }
استخدام المعدِّل nestedScroll
إذا كنت بحاجة إلى إنشاء تمرير منسّق متقدّم بين عناصر متعدّدة،
يمنحك
nestedScroll
المعدِّل مزيدًا من المرونة من خلال تحديد تسلسل هرمي للتمرير المتداخل. كما هو موضّح في القسم السابق، تتوافق بعض المكوّنات مع التمرير المتداخل. ومع ذلك، بالنسبة إلى العناصر المركّبة التي لا يمكن تمريرها تلقائيًا، مثل Box أو Column، لن يتم نشر التغييرات في التمرير على هذه المكوّنات في نظام التمرير المتداخل، ولن تصل التغييرات إلى NestedScrollConnection أو المكوّن الرئيسي. لحلّ هذه المشكلة، يمكنك استخدام nestedScroll لمنح هذه الميزة لمكوّنات أخرى، بما في ذلك المكوّنات المخصّصة.
دورة التمرير المتداخل
دورة التمرير المتداخل هي تدفّق التغييرات في التمرير التي يتم إرسالها لأعلى ولأسفل شجرة التسلسل الهرمي من خلال جميع المكوّنات (أو العُقد) التي تشكّل جزءًا من نظام التمرير المتداخل، مثلاً باستخدام مكوّنات ومعدِّلات قابلة للتمرير أو nestedScroll.
مراحل دورة التمرير المتداخل
عندما يرصد مكوّن قابل للتمرير حدثًا مشغِّلاً (مثلاً، إيماءة)، قبل أن يتم تفعيل إجراء التمرير الفعلي، يتم إرسال التغييرات التي تم إنشاؤها إلى نظام التمرير المتداخل وتمر بثلاث مراحل: ما قبل التمرير واستهلاك العُقدة وما بعد التمرير.
في المرحلة الأولى، وهي مرحلة ما قبل التمرير، سيرسل المكوّن الذي تلقّى التغييرات في أحداث المشغِّل هذه الأحداث لأعلى، من خلال شجرة التسلسل الهرمي، إلى العنصر الرئيسي الأعلى. بعد ذلك، ستنتقل أحداث التغيير لأسفل، ما يعني أنّه سيتم نشر التغييرات من العنصر الرئيسي الأعلى إلى العنصر الفرعي الذي بدأ دورة التمرير المتداخل.
يمنح ذلك العناصر الرئيسية للتمرير المتداخل (العناصر المركّبة التي تستخدم nestedScroll أو المعدِّلات القابلة للتمرير) فرصة إجراء إجراء معيّن باستخدام التغيير قبل أن تتمكّن العُقدة نفسها من استخدامه.
في مرحلة استهلاك العُقدة، ستستخدم العُقدة نفسها أي تغيير لم يستخدمه العنصر الرئيسي. في هذه المرحلة، يتم إجراء حركة التمرير فعليًا وتكون مرئية.
خلال هذه المرحلة، يمكن للعنصر الفرعي اختيار استخدام كل التمرير المتبقي أو جزء منه. سيتم إرسال أي شيء متبقٍ لأعلى مرة أخرى للانتقال إلى مرحلة ما بعد التمرير.
أخيرًا، في مرحلة ما بعد التمرير، سيتم إرسال أي شيء لم تستخدمه العُقدة نفسها لأعلى مرة أخرى إلى العناصر الرئيسية لاستخدامه.
تعمل مرحلة ما بعد التمرير بطريقة مشابهة لمرحلة ما قبل التمرير، حيث يمكن لأي من العناصر الرئيسية اختيار استخدام التغيير أو عدم استخدامه.
على غرار التمرير، عند انتهاء إيماءة السحب، يمكن ترجمة نيّة المستخدم إلى سرعة تُستخدَم لرمي الحاوية القابلة للتمرير (التمرير باستخدام حركة). يُعدّ التمرير بالإصبع ثم رفعه بسرعة أيضًا جزءًا من دورة التمرير المتداخل، وتمر السرعات التي يتم إنشاؤها من خلال حدث سحب بمراحل مشابهة: ما قبل التمرير بالإصبع ثم رفعه بسرعة واستهلاك العقدة وما بعد التمرير بالإصبع ثم رفعه بسرعة. يُرجى العِلم أنّ حركة التمرير السريع مرتبطة فقط بإيماءة اللمس ولن يتم تفعيلها من خلال أحداث أخرى، مثل تسهيل الاستخدام أو التمرير على الأجهزة.
المشاركة في دورة التمرير المتداخل
تعني المشاركة في الدورة اعتراض التغييرات واستهلاكها والإبلاغ عن استهلاكها على طول التسلسل الهرمي. توفّر Compose مجموعة من الأدوات للتأثير في طريقة عمل نظام التمرير المتداخل وكيفية التفاعل معه مباشرةً، مثلاً عندما تحتاج إلى إجراء إجراء معيّن باستخدام التغييرات في التمرير قبل أن يبدأ مكوّن قابل للتمرير في التمرير.
إذا كانت دورة التمرير المتداخل نظامًا يعمل على سلسلة من العُقد، فإنّ المعدِّل
nestedScroll
هو طريقة لاعتراض هذه التغييرات وإدراجها فيها، و
التأثير في البيانات (التغييرات في التمرير) التي يتم نشرها في السلسلة. يمكن وضع هذا المعدِّل في أي مكان في التسلسل الهرمي، ويتواصل مع مثيلات المعدِّل المتداخل للتمرير لأعلى الشجرة حتى يتمكّن من مشاركة المعلومات من خلال هذه القناة. إنّ الكتل البرمجية الإنشائية لهذا المعدِّل هي NestedScrollConnection وNestedScrollDispatcher.
NestedScrollConnection
توفّر طريقة للاستجابة لمراحل دورة التمرير المتداخل والتأثير في
نظام التمرير المتداخل. ويتألف من أربع طرق ردّ، يمثّل كل منها إحدى مراحل الاستهلاك: ما قبل التمرير وما بعد التمرير وما قبل الرمي وما بعد الرمي:
val nestedScrollConnection = object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { println("Received onPreScroll callback.") return Offset.Zero } override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource ): Offset { println("Received onPostScroll callback.") return Offset.Zero } }
تقدّم كل طريقة ردّ أيضًا معلومات عن التغيير الذي يتم نشره: التغيير available لهذه المرحلة المحدّدة، والتغيير consumed الذي تم استخدامه في المراحل السابقة. إذا أردت في أي وقت إيقاف نشر التغييرات لأعلى التسلسل الهرمي، يمكنك استخدام اتصال التمرير المتداخل لإجراء ذلك:
val disabledNestedScrollConnection = remember { object : NestedScrollConnection { override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource ): Offset { return if (source == NestedScrollSource.SideEffect) { available } else { Offset.Zero } } } }
تقدّم جميع طرق الردّ معلومات عن الـ
NestedScrollSource
نوع.
NestedScrollDispatcher
تُعدّ دورة التمرير المتداخل. يؤدي استخدام أداة إرسال واستدعاء طرقها إلى تفعيل الدورة. تحتوي الحاويات القابلة للتمرير على أداة إرسال مدمَجة ترسل التغييرات التي تم التقاطها أثناء الإيماءات إلى النظام. لهذا السبب، تتضمّن معظم حالات استخدام تخصيص التمرير المتداخل استخدام NestedScrollConnection بدلاً من أداة إرسال، للاستجابة للتغييرات الحالية بدلاً من إرسال تغييرات جديدة.
يمكنك الاطّلاع على
NestedScrollDispatcherSample
لمزيد من حالات الاستخدام.
تغيير حجم صورة عند التمرير
أثناء تمرير المستخدم، يمكنك إنشاء تأثير مرئي ديناميكي يتغيّر فيه حجم الصورة استنادًا إلى موضع التمرير.
تغيير حجم صورة استنادًا إلى موضع التمرير
توضّح هذه المقتطفة تغيير حجم صورة داخل LazyColumn استنادًا إلى
موضع التمرير العمودي. تتقلّص الصورة أثناء تمرير المستخدم للأسفل، وتزداد أثناء تمريره للأعلى، وتبقى ضمن حدود الحجم الأدنى والأقصى المحدّدة:
@Composable fun ImageResizeOnScrollExample( modifier: Modifier = Modifier, maxImageSize: Dp = 300.dp, minImageSize: Dp = 100.dp ) { var currentImageSize by remember { mutableStateOf(maxImageSize) } var imageScale by remember { mutableFloatStateOf(1f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { // Calculate the change in image size based on scroll delta val delta = available.y val newImageSize = currentImageSize + delta.dp val previousImageSize = currentImageSize // Constrain the image size within the allowed bounds currentImageSize = newImageSize.coerceIn(minImageSize, maxImageSize) val consumed = currentImageSize - previousImageSize // Calculate the scale for the image imageScale = currentImageSize / maxImageSize // Return the consumed scroll amount return Offset(0f, consumed.value) } } } Box(Modifier.nestedScroll(nestedScrollConnection)) { LazyColumn( Modifier .fillMaxWidth() .padding(15.dp) .offset { IntOffset(0, currentImageSize.roundToPx()) } ) { // Placeholder list items items(100, key = { it }) { Text( text = "Item: $it", style = MaterialTheme.typography.bodyLarge ) } } Image( painter = ColorPainter(Color.Red), contentDescription = "Red color image", Modifier .size(maxImageSize) .align(Alignment.TopCenter) .graphicsLayer { scaleX = imageScale scaleY = imageScale // Center the image vertically as it scales translationY = -(maxImageSize.toPx() - currentImageSize.toPx()) / 2f } ) } }
نقاط أساسية حول الرمز
- يستخدم هذا الرمز
NestedScrollConnectionلاعتراض أحداث التمرير. onPreScrollتحسب التغيير في حجم الصورة استنادًا إلى التغيير في التمرير.- يخزّن متغيّر الحالة
currentImageSizeالحجم الحالي للصورة، ويتم تقييده بينminImageSizeوmaxImageSize. imageScaleمستمد منcurrentImageSize. - يتم إزاحة
LazyColumnاستنادًا إلىcurrentImageSize. - تستخدم
ImageالمعدِّلgraphicsLayerلتطبيق المقياس المحسوب. - يضمن
translationYداخلgraphicsLayerبقاء الصورة في المنتصف عموديًا أثناء تغيير حجمها.
النتيجة
تؤدي المقتطفة السابقة إلى تأثير تغيير حجم الصورة عند التمرير:
التوافق بين التمرير المتداخل
عند محاولة تضمين عناصر View قابلة للتمرير في عناصر مركّبة قابلة للتمرير، أو العكس، قد تواجه مشاكل. تحدث المشاكل الأكثر وضوحًا عند تمرير العنصر الفرعي والوصول إلى حدود بدايته أو نهايته وتوقّع أن يتولّى العنصر الرئيسي التمرير. ومع ذلك، قد لا يحدث هذا السلوك المتوقّع أو قد لا يعمل على النحو المتوقّع.
تنتج هذه المشكلة عن التوقّعات المضمّنة في العناصر المركّبة القابلة للتمرير.
تتّبع العناصر المركّبة القابلة للتمرير قاعدة "التمرير المتداخل تلقائيًا"، ما يعني أنّ
أي حاوية قابلة للتمرير يجب أن تشارك في سلسلة التمرير المتداخل، كعنصر رئيسي من خلال
NestedScrollConnection،
وكعنصر فرعي من خلال
NestedScrollDispatcher.
بعد ذلك، سيؤدي العنصر الفرعي إلى تمرير متداخل للعنصر الرئيسي عندما يكون العنصر الفرعي عند الحد. على سبيل المثال، تسمح هذه القاعدة لـ Pager وLazyRow في Compose بالعمل معًا بشكل جيد. ومع ذلك، عند إجراء تمرير قابل للتشغيل التفاعلي
باستخدام ViewPager2 أو RecyclerView، بما أنّهما لا تنفّذان
NestedScrollingParent3،
لا يمكن إجراء التمرير المستمر من العنصر الفرعي إلى العنصر الرئيسي.
لتفعيل واجهة برمجة التطبيقات للتوافق بين التمرير المتداخل بين عناصر View القابلة للتمرير والعناصر المركّبة القابلة للتمرير، المتداخلة في كلا الاتجاهَين، يمكنك استخدام واجهة برمجة التطبيقات للتوافق بين التمرير المتداخل للتخفيف من هذه المشاكل في السيناريوهات التالية.
عنصر رئيسي متعاون View يحتوي على عنصر فرعي ComposeView
عنصر رئيسي متعاون View هو عنصر ينفّذ
NestedScrollingParent3
وبالتالي يمكنه تلقّي التغييرات في التمرير من عنصر فرعي متداخل متعاون
مركّب. سيعمل ComposeView كعنصر فرعي في هذه الحالة ويجب أن
ينفّذ (بشكل غير مباشر)
NestedScrollingChild3.
من الأمثلة على العنصر الرئيسي المتعاون androidx.coordinatorlayout.widget.CoordinatorLayout.
إذا كنت بحاجة إلى التوافق بين التمرير المتداخل بين حاويات View الرئيسية القابلة للتمرير والعناصر المركّبة الفرعية القابلة للتمرير المتداخلة، يمكنك استخدام rememberNestedScrollInteropConnection().
rememberNestedScrollInteropConnection() تسمح وتتذكّر NestedScrollConnection التي تتيح إمكانية التشغيل التفاعلي للتمرير المتداخل بين عنصر View رئيسي ينفّذ NestedScrollingParent3 وعنصر فرعي في Compose. يجب استخدام هذه الطريقة مع المعدِّل
nestedScroll. بما أنّ التمرير المتداخل مفعّل تلقائيًا على جانب Compose، يمكنك
استخدام هذا الاتصال لتفعيل التمرير المتداخل على جانب View وإضافة
منطق الربط الضروري بين Views والعناصر المركّبة.
من حالات الاستخدام الشائعة استخدام CoordinatorLayout وCollapsingToolbarLayout وعنصر مركّب فرعي، كما هو موضّح في هذا المثال:
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/app_bar" android:layout_width="match_parent" android:layout_height="100dp" android:fitsSystemWindows="true"> <com.google.android.material.appbar.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <!--...--> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.compose.ui.platform.ComposeView android:id="@+id/compose_view" app:layout_behavior="@string/appbar_scrolling_view_behavior" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.coordinatorlayout.widget.CoordinatorLayout>
في نشاطك أو جزءك، عليك إعداد العنصر المركّب الفرعي و
المطلوبة
NestedScrollConnection:
open class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById<ComposeView>(R.id.compose_view).apply { setContent { val nestedScrollInterop = rememberNestedScrollInteropConnection() // Add the nested scroll connection to your top level @Composable element // using the nestedScroll modifier. LazyColumn(modifier = Modifier.nestedScroll(nestedScrollInterop)) { items(20) { item -> Box( modifier = Modifier .padding(16.dp) .height(56.dp) .fillMaxWidth() .background(Color.Gray), contentAlignment = Alignment.Center ) { Text(item.toString()) } } } } } } }
عنصر مركّب رئيسي يحتوي على عنصر AndroidView فرعي
يغطّي هذا السيناريو تنفيذ واجهة برمجة التطبيقات للتوافق بين التمرير المتداخل على جانب Compose، أي عندما يكون لديك عنصر مركّب رئيسي يحتوي على عنصر AndroidView فرعي. ينفّذ AndroidView كلاً من
NestedScrollDispatcher،
لأنّه يعمل كعنصر فرعي لعنصر رئيسي في Compose قابل للتمرير، و
NestedScrollingParent3
، لأنّه يعمل كعنصر رئيسي لعنصر View فرعي قابل للتمرير. بعد ذلك، سيتمكّن العنصر الرئيسي في Compose من تلقّي التغييرات في التمرير المتداخل من عنصر View فرعي قابل للتمرير ومتداخل.
يوضّح المثال التالي كيفية تحقيق التوافق بين التمرير المتداخل في هذا السيناريو، بالإضافة إلى شريط أدوات قابل للطي في Compose:
@Composable
private fun NestedScrollInteropComposeParentWithAndroidChildExample() {
val toolbarHeightPx = with(LocalDensity.current) { ToolbarHeight.roundToPx().toFloat() }
val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
// Sets up the nested scroll connection between the Box composable parent
// and the child AndroidView containing the RecyclerView
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
// Updates the toolbar offset based on the scroll to enable
// collapsible behaviour
val delta = available.y
val newOffset = toolbarOffsetHeightPx.value + delta
toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
return Offset.Zero
}
}
}
Box(
Modifier
.fillMaxSize()
.nestedScroll(nestedScrollConnection)
) {
TopAppBar(
modifier = Modifier
.height(ToolbarHeight)
.offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) }
)
AndroidView(
{ context ->
LayoutInflater.from(context)
.inflate(R.layout.view_in_compose_nested_scroll_interop, null).apply {
with(findViewById<RecyclerView>(R.id.main_list)) {
layoutManager = LinearLayoutManager(context, VERTICAL, false)
adapter = NestedScrollInteropAdapter()
}
}.also {
// Nested scrolling interop is enabled when
// nested scroll is enabled for the root View
ViewCompat.setNestedScrollingEnabled(it, true)
}
},
// ...
)
}
}
private class NestedScrollInteropAdapter :
Adapter<NestedScrollInteropAdapter.NestedScrollInteropViewHolder>() {
val items = (1..10).map { it.toString() }
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): NestedScrollInteropViewHolder {
return NestedScrollInteropViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
)
}
override fun onBindViewHolder(holder: NestedScrollInteropViewHolder, position: Int) {
// ...
}
class NestedScrollInteropViewHolder(view: View) : ViewHolder(view) {
fun bind(item: String) {
// ...
}
}
// ...
}
يوضّح هذا المثال كيفية استخدام واجهة برمجة التطبيقات مع المعدِّل scrollable:
@Composable
fun ViewInComposeNestedScrollInteropExample() {
Box(
Modifier
.fillMaxSize()
.scrollable(rememberScrollableState {
// View component deltas should be reflected in Compose
// components that participate in nested scrolling
it
}, Orientation.Vertical)
) {
AndroidView(
{ context ->
LayoutInflater.from(context)
.inflate(android.R.layout.list_item, null)
.apply {
// Nested scrolling interop is enabled when
// nested scroll is enabled for the root View
ViewCompat.setNestedScrollingEnabled(this, true)
}
}
)
}
}
أخيرًا، يوضّح هذا المثال كيفية استخدام واجهة برمجة التطبيقات للتوافق بين التمرير المتداخل مع
BottomSheetDialogFragment
لتحقيق سلوك ناجح للسحب والإغلاق:
class BottomSheetFragment : BottomSheetDialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootView: View = inflater.inflate(R.layout.fragment_bottom_sheet, container, false)
rootView.findViewById<ComposeView>(R.id.compose_view).apply {
setContent {
val nestedScrollInterop = rememberNestedScrollInteropConnection()
LazyColumn(
Modifier
.nestedScroll(nestedScrollInterop)
.fillMaxSize()
) {
item {
Text(text = "Bottom sheet title")
}
items(10) {
Text(
text = "List item number $it",
modifier = Modifier.fillMaxWidth()
)
}
}
}
return rootView
}
}
}
يُرجى العِلم أنّ
rememberNestedScrollInteropConnection()
ستثبّت
NestedScrollConnection
في العنصر الذي تربطه به. تتولّى NestedScrollConnection نقل التغييرات من مستوى Compose إلى مستوى View. يتيح ذلك للعنصر المشاركة في التمرير المتداخل، ولكنّه لا يتيح تمرير العناصر تلقائيًا. بالنسبة إلى العناصر المركّبة التي لا يمكن تمريرها تلقائيًا، مثل Box أو Column، لن يتم نشر التغييرات في التمرير على هذه المكوّنات في نظام التمرير المتداخل، ولن تصل التغييرات إلى NestedScrollConnection التي توفّرها rememberNestedScrollInteropConnection()، وبالتالي لن تصل هذه التغييرات إلى مكوّن View الرئيسي. لحلّ هذه المشكلة، تأكَّد من ضبط المعدِّلات القابلة للتمرير على هذه الأنواع من العناصر المركّبة المتداخلة أيضًا. يمكنك الرجوع إلى القسم السابق حول التمرير
المتداخل للحصول على معلومات أكثر تفصيلاً.
عنصر رئيسي غير متعاون View يحتوي على عنصر فرعي ComposeView
عنصر View غير متعاون هو عنصر لا ينفّذ واجهات NestedScrolling الضرورية على جانب View. يُرجى العِلم أنّ هذا يعني أنّ التوافق بين التمرير المتداخل مع عناصر Views هذه لا يعمل تلقائيًا. إنّ Views غير المتعاونة هي RecyclerView وViewPager2.
مراجع إضافية
مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عند إيقاف JavaScript
- التعرّف على الإيماءات
- نقل
CoordinatorLayoutإلى Compose - استخدام Views في Compose