تک‌ضرب زدن و فشار دادن

بسیاری از عناصر ترکیبی از تک‌ضرب یا کلیک پشتیبانی داخلی دارند و شامل 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، تابع لامبدای onClose را ارسال می‌کنید. این کار به‌طور خودکار لامبدا را دوباره اجرا می‌کند و مطمئن می‌شود که وقتی کاربر روی سایه ضربه می‌زند، پاسخ‌گوی مناسب فراخوانده شود.

برای بزرگ‌نمایی دو تک‌ضرب بزنید

گاهی‌اوقات 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
        }
)