Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

+666новых вакансий за сутки·52 265вакансий открыто сейчас·Новое:Talanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Новые вакансии в TelegramРаботодателям
Talanto
Вакансии
Компании5 686
РезюмеAIПодготовкаСтатьи
ТарифыТарифы
Войти

Не нашли подходящее?

Пусть нужная вакансия найдёт вас сама.

  1. Подборка в TelegramНовые вакансии по вашему фильтру — сразу в бота.Подключить
  2. Мэтчи по резюмеЗагрузите резюме — покажем, где вы подходите лучше всего.Загрузить
  3. Сохранить фильтрНастройте поиск один раз и возвращайтесь к новым вакансиям.Настроить
Что нового на TalantoTalanto осенью: поиск с цифрами, вакансия по шагам и каталог компаний

Для работодателей

Опубликуйте вакансию — отклики придут в ваш кабинет.

  1. Вакансиябесплатно
  2. Откликиконтакты бесплатно
  3. Наймэтапы и интервью

40 компаний публикуют вакансии напрямую · 4 394 кандидата открыты к предложениям

Разместить вакансию

Популярные направления

Все подборки
  • Python5 973
  • Облачные технологии4 523
  • QA вакансии1 689
  • Android1 143
  • Инженер данных788
  • Продакт-менеджер308

Разработка

  • iOS1 093
  • Backend вакансии1 020
  • Fullstack вакансии448
  • AI вакансии378
  • Frontend вакансии369
  • Mobile вакансии27

По стеку

  • Go3 345
  • Java2 037
  • Kotlin908
  • Node.js746
  • Rust492
  • .NET481
  • PHP278
  • C++213
  • C#199

DevOps и инфраструктура

  • Kubernetes2 562
  • Linux-инфраструктура2 030
  • DevOps вакансии1 855
  • Terraform / IaC1 548
  • CI/CD876
  • Мониторинг и Observability709
  • SRE229
  • Кибербезопасность40

Аналитика и данные

  • Аналитик данных479
  • Data Scientist443
  • Бизнес-аналитик342
  • Системный аналитик50

Другие направления

  • Проджект-менеджер107
  • UI/UX дизайн70
  • Ручное тестирование14

По регионам

  • Вакансии в США24 039
  • Вакансии в Европе9 373
  • Работа в России2 968
  • Вакансии в Канаде2 114

По формату

  • Удалённые вакансии41 595
  • Релокация в США24 039
  • Гибридная работа5 209
  • Офисная работа3 834

По уровню

  • Senior вакансии22 591
  • Middle вакансии12 174
  • Junior вакансии4 013

Сейчас нанимают

Все компании
  • Rakuten189 вакансий
  • ASML163 вакансии
  • Grab134 вакансии
  • Wise81 вакансия
  • Infobip28 вакансий
  • Darktrace17 вакансий
  • Spotify17 вакансий
  • Celonis14 вакансий
  • UiPath13 вакансий
  • Adyen9 вакансий

Вакансии из 344 источников: карьерные страницы компаний и джоб-борды.

КомпанииО насНовостиПубличная офертаУсловия использованияПолитика конфиденциальностиПоддержка
←К теме Jetpack ComposeВсе вопросы
Jetpack ComposeMiddleКодинг

Как обрабатывать пользовательский ввод и жесты в Compose?

В закладки
Сначала попробуйте ответить сами30 секунд
Сравнить с ответом ↓

Для кликов используют Modifier.clickable/combinedClickable, для перетаскивания — draggable или AnchoredDraggableState, для пинча/ротации — transformable, для полного контроля — pointerInput с awaitEachGesture и detectDragGestures.

Базовые кликабельные модификаторы

// clickable — добавляет ripple и семантику
Box(
    modifier = Modifier
        .clickable(onClick = { /* обработка клика */ })
        .padding(16.dp)
) {
    Text("Нажми меня")
}

// combinedClickable — клик + долгое нажатие + двойной клик
Box(
    modifier = Modifier.combinedClickable(
        onClick = { println("Одиночный клик") },
        onLongClick = { println("Долгое нажатие") },
        onDoubleClick = { println("Двойной клик") }
    )
)

Обработка ввода и жестов в Jetpack Compose

Compose предоставляет несколько уровней API для работы с вводом: от высокоуровневых компонентов (TextField, Button) до низкоуровневых детекторов жестов через Modifier.pointerInput.

TextField: текстовый ввод

var text by remember { mutableStateOf("") }

OutlinedTextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("Введите текст") },
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Email,
        imeAction = ImeAction.Done
    ),
    keyboardActions = KeyboardActions(
        onDone = { /* скрыть клавиатуру */ }
    ),
    singleLine = true
)

Высокоуровневые детекторы жестов

// draggable — только горизонталь или вертикаль
var offsetX by remember { mutableFloatStateOf(0f) }
Box(
    modifier = Modifier
        .offset { IntOffset(offsetX.roundToInt(), 0) }
        .draggable(
            orientation = Orientation.Horizontal,
            state = rememberDraggableState { delta ->
                offsetX += delta
            }
        )
)

// swipeable (устарел в Compose 1.6, используйте AnchoredDraggable)
val anchors = DraggableAnchors {
    SwipeState.Left at -200f
    SwipeState.Center at 0f
    SwipeState.Right at 200f
}
val anchoredState = remember {
    AnchoredDraggableState(
        initialValue = SwipeState.Center,
        anchors = anchors,
        positionalThreshold = { totalDistance -> totalDistance * 0.5f },
        velocityThreshold = { 100f },
        animationSpec = tween()
    )
}

transformable: масштаб, поворот, перемещение

var scale by remember { mutableFloatStateOf(1f) }
var rotation by remember { mutableFloatStateOf(0f) }
var offset by remember { mutableStateOf(Offset.Zero) }

val transformState = rememberTransformableState { zoomChange, panChange, rotationChange ->
    scale = (scale * zoomChange).coerceIn(0.5f, 3f)
    rotation += rotationChange
    offset += panChange
}

Box(
    modifier = Modifier
        .graphicsLayer(
            scaleX = scale,
            scaleY = scale,
            rotationZ = rotation,
            translationX = offset.x,
            translationY = offset.y
        )
        .transformable(state = transformState)
)

pointerInput: низкоуровневый контроль

Box(
    modifier = Modifier.pointerInput(Unit) {
        awaitEachGesture {
            // Ждём первого касания
            val down = awaitFirstDown()
            down.consume()

            // Ждём движения или отпускания
            var pointer = down
            while (true) {
                val event = awaitPointerEvent()
                val change = event.changes.firstOrNull() ?: break

                if (change.pressed) {
                    val dragAmount = change.position - change.previousPosition
                    println("Движение: $dragAmount")
                    change.consume()
                } else {
                    println("Отпущено")
                    break
                }
            }
        }
    }
)

detectTapGestures и detectDragGestures

// Готовые детекторы внутри pointerInput
Modifier.pointerInput(Unit) {
    detectTapGestures(
        onTap = { offset -> println("Tap at $offset") },
        onLongPress = { offset -> println("Long press at $offset") },
        onDoubleTap = { offset -> println("Double tap at $offset") }
    )
}

Modifier.pointerInput(Unit) {
    detectDragGestures(
        onDragStart = { offset -> println("Start: $offset") },
        onDrag = { change, dragAmount ->
            change.consume()
            println("Drag: $dragAmount")
        },
        onDragEnd = { println("End") }
    )
}

Подводные камни

  • forEachGesture устарел с Compose 1.5 — используйте awaitEachGesture, который корректно обрабатывает отмену жеста (gesture cancellation).
  • Modifier.pointerInput с ключом Unit пересоздаёт обработчик только один раз; если передать переменную как ключ, блок пересоздаётся при каждом изменении этой переменной — захватывайте нужные значения через closures.
  • Конфликт жестов: pointerInput на дочернем элементе внутри LazyColumn перехватит вертикальный свайп — нужно пробрасывать событие через PointerEventPass.Initial или использовать nestedScroll.
  • clickable добавляет Indication (ripple) по умолчанию; для пользовательских UI-элементов без ripple используйте indication = null, но тогда добавьте семантику вручную через Modifier.semantics.
  • AnchoredDraggableState требует явного указания anchors — если anchors пусты, состояние не переходит и жест игнорируется без ошибок.
  • transformable не управляет layout автоматически: изменения scale и offset нужно применять через graphicsLayer или offset modifier вручную.
  • На устройствах с gesture navigation система перехватывает свайп от краёв экрана раньше приложения — поэтому горизонтальные swipe-to-go-back жесты нужно регистрировать через WindowCompat.setDecorFitsSystemWindows.

Частые вопросы на собеседовании

  • Что такое State в Jetpack Compose и как работает state hoisting?
  • Что такое recomposition и что её вызывает в Jetpack Compose?
  • В чём разница между remember и rememberSaveable?
  • Каковы лучшие практики производительности в Jetpack Compose (стабильность, пропускаемость)?

Типичные ошибки

  • Объяснять «пользовательский ввод и жесты» только как синтаксис и не описывать поведение runtime/compiler.
  • Игнорировать важный риск: Слишком ранний переход на raw pointerInput часто ломает keyboard, TalkBack и hit testing.
  • Давать пример без edge case: отмены, null, recomposition, platform boundary или ошибки.

Что проверяет интервьюер

  • Формулирует суть темы «пользовательский ввод и жесты» своими словами и связывает ее с кодом.
  • Называет механизм: Высокоуровневые modifiers дают semantics и accessibility, pointerInput нужен для custom gesture logic и coroutine gesture scopes.
  • Видит production-последствие: Слишком ранний переход на raw pointerInput часто ломает keyboard, TalkBack и hit testing.

Как вы знаете этот вопрос?

Проверить себя по теме Jetpack Compose →

Источники

  • https://developer.android.com/develop/ui/compose/touch-input/pointer-input
←Предыдущий вопросПредставьте, экран на Jetpack Compose тормозит, теряет состояние или ведёт себя по-разному на разных платформах. Как вы будете искать причину?Следующий вопрос→Чем `LazyColumn` отличается от `Column` с модификатором прокрутки?

Связанные темы

Jetpack Composeinput-gestures-reasoningui-state