Удобная вставка текста в текстовые поля кнопки

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

Лирическое вступление

Для формирования сложного GUI (например игрового) я почти всегда использую следующий способ: GUI формируется в fla файле (каждое окно — отдельный класс), всем кнопкам, текстовым полям и тп прописываются instance name, а затем окно или несколько экспортируется в swc, которые подключаются к проекту (например FlashDevelop), пишутся классы наследованные от классов втянутых из swc в которых я уже вношу текст на кнопки, привязываю обработчики событий, и вообще из шаблона делаю собственно GUI. Делая так я имею возможность импортировать макеты окон прямо из PSD файлов и быстро подключать их к проекту. Уверен что этот прием известен большинству flash-разработчиков, потому продолжим (если есть необходимость в статье об этом — скажите).

Первая проблема

В процессе написания класса окна унаследованного от класса из swc часто возникает необходимость просто и быстро вписать текст в текстовое поле, являющее дочерним объектом не для самого окна, а например обернутое в один или несколько MovieClip-ов. Обычно такая ситуация решается одним из трех способов:

  1. разработчик возвращается во fla файл и убирает ненужные обертки (часто этот способ самый правильный, хоть не всегда возможный)
  2. разработчик использует методы getChildAt, getChildByName
    В результате присвоение текста выглядит примерно так
    (((myWindow.getChildByName('myChildMovie') as Sprite).
                    getChildAt(0) as Sprite).
                                    getChildByName('myTextfield') as Textfield).text='текст';
    или и вовсе
    myWindow.getChildByName('myChildMovie').getChildAt(0).
                                            getChildByName('myTextfield').text='текст';
    Это наиболее некрасивый и глючный способ.
  3. разработчик для доступа к вложенному текстовому полю возвращается во fla и присваивает Class Name и Instance Name каждому вложенному MovieClip находящемуся в иерархии сцены между окном и текстовым полем а также Instance Name самому текстовому полю. В результате присвоение текста выглядит так
    myWindow.myChildMovie.mySubChildMovie.myTextfield.text='текст';

    Этот способ довольно громоздкий но в целом не плохой


Вторая проблема

Допустим в классе окна есть несколько дочерних кнопок, у которых в каждом состоянии есть динамическое текстовое поле, нам нужно каждой кнопке присвоить текст таким образом, чтобы он прописался во все текстовые поля состояний кнопки.
Обычно это делается примерно так:
myButton.upState.getChildByName('myTextField').text="текст на кнопке";
myButton.overState.getChildAt(0).text="текст на кнопке";
myButton.downState.getChildByName('myTextField').text="текст на кнопке";

что не красиво и не эффективно

Решение

Представленная функция берет переданный ей DisplayObject и пробегает рекурсивно все его дочерние DisplayObject и каждому найденному текстовому полю присваивает переданный ей текст.
параметр _mc: DisplayObject, это может быть кнопка с текстовыми полями, MovieClip с глубоко внутри запрятанным текстовым полем или даже само текстовое поле.
параметр _text: String, строка которую нужно присвоить текстовым полям.

public static function setTextToAllTextfields(_mc : DisplayObject,_text : String) : void
{
        if (_mc is TextField)
        {
                var tf : TextField = _mc as TextField;
                var format : TextFormat;
                if (tf.text.length>0)
                {
                        format = tf.getTextFormat(0, tf.text.length);
                } else 
                {
                        format = tf.getTextFormat();
                }
                tf.text = _text;
                // Это сделано для того чтобы после смены текста не исчезал TextFormat 
                //созданный через настройки текстового поля во Flash
                tf.setTextFormat(format);
        }

        if (_mc is DisplayObjectContainer)
        {
                var mc : DisplayObjectContainer = _mc as DisplayObjectContainer;

                for (var x : int = 0; x < mc.numChildren; ++x)
                {
                        var child : DisplayObject = mc.getChildAt(x);
                        setTextToAllTextfields(child, _text);
                }
        }

        if (_mc is SimpleButton)
        {
                var btn : SimpleButton = _mc as SimpleButton;
                setTextToAllTextfields(btn.upState, _text);
                setTextToAllTextfields(btn.overState, _text);
                setTextToAllTextfields(btn.downState, _text);
        }
}


Пример использования:

setTextToAllTextfields(myMovieClip,'Текст для всех вложеных текстовых полей');
setTextToAllTextfields(myButton,'Текст для всех состояний кнопки');
setTextToAllTextfields(myTextFileld,'Текст для текстового поля');


Кроме того, если во Flash динамическому текстовому полю был присвоен параметр «жирный» то простое присвоение текста через поле .text эту жирность уберет. Используя эту функцию этой проблемы удасться избежать, то есть имеет смысл использовать ее и для присваивания текста в текстовое поле.

Заключение

Удобно? Я считаю да. Но за все приходится платить и платим мы быстродействием. Потому не стоит применять эту функцию в узких местах программы. Однако открытие окна обычно узким местом не является и потому относительно медленное выполнение вставки текста не приведет к сколько-нибудь заметному подтормаживанию.

Согласно пункту 6 правил даю ссылку на эту статью на моем блоге.

Спасибо за внимание!
  • +9

Комментарии (22)

0
Спасибо за статью!
Интересный подход. Глубокая иерархия вложенных мувиклипов — это, конечно, не слишком хорошо, но метод решения ты придумал элегантный, и рекурсия смотрится здесь приятно.
P.S. Очень клёвая аватарка :)
  • SeeD
  • SeeD
0
Спасибо :)
0
Привет дружище:)
0
Привет, Костя. Жду твою Cut The Belka ;)
0
Мы тут на конкурсе застряли www.fgl.com/view_game.php?game_id=25580, белка сразу после :)
+2
Еще заметочка: вместо obj.getChildByName('childName') можно писать просто obj['childName']
  • nukie
  • nukie
