Как поставить курсор в конец текста EditText: Kotlin для начинающих

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему курсор в EditText — это боль начинающего?

Представьте: вы пишете приложение для заметок или чата. Пользователь открывает уже существующую запись, начинает печатать — и вдруг новый текст вставляется в начало или в середину строки. Знакомая ситуация? Это одна из тех маленьких, но безумно раздражающих проблем, с которыми сталкивается каждый, кто начинает изучать Android-разработку на Kotlin.

На Stack Overflow вопрос «Place cursor at the end of text in EditText» собрал тысячи просмотров и десятки ответов. Почему? Потому что интуитивного способа «просто поставить курсор в конец» в Android SDK нет. Нужно знать несколько хитростей. Сегодня мы разберем их раз и навсегда — просто, понятно и с примерами кода.

Кстати, если вы пишете не только под Android, но и, например, бэкенд на Python — на нашем сайте PythonLib вы найдете много материалов для старта. Но сегодня мы говорим о мобильной разработке.

Классическое решение: setSelection()

Главный инструмент для управления курсором в EditText — это метод setSelection(). Он принимает индекс позиции, куда нужно переместить курсор. Но чтобы поставить курсор в самый конец, нам сначала нужно узнать длину текста.

// Простой способ: берём длину текста и передаём её в setSelection
editText.setText("Привет, мир!")
editText.setSelection(editText.text.length)

Вот и всё! editText.text.length возвращает количество символов. Передаём это число в setSelection() — и курсор оказывается после последнего символа.

Но есть подвох: когда вызывать?

Новички часто пишут этот код сразу после setText() — и удивляются, что ничего не работает. Проблема в том, что EditText может ещё не успеть обновиться. Особенно это актуально, если вы устанавливаете текст программно во фрагменте или активности.

Лучшее решение — использовать post() или слушатель onTextChanged. Вот пример с post() — это гарантирует, что код выполнится после того, как View будет готов:

editText.setText("Длинный текст, который нужно отредактировать")
editText.post {
    editText.setSelection(editText.text.length)
}

Теперь курсор точно окажется в конце. Этот метод — ваш спаситель в 90% случаев.

А если текст меняется динамически?

Допустим, вы пишете поле поиска, которое само подставляет подсказки. Или редактор, где пользователь может вставлять шаблоны. В таких ситуациях нужно ставить курсор в конец каждый раз, когда текст меняется программно.

Используйте TextWatcher или, что проще, просто вызывайте setSelection() после каждого изменения:

fun appendText(newText: String) {
    val currentText = editText.text.toString()
    editText.setText(currentText + newText)
    editText.setSelection(editText.text.length)
}

Этот код сначала добавляет новый текст к существующему, а затем перемещает курсор в конец. Работает безотказно.

Хитрость для опытных: Editable и Spannable

Если вы работаете напрямую с Editable (например, получаете его из editText.text), можно использовать SpannableStringBuilder и метод setSpan(). Но для базовой задачи «курсор в конец» это избыточно. Запомните: post { setSelection(length) } — ваш базовый приём.

Почему это не работает в диалогах и фрагментах?

Ещё один частый вопрос на Stack Overflow. Если ваш EditText находится внутри AlertDialog или BottomSheetDialogFragment, код может не сработать, потому что View ещё не прикреплён к окну.

Решение — использовать onResume() или onWindowFocusChanged(). Вот пример для диалога:

// Создаём диалог с EditText
val dialog = AlertDialog.Builder(this)
    .setView(editText)
    .show()

// Ждём, пока диалог отобразится
editText.postDelayed({
    editText.setSelection(editText.text.length)
}, 100) // 100 мс — обычно хватает

postDelayed() — это костыль, но он работает. В современных проектах лучше использовать DialogFragment с правильным жизненным циклом, но для быстрого прототипа — сойдёт.

Современная альтернатива: EditText с autofill

В Android 8+ появилась система Autofill, которая может сбрасывать позицию курсора. Если ваше приложение поддерживает автозаполнение, убедитесь, что вы вызываете setSelection() после того, как автозаполнение завершилось. Иначе пользователь выберет пароль из менеджера — и курсор улетит в начало.

Для этого можно подписаться на OnReceiveContentListener (API 31+) или использовать TextWatcher с проверкой:

editText.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        // Если текст изменился не пользователем — ставим курсор в конец
        if (!isUserTyping) {
            s?.let { editText.setSelection(it.length) }
        }
    }
    // ... остальные методы
})

Этот подход позволяет отличить программное изменение текста от ручного ввода. Удобно, но требует аккуратности.

Ошибки, которые совершают все новички

Давайте пробежимся по типичным граблям:

  • Забывают про post() — и код выполняется до отрисовки View. Результат: курсор остаётся на месте.
  • Используют editText.text.length до setText() — получают длину старого текста и ставят курсор не туда.
  • Путают setSelection() с selectAll() — первый ставит курсор, второй выделяет весь текст.
  • Пытаются поставить курсор в конец null текста — если текст пустой, length равен 0, и курсор встанет в начало. Это нормально, но не всегда ожидаемо.

Запомните эти четыре пункта — и вы сэкономите часы дебага.

Готовый шаблон для реального проекта

Чтобы вы не копировали код по кусочкам, вот полноценная функция-расширение для EditText на Kotlin. Добавьте её в свой проект — и пользуйтесь:

fun EditText.moveCursorToEnd() {
    post {
        // Убеждаемся, что текст не null
        val textLength = text?.length ?: 0
        setSelection(textLength)
    }
}

// Использование:
editText.setText("Пример текста")
editText.move                

Блоги

Рекомендаций по книгам