التمرير المتداخل هو نظام تعمل فيه مكوّنات تمرير متعددة مضمّنة في بعضها البعض معًا من خلال التفاعل مع إيماءة تمرير واحدة وإرسال تغييرات التمرير.
يتيح نظام الانتقال المتداخل التنسيق بين المكوّنات التي يمكن الانتقال فيها والمرتبطة بشكل هرمي (غالبًا من خلال مشاركة المكوّن الرئيسي نفسه). يربط هذا النظام بين حاويات التنقّل ويسمح بالتفاعل مع فروق التنقّل التي يتم نشرها ومشاركتها بينها.
توفّر أداة 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.
بعد ذلك، سيؤدي العنصر الفرعي إلى الانتقال المتداخل للعنصر الرئيسي عندما يكون العنصر الفرعي عند
الحد. على سبيل المثال، تسمح هذه القاعدة لـ Compose Pager وCompose LazyRow
بالعمل معًا بشكل جيد. ومع ذلك، عند إجراء التمرير القابل للتشغيل التفاعلي
باستخدام 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>
في Activity أو Fragment، عليك إعداد الدالة المركّبة الفرعية و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( title = {}, 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(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
العرض غير المتعاون هو العرض الذي لا ينفّذ واجهات NestedScrolling اللازمة على جانب View. يُرجى العِلم أنّ هذا يعني أنّ إمكانية التشغيل التفاعلي للتمرير المتداخل مع Views هذه لا تعمل بشكل تلقائي. الـ Views غير المتعاونة هي RecyclerView وViewPager2.
مراجع إضافية
التطبيقات المقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعَّل
- التعرّف على الإيماءات
- نقل بيانات
CoordinatorLayoutإلى Compose - استخدام طرق العرض في Compose