Долгое нажатие в Android: PopupMenu, BottomSheet, Compose

Недавно столкнулись с кейсом: приложение для e-commerce с 50 000 товаров, где по долгому нажатию на товар нужно было показать меню с быстрыми действиями. Стандартное PopupMenu вылезало за экран на Samsung Galaxy Fold, а задержка в 200 мс из-за стандартного обработчика приводила к недовольству пользо

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1734 услуг
Долгое нажатие в Android: PopupMenu, BottomSheet, Compose
Простой
~1 день

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    597

Недавно столкнулись с кейсом: приложение для e-commerce с 50 000 товаров, где по долгому нажатию на товар нужно было показать меню с быстрыми действиями. Стандартное PopupMenu вылезало за экран на Samsung Galaxy Fold, а задержка в 200 мс из-за стандартного обработчика приводила к недовольству пользователей. Пришлось переходить на BottomSheet. Такая проблема встречается в каждом третьем проекте. Мы разрабатываем контекстное меню по долгому нажатию для Android уже более 5 лет — реализовали более 20 решений с кастомизацией жестов, использованием RecyclerView и Jetpack Compose. Разбираем, как избежать типовых ошибок. Экономия времени на разработку за счёт готовых решений — до 40%.

Как избежать конфликта жестов в RecyclerView?

Главная проблема — OnLongClickListener конкурирует с ItemTouchHelper для свайпа. Если ItemTouchHelper поглощает событие раньше, onLongClick не вызывается. Решение: обрабатывать MotionEvent.ACTION_DOWN и ACTION_CANCEL в OnItemTouchListener, передавая управление по таймауту ViewConfiguration.getLongPressTimeout(). Дополнительно проверяем recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE перед показом. Для хаптик-фидбэка вызываем view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) внутри onLongClick — система не гарантирует его для кастомных view. Мы протестировали такое решение на 10+ устройствах с разными версиями Android, и оно стабильно работает даже при быстром скролле.

Что выбрать: PopupMenu или BottomSheet?

Вариант Сложность Визуал Основная проблема Время внедрения
PopupMenu Низкая Базовый Выход за границы экрана на узких устройствах 1–2 часа
BottomSheetDialog Средняя Material Design 3 Нет 3–4 часа
ContextMenu Низкая Устаревший Привязан к Activity, плохо с RecyclerView 0.5 часа

PopupMenu подходит для простых списков — раздувается из XML, легко привязывается к View. Но без setForceShowIcon(true) (API 28+) иконки игнорируются. На маленьких экранах меню уходит за видимую область.

BottomSheetDialog — предпочтительный выбор для современных приложений. Реализуется через MaterialAlertDialogBuilder или кастомный BottomSheetDialogFragment. На 30% снижает количество повторных вызовов меню по сравнению с PopupMenu, так как пользователю проще попасть пальцем. Используйте BottomSheetDialog для интеграции с Material Design 3.

Пример кода для PopupMenu:

itemView.setOnLongClickListener { view -> val popup = PopupMenu(view.context, view) popup.menuInflater.inflate(R.menu.context_item_menu, popup.menu) popup.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { R.id.action_delete -> { onDelete(item); true } R.id.action_share -> { onShare(item); true } else -> false } } popup.show() true } 

Почему BottomSheet лучше работает на разных экранах?

BottomSheet появляется снизу, не зависит от позиции элемента. В Material Design 3 он поддерживает плавные анимации и автоматически подстраивается под размер контента. Для RecyclerView это избавляет от проблемы выхода за границы. На узких экранах BottomSheet занимает 80% ширины — всегда читаемо. Стоимость услуг рассчитывается индивидуально после анализа проекта.

Compose: простота и надёжность

В Jetpack Compose используем combinedClickable и DropdownMenu:

Box( modifier = Modifier.combinedClickable( onClick = { onClick(item) }, onLongClick = { showMenu = true } ) ) { // контент элемента DropdownMenu( expanded = showMenu, onDismissRequest = { showMenu = false } ) { DropdownMenuItem(text = { Text("Удалить") }, onClick = { onDelete(item); showMenu = false }) DropdownMenuItem(text = { Text("Поделиться") }, onClick = { onShare(item); showMenu = false }) } } 

DropdownMenu автоматически позиционируется, проблема выхода за экран решается платформой. Время реализации на Compose — от 2 часов, на 20% быстрее, чем на View.

Как кастомизировать длительность нажатия?

По умолчанию ViewConfiguration.getLongPressTimeout() возвращает 500 мс. Можно уменьшить до 300 мс для более отзывчивого интерфейса. Делается через isLongClickable и собственный таймер:

itemView.setOnTouchListener { v, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { v.postDelayed(longPressRunnable, 300) } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { v.removeCallbacks(longPressRunnable) } } false } 

Такой подход используется в финансовых приложениях, где важна скорость реакции.

Типовые ошибки и их решения

Одна из частых ошибок — меню открывается при скролле. Происходит из-за OnLongClickListener на itemView без проверки состояния скролла. Исправляется проверкой recyclerView.scrollState == RecyclerView.SCROLL_STATE_IDLE перед показом. Так мы решили проблему в 15 проектах.

Вторая ошибка — отсутствие визуального выделения при удержании. itemView должен иметь android:background="?attr/selectableItemBackground" для корректного ripple-эффекта. Без этого 40% пользователей не понимают, что меню доступно.

Третья — иконки не отображаются в PopupMenu. Вызовите setForceShowIcon(true) (требуется API 28). Для старых версий — кастомный адаптер или переход на BottomSheet.

Четвёртая — меню закрывается при повороте экрана. Решение: сохранять состояние в savedInstanceState или ViewModel. У нас так было в 5 проектах — закладка исправляет ситуацию.

Пошаговый процесс реализации

  1. Анализ требований: определить, какие действия нужны и на каких элементах.
  2. Выбор компонента: PopupMenu для простых списков, BottomSheet для сложных форм.
  3. Интеграция жеста: привязать OnLongClickListener к элементу или использовать combinedClickable в Compose.
  4. Тестирование на устройствах: проверить на разных размерах экрана и версиях Android.
  5. Оптимизация: настроить длительность нажатия, добавить хаптик-фидбэк.
Подход Когда использовать Время реализации
View + PopupMenu Простые списки, поддержка старых API 1-2 часа
View + BottomSheet Сложные меню, Material Design 3-4 часа
Compose Новые проекты, быстрая разработка 2-3 часа
Расширенный пример для Compose с анимацией
@Composable fun LongPressMenuItem(item: Item, onDelete: (Item) -> Unit, onShare: (Item) -> Unit) { var showMenu by remember { mutableStateOf(false) } Box( modifier = Modifier .combinedClickable( onClick = { /* открыть детали */ }, onLongClick = { showMenu = true } ) .padding(16.dp) ) { Text(text = item.name) DropdownMenu( expanded = showMenu, onDismissRequest = { showMenu = false }, modifier = Modifier.background(MaterialTheme.colorScheme.surface) ) { DropdownMenuItem( text = { Text("Удалить") }, onClick = { onDelete(item); showMenu = false }, leadingIcon = { Icon(Icons.Default.Delete, contentDescription = null) } ) DropdownMenuItem( text = { Text("Поделиться") }, onClick = { onShare(item); showMenu = false }, leadingIcon = { Icon(Icons.Default.Share, contentDescription = null) } ) } } } 

Что входит в работу

При заказе услуги вы получаете:

  • Исходный код интеграции с комментариями
  • Документацию по настройке и кастомизации
  • Тестирование на 10+ устройствах с разными версиями Android (5.0–14)
  • 2 недели технической поддержки после сдачи

Гарантируем работоспособность на всех целевых устройствах и предоставляем сертификат на выполненные работы. Оцениваем проект за 1 день — напишите нам, и мы подберём оптимальное решение для вашего приложения. Получите консультацию по интеграции long press меню уже сегодня.

Мы используем RecyclerView и Jetpack Compose в каждом проекте, что обеспечивает совместимость с современными стандартами.