تتضمّن العديد من الدوال المركّبة دعمًا مدمجًا للنقرات وتتضمّن دالة lambda باسم onClick. على سبيل المثال، يمكنك إنشاء Surface قابل للنقر يتضمّن جميع سلوكيات التصميم المتعدد الأبعاد المناسبة للتفاعل مع الأسطح:
Surface(onClick = { /* handle click */ }) { Text("Click me!", Modifier.padding(24.dp)) }
ولكن النقرات ليست الطريقة الوحيدة التي يمكن للمستخدم من خلالها التفاعل مع الدوال المركّبة. تركّز هذه الصفحة على الإيماءات التي تتضمّن مؤشرًا واحدًا، حيث لا يكون موضع هذا المؤشر مهمًا للتعامل مع هذا الحدث. يسرد الجدول التالي هذه الأنواع من الإيماءات:
الإيماءة |
الوصف |
النقر |
ينتقل المؤشر للأسفل ثم للأعلى |
النقر مرّتين |
يتحرّك المؤشر للأسفل، ثم للأعلى، ثم للأسفل، ثم للأعلى |
الضغط مع الاستمرار |
يتحرّك المؤشر للأسفل ويتم الضغط عليه لفترة أطول |
الصحافة |
المؤشر يتّجه للأسفل |
الاستجابة للنقر
clickable هو معدِّل شائع الاستخدام يجعل الدالة المركّبة تتفاعل مع
النقرات. يضيف هذا المُعدِّل أيضًا ميزات إضافية، مثل إتاحة التركيز، والتمرير بالماوس وقلم الشاشة، ومؤشر مرئي قابل للتخصيص عند الضغط عليه. يستجيب المُعدِّل لـ "النقرات" بأوسع معنى للكلمة، ليس فقط باستخدام الماوس أو الإصبع، ولكن أيضًا للأحداث الناتجة عن النقر من خلال الإدخال باستخدام لوحة المفاتيح أو عند استخدام خدمات تسهيل الاستخدام.
لنفترض أنّ لديك شبكة من الصور، وعندما ينقر المستخدم على صورة، يتم عرضها بملء الشاشة:
يمكنك إضافة المُعدِّل clickable إلى كل عنصر في الشبكة لتنفيذ هذا السلوك:
@Composable private fun ImageGrid(photos: List<Photo>) { var activePhotoId by rememberSaveable { mutableStateOf<Int?>(null) } LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier.clickable { activePhotoId = photo.id } ) } } if (activePhotoId != null) { FullScreenImage( photo = photos.first { it.id == activePhotoId }, onDismiss = { activePhotoId = null } ) } }
يضيف المُعدِّل clickable أيضًا سلوكًا إضافيًا:
interactionSourceوindication، اللذان يرسمان تموّجًا تلقائيًا عندما ينقر المستخدم على الدالة المركّبة. يمكنك التعرّف على كيفية تخصيص هذه الإعدادات في صفحة التعامل مع تفاعلات المستخدمين.- يسمح لخدمات تسهيل الاستخدام بالتفاعل مع العنصر من خلال ضبط معلومات الدلالات.
- يتيح التفاعل باستخدام لوحة المفاتيح أو ذراع التحكّم من خلال السماح بالتركيز والضغط على
Enterأو منتصف لوحة التحكّم للتفاعل. - اجعل العنصر قابلاً للتمرير فوقه، بحيث يستجيب للماوس أو قلم الشاشة الذي يحوم فوقه.
اضغط مع الاستمرار لعرض قائمة سياق
تتيح لك السمة combinedClickable إضافة سلوك النقر مرّتين أو الضغط مع الاستمرار، بالإضافة إلى سلوك النقر العادي. يمكنك استخدام combinedClickable لعرض قائمة سياق عندما يلمس المستخدم صورة في الشبكة مع الاستمرار:
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) } val haptics = LocalHapticFeedback.current LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier .combinedClickable( onClick = { activePhotoId = photo.id }, onLongClick = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) contextMenuPhotoId = photo.id }, onLongClickLabel = stringResource(R.string.open_context_menu) ) ) } } if (contextMenuPhotoId != null) { PhotoActionsSheet( photo = photos.first { it.id == contextMenuPhotoId }, onDismissSheet = { contextMenuPhotoId = null } ) }
من أفضل الممارسات تضمين تجاوب حسّي عندما يضغط المستخدم مع الاستمرار على العناصر، ولهذا السبب يتضمّن المقتطف استدعاء performHapticFeedback.
إغلاق دالة مركّبة من خلال النقر على شاشة معتمة
في الأمثلة أعلاه، يضيف clickable وcombinedClickable وظائف مفيدة إلى الدوال المركّبة. وتعرض مؤشرًا مرئيًا عند التفاعل معها، وتستجيب عند تمرير مؤشر الماوس فوقها، وتتضمّن ميزات التركيز ولوحة المفاتيح وتسهيل الاستخدام. لكن هذا السلوك الإضافي ليس مرغوبًا فيه دائمًا.
لنلقِ نظرة على شاشة تفاصيل الصورة. يجب أن تكون الخلفية شبه شفافة وأن يتمكّن المستخدم من النقر عليها لإغلاق شاشة التفاصيل:
في هذه الحالة، يجب ألا يكون لهذه الخلفية أي مؤشر مرئي على التفاعل، وألا تستجيب للتمرير فوقها، وألا تكون قابلة للتركيز، وأن تختلف استجابتها لأحداث لوحة المفاتيح وأدوات تسهيل الاستخدام عن استجابة الدالة المركّبة العادية. بدلاً من محاولة تكييف سلوك clickable، يمكنك الانتقال إلى مستوى تجريد أدنى واستخدام المُعدِّل pointerInput مباشرةً مع طريقة detectTapGestures:
@Composable private fun Scrim(onClose: () -> Unit, modifier: Modifier = Modifier) { val strClose = stringResource(R.string.close) Box( modifier // handle pointer input .pointerInput(onClose) { detectTapGestures { onClose() } } // handle accessibility services .semantics(mergeDescendants = true) { contentDescription = strClose onClick { onClose() true } } // handle physical keyboard input .onKeyEvent { if (it.key == Key.Escape) { onClose() true } else { false } } // draw scrim .background(Color.DarkGray.copy(alpha = 0.75f)) ) }
كمفتاح لمعدِّل pointerInput، يمكنك تمرير دالة lambda onClose. يؤدي ذلك إلى إعادة تنفيذ دالة lambda تلقائيًا، ما يضمن استدعاء دالة الاستدعاء الصحيحة عندما ينقر المستخدم على الشاشة المعتمة.
انقر مرّتين للتكبير أو التصغير
في بعض الأحيان، لا يتضمّن clickable وcombinedClickable معلومات كافية
للردّ على التفاعل بالطريقة الصحيحة. على سبيل المثال، قد تحتاج الدوال المركّبة إلى الوصول إلى الموضع ضمن حدود الدالة المركّبة حيث حدث التفاعل.
لنلقِ نظرة على شاشة تفاصيل الصورة مرة أخرى. من أفضل الممارسات إتاحة إمكانية تكبير الصورة من خلال النقر مرّتين:
كما ترى في الفيديو، يحدث التكبير حول موضع حدث النقر. تختلف النتيجة عندما نكبّر الجزء الأيمن من الصورة مقارنةً بالجزء الأيسر. يمكننا استخدام المُعدِّل pointerInput مع detectTapGestures لدمج موضع النقر في حساباتنا:
var zoomed by remember { mutableStateOf(false) } var zoomOffset by remember { mutableStateOf(Offset.Zero) } Image( painter = rememberAsyncImagePainter(model = photo.highResUrl), contentDescription = null, modifier = modifier .pointerInput(Unit) { detectTapGestures( onDoubleTap = { tapOffset -> zoomOffset = if (zoomed) Offset.Zero else calculateOffset(tapOffset, size) zoomed = !zoomed } ) } .graphicsLayer { scaleX = if (zoomed) 2f else 1f scaleY = if (zoomed) 2f else 1f translationX = zoomOffset.x translationY = zoomOffset.y } )
التطبيقات المقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعَّل
- التعرّف على الإيماءات
- التصميم المتعدد الأبعاد 2 في Compose
- Kotlin لـ Jetpack Compose