0
Точно, забыл упомянуть, спасибо. Это все тот же второй вариант но компактнее записанный
0
Полезная статья. Беру на заметку :) Как раз сейчас борюсь с исчезанием жирного шрифта из текстовых полей после присвоения через AS3.
0
Отличная статья. Спасибо.
Вопрос: а про организацию кода целой игры, ни кто не писал?
0
неплохой метод
только с потерей «жирности» можно бороться и передавая текст отформатированным, например так
textField.text = "<b>Тут жирный текст</b>"

также и цвет шрифта выставить можно
textField.text = "<font color='#FF0000'/><b>Тут жирный красный текст</b>"

не мешало бы конечно сделать сам метод более «умным» — передавать имя текстового поля, у меня например в мувике может быть несколько текстовых полей =)
0
Есть несколько замечаний в ответ
1.
textField.text = "<b>Тут жирный текст</b>"

Приведет к тому что в текстовом поле будут видны теги в виде текста, а чтобы был отрендерен html его надо присваивать не в свойство text а в свойство htmlText. Так же можно задавать в коде формат не через htmlText, а создавая новый TextFormat.

2. Всегда выставлять в коде формат руками для полей которым этот формат присвоен в html не рационально, тк вместо того чтобы сделать всю полировку окна во флеше а в коде только вставлять значения, мы начнем еще в коде заморачиваться видом текстовых полей. Это, к тому же, приведет к проблемам если нам понадобиться поменять макет окна. Тогда нам кроме втягивание нового вида окна во флеш надо будет не забыть и поменять формат текста в коде.

3. Можно действительно передавать имя текстового поля, но я предпочитаю в случае если мувик содержит не одно поле а несколько, все же поступать как описано в «пункте 3 первой проблемы». В этом случае на моей стороне мощь средств IDE — автоподстановка и контроль за динамическими свойствами (в FDT). А когда все кнопки и поля доступны автоподстановкой через точку, тогда запросто можно юзать эту фенкцию для одного единственного мувика или кнопки. Получается что имя текстового поля я просто выношу из скобок и делаю более багоустойчивым.
0
1. Да, конечно
textField.htmlText = "<b>Тут жирный текст</b>"
опечатка
2. Так то оно так, но если у текстфилда одно слово выделено цветом к примеру, твой метод не пройдет — он ведь считает что весь текст в одном формате
3. Мне одному кажется подобное извратом myWindow.myChildMovie.mySubChildMovie.myTextfield.text='текст'?
а ведь можно было бы всего лишь передать имя текстфилда доп. параметром, вроде такого:
setTextToAllTextfields(myMovieClip, 'myTextfield','Текст');
0
2. Ну это естественно, я же не предлагаю эту функцию использовать на все случаи жизни, есть вещи для которых она не предназначена
3. Ну тут на вкус и цвет. Я предпочитаю чтобы инстанснеймы строками не передавались, а контролировались компилятором, иначе опечатка = ошибка времени исполнения, а в моем случае это ошибка компиляции что куда удобней.
0
3. Однако если графика не будет embedded, то ты уже не сможешь так их контролировать )
Согласен, на вкус и цвет все фломастеры разные =)
0
Ну в начале статьи я же уточнил что использую такой метод именно для эмбеда )
0
Я всегда подписываю все дочерние инстансы и обращаюсь по прямому пути к ним. Стоит только заметить что вложенность у меня всегда маленькая, максимум 3-4 мувика. Да и на мой взгляд обращаться к ним непосредственно — не позволяет забыть стректуру игры если придётся возвращаться к арту (а у меня такое постоянно).

Ну и ещё — в любом случае используются все элементы такой вот иерархии. Каждый мувиклип. А если гдето в глубине один нужный — нужно перестраивать саму иерархию.

Надеюсь понятно написал)
0
полностью согласен
0
«пишутся классы наследованные от классов втянутых из swc» — а зачем ты наследуешь классы от мувиков во fla?
достаточно использовать их в качестве графического представления и проблем с наследованием и расширяемостью не будет, по типу:
public class BaseMenu extends Sprite {
                /**
                 * контейнер для меню
                 */
                protected var container : MovieClip;

                public function BaseMenu( containerClass : Class ) {
                        this.container = new containerClass();
                        addChild( container );
                }
}

ИМХО так намного удобнее
0
Делая так ты отказываешься от помощи IDE. MovieClip не содержит в себе чайлдов доступных через точку автоподстановкой. А вот если написал
var asset:MyWindow= new MyWindow();
asset.

потом встал после точки, контрол пробел и выбирай чайлды, myButton, myTextField и все другие у кого есть инстанс неймы. Не знал об этом?
0
теперь ясно, на мой взгляд не гибкое решение, но имеет право на жизнь )
0
Кроме того если во флеше ты чтото изменил, выкинул поле например, то в твоем случае ты получишь опять же ошибку времени выполнения. А в моем — ошибку компиляции, которую устранить намного легче и приятнее. Твои ошибки времени выполнения нуужно будет ловить по одной, а мои будут видны все сразу. И еще. нажал контрол клик на таком члене и видишь полный список таких чшенов по именам) Помоему куда как более удобное решение чем твое.
0
тут как посмотреть:
1. это работает только с эмбед, захочешь грузить графику и такой код не заработает
2. таким образом все менюшки у тебя пишутся с нуля, я же могу сделать базовую менюшку в которой будут реализованы все базовые методы
3. наследовать класс от мувиклипа только для того чтобы можно было чуть удобнее добираться до элементов его представления — по мне так неправильно. Ошибку времени выполнения можно получить всегда ) тестировать нужно когда что либо выкидываешь ))

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