понедельник, сентября 22, 2008

Ввод текста не в TextField: реально ли такое? AS3

Столкнулся со следующей проблемой:
Необходимо в некотором текстовом поле отобразить нажатый на клавиатуре символ.
Первая мысль - просто слушаем событие клавиатуры KeyboardEvent от, к примеру, Stage. Однако, событие имеет только два свойства, предоставляющих информацию о нажатом символе (не считая свойств, информирующих о вспомогательных клавишах): это charCode и keyCode. Из них, так или иначе, можно узнать, какая буква была нажата но - без учета текущего языка ввода! Как получить нужный символ с учетом текущей языковой версии ввода?
Идей решения этой проблемы я практически не нашел. Только здесь предлагается использовать невидимое текстовое поле с установленным на нем фокусе. Аналогичное решение предлагается здесь.
Порывшись в хелпе, в исходниках Flash/Flex-компонентов осуществляющих ввод текста, выяснилось, что всё сводится именно к TextField. Но в сам TextField, естественно, влезть невозможно.
Пока вижу единственный вариант - использовать TextField как источник введенного символа.
Хелп по этому поводу говорит: "Because mappings between keys and specific characters vary by device and operating system, use the TextEvent event type for processing character input." Но никто кроме TextField не умеет слушать событие TextEvent. То есть имеется подтверждение тому, что без текстового поля не обойтись.

Итак. Алгоритм таков (работает отлично):
Создаем TextField и куда-нибудь его вставляем. Делаем его невидимым - устанавливаем его свойство visible в false. Удивительно, но невидимые текстовые поля всё равно получают фокус! Не забываем установить type = TextFieldType.INPUT;
Подписываемся на прослушивание его события TextEvent.TEXT_INPUT. В обработчике этого события извлекаем нужный нам символ: event.text.charAt(0); и используем его как нам заблагорассудится. И не забываем при этом очистить поле.
Кроме того, подписываемся к this.someDisplayObject.stage на прослушивание события : KeyboardEvent.KEY_DOWN. В обработчике этого события мы устанавливаем фокус на наш невидимый TextField.

Решение, безусловно, выглядит как "заплатка". За то работает.
Если предполагаются другие интерактивные элементы, то нужно будет продумать систему отключения и включения нашей системы в зависимости от текущего фокуса ввода.

7 комментариев:

Анонимный комментирует...

На самом деле event.text.charAt(0); не достаточно, т.к. в поле можно разом вставить несколько символов

Racer комментирует...

Верно! Но в моем случае надо вводить только один символ.
Если же необходимо обработать всю строку из буфера - надо просто циклически пройтись по всем символам и сделать с ними нужные действия.

Анонимный комментирует...

Я так и не понял как установить фокус на текстовое поле.

Пишу:

stage.focus=poletext;
poletext._focusrect;

Ни тот ни другой вариант не работает :(.

Racer комментирует...

Если взять фрагменты моего кода (не стал адаптировать, выписал основные моменты):

В конструкторе управляющего класса:

this.__hiddenTextField = new TextField();
this.__hiddenTextField.visible = false;
this.__hiddenTextField.type = TextFieldType.INPUT;

. . .

После добавления _renderer в Stage:

this.__dispatcher = this._renderer.stage;
this.__dispatcher.addEventListener(KeyboardEvent.KEY_DOWN, this.dispatcher_keyDownHandler);
this._renderer.addChild(this.__hiddenTextField);
this.__hiddenTextField.addEventListener(TextEvent.TEXT_INPUT, this.hiddenTextField_changeHandler);

. . .

В обработчике dispatcher_keyDownHandler:

this.__dispatcher.focus = this.__hiddenTextField;
switch (event.keyCode) {case Keyboard.BACKSPACE: {
.... обработка действий других клавиш

. . .

В обработчике hiddenTextField_changeHandler:

var letter:String = event.text.charAt(0);
this.__hiddenTextField.text = "";
if (letter.match(/[a-zа-яА-ЯёЁ]/gi).length > 0) {
this.dispatchEvent( . . Что-то отсылается другим классам . . ));
}

Анонимный комментирует...

Спасибо. Теперь все доходчиво ясно...

Анонимный комментирует...

В одном проекте использовал такую идею для создания сложного текстового редактора. Радость от такого подхода не только в том, что раскладка клавиатуры работает, но еще и ctrl+v отрабатывается нормально. Получается более или менее полноценная поддержка настоящего клипборда (копирование в буффер, естественно, приходится в ручную делать)

Racer комментирует...

ЗдОрово!
Кстати, насчет текстового редактора. Может подскажешь идею, как сделать (наверняка должен был столкнуться с такой ситуацией) разбиение большого объема текста на страницы, с учетом возможности редактирования размера шрифта, выравнивания строк по ширине страницы и т.п.?
Спасибо.