Macromedia Flash: Импорт данных

Не секрет, что Macromedia Flash отличается от прочих сред программирования своей строгой web-ориентированностью, а это, в свою очередь, налагает существенные ограничения на его возможности в целях безопасности для конечного пользователя. В этой статье мне бы хотелось описать способы, с помощью которых можно передать во Flash-объект необходимую информацию для отображения новостей, названий меню и прочего подобного применения. Думаю, это будет полезным как людям, только начинающим работу в этой среде, так и людям, изредка сталкивающимся с Flash-анимацией, для правильного представления ее возможностей и ограничений. Все примеры будут даны для Macromedia Flash MX 2004 версии продукта как самой распространенной на сегодняшний день.

Девочка делает новости...


Нет, говорить о творчестве Ларисы Грибалевой мы не будем, а поговорим о формате XML. Если вы не знакомы с ним, то рекомендую сделать это как можно скорее. Четкая структура передаваемой информации делает этот способ передачи самым удобным и рекомендуемым мною для повседневного использования. Flash-объект может прочитать информацию из XML-файла на локальном диске, из файла по заданному URL-адресу либо просто из XML- строки, которую можно передать с помощью JavaScript. Ниже приведен пример небольшого текста в XML-формате с новостями, которые мы попытаемся отобразить во Flash-объекте.

<xml>
<news>
<header>First header</header>
<text>This is first text</text>
</news>
<news>
<header>Second header</header>
<text>This is secondt text</text>
</news>
<news>
<header>Третий заголовок</header>
<text>Это текст третьего заголовка</text>
</news>
</xml>

Из текста XML видно, что каждый тег news представляет собой одну новость, состоящую из заголовка (header) и собственно текста новости (text). Сохраним данный XML-текст под именем news.xml и положим в папку к нашему будущему приложению. Теперь открываем Flash и создаем новый символ — movie clip (Ctrl+F8).

Внутри этого мувика следует создать два динамических текстовых поля (dynamic text). Первое будет заголовком новости (сделайте в нем шрифт побольше + выставьте жирный текст), а второе — текстом новости (обычный текст, антиалиасинг выключен). В поле Instance Name первого поля напишите nHeader, а в аналогичном поле второго поля — nText. Любители всяких украшательств могут положить на слой под полями градиентную заливку, обрамить новость рамочкой и увешать всяческими финтифлюшками.

В итоге мы имеем оболочку для одной новости. Логично предположить, что новости должны добавляться динамически в зависимости от того, сколько этих самых новостей (читай тегов news) содержится в XML-файле. Открываем библиотеку (F11) и, кликая правой кнопкой мышки на созданный мувик, выбираем пункт Linkage. Ставим галочку Export for ActionScript и в поле Identifier вводим название News.

Для тех, кто в первый раз сталкивается с подобным шаманством, поясняю, что после вышеописанных действий мы сможем в последующем легко клонировать мувик новостей столько раз, сколько понадобится в процессе работы. Для работы с XML-файлом во флэше используется класс XML. Все свойства и методы этого класса можно найти в файлах помощи. Я же рассмотрю только основные, необходимые нам для чтения информации:
XML.ignoreWhite — если это свойство установлено в true, то в процессе разбора узлы, содержащие только пробелы, игнорируются.
XML.nodeValue — текст указанного узла, если узел является текстовым.
XML.load(имя_файла) — загружает XML-документ с указанного адреса.
XML.onLoad — передает управление пользовательской функции в случае удачной загрузки XML-файла.
XML.childNodes — содержит массив дочерних элементов, принадлежащих данному узлу. Для того, чтобы наглядно объяснить, что такое childNodes, можно представить исходный XML-текст в виде следующего дерева:

<xml>childNodes[0]
<news> childNodes[0].childNodes[0]
<header>childNodes[0].childNodes[0].childNodes[0]
<text> childNodes[0].childNodes[0].childNodes[1]
</news>
<news> childNodes[0]. childNodes[1]
<header> childNodes[0].childNodes[1].childNodes[0]
<text> childNodes[0].childNodes[1].childNodes[1]
</news>
</xml>

Отдельно следует заметить, что сами текстовые данные (в нашем случае это текст заголовка и текст новости) являются отдельным узлом, т.е. всегда childNodes[0]. Теперь полученных знаний достаточно для написания кода, выводящего столько новостей, сколько указано в XML-файле. Кликнем на первом кадре таймлайна и, вызвав окно ввода ActionScript (F9), напишем:

// создаем объект xml класса XML;
var xml:XML = new XML();
// выставляем ему игнорирование пустых тегов с пробелами
xml.ignoreWhite = true;
// загружаем файл news.xml из текущей папки
xml.load("news.xml");
// в случае удачной загрузки вызываем функцию loadResult
xml.onLoad = loadResult;

function loadResult(){
//проходим в цикле по всем тегам news
for (i=0; i<xml.childNodes[0].childNodes.length; i++) {
//создаем копию клипа News, который находится в библиотеке
_root.attachMovie("News","news"+i,i);
//задаем координаты полученного мувика
eval("news"+i)._x = 10;
eval("news"+i)._y = 10 + i*(eval("news"+i)._height+5);
// считываем текстовое значение xml поля header
eval("news"+i).nHeader.text = xml.childNodes[0].childNodes[i].childNodes[0].childNodes[0].nodeValue;
// считываем текстовое значение xml поля text
eval("news"+i).nText.text = xml.childNodes[0].childNodes[i].childNodes[1].childNodes[0].nodeValue;
}}

Если все сделано правильно, то при добавлении элемента news в XML-файл флэшка при каждом запуске будет отображать нужное количество новостей. Для каждой новости можно добавить дату ее создания, автора и т.д. в меру фантазии создателя. Кстати говоря, сама флэшка у меня получилась размером в 560 байт, что даже для Интернета является совершенно смешным размером. В данном примере мы использовали текстовые элементы XML- документа без примера работы с атрибутами тегов, поэтому я предлагаю изменить XML-файл следующим образом:

<xml>
<news header="First header">
<text>This is first text</text>
</news>
<news header="Second header">
<text>This is secondt text</text>
</news>
<news header="Третий заголовок">
<text>Это текст третьего заголовка</text>
</news>
</xml>

Итак, мы видим, что наш заголовок новости благополучно превратился в атрибут тега news, а значит, доставать данные из него придется несколько иным способом. Для этого используем свойство XML.attributes, которое содержит массив всех атрибутов объекта заданного узла, и перепишем функцию loadResult следующим способом:

function loadResult(){
for (i=0; i<xml.childNodes[0].childNodes.length; i++){
_root.attachMovie("News","news"+i,i);
eval("news"+i)._x = 10;
eval("news"+i)._y = 10 + i*(eval("news"+i)._height+5);
// получаем значение атрибута header
eval("news"+i).nHeader.text = xml.childNodes[0].childNodes[i].attributes.header;
// считываем текстовое значение из xml поля text
eval("news"+i).nText.text = xml.childNodes[0].childNodes[i].childNodes[0].childNodes[0].nodeValue;
}}

Думаю, что, осилив общий принцип работы с XML-файлом, можно без особого труда усложнить задачу самостоятельно. Я же хочу еще описать некоторые дополнительные возможности и подводные камни. В случае, если русский текст отображается письменами индейцев Майя (а это свидетельствует о том, что ваш XML-файл сохранен не в Unicode-кодировке), следует прописать в начале скрипта следующую строчку, чтобы заставить Flash работать в той кодировке, которая стоит у вас в системе в качестве "родной":

System.useCodepage = true;

Если вы являетесь счастливым обладателем глубоких знаний в asp/php/aspx и иже с ними, то сможете легко организовать настоящую систему управления новостями. Скрипт может зачитывать новости с других порталов или из простого текстового файла и отдавать их флэшке в XML-формате банальным выводом XML-кода на экран. Для примера я приведу простейший PHP-файл, который будет описывать новости для Flash-объекта:

<?php
// здесь ваш скрипт может делать любые действия
echo '<xml><news>';
echo '<header>First header</header>';
echo '<text>This is first text</text>';
echo '</news><news>';
echo '<header>Second header</header>';
echo '<text>This is secondt text</text>';
echo '</news><news>';
echo '<header>Третий заголовок</header>';
echo '<text>Это текст третьего заголовка</text>';
echo '</news></xml>';
?>

Назовем этот файл news.php. При его запуске ничего не выводится на экран, но при просмотре кода страницы должен быть виден наш XML-код. Для того, чтобы флэшка получила результат работы скрипта, достаточно вызвать этот скрипт вместо имени XML-файла в методе load:

xml.load("http://localhost:100/news.php");

Практически всегда возникает проблема кэширования файлов, когда Flash-объект берет данные из закэшированного XML-файла, даже если вы внесли изменения в основной XML-файл. Это обычно происходит при взятии XML-файла с URL-адреса либо при нахождении самой флэшки на web-сервере. Проще говоря, если в имени XML-файла есть "http", то это наш случай. Секрет корректного обновления информации крайне прост. Для этого достаточно немного модифицировать метод load класса XML так, чтобы при его вызове каждый раз прибавлялся параметр — случайное число. Для браузера имя подгружаемых данных всегда будет уникально, и поэтому при каждом обновлении будут отдаваться последние данные.

xml.load("news.xml" + "?" + random(9999));
xml.load("http://mysite.by/news.xml" + "?" + random(9999));
xml.load("http://localhost:100/news.php" + "?" + random(9999));

Следующий аспект при загрузке данных из XML-файла — использование HTML-текста. Если вы еще не знакомы достаточно хорошо с XML-форматом, сразу должен предупредить, что обычное написание HTML-кода не возымеет должного действия. Для того чтобы передать флэшке HTML-текст, его следует обрамить следующим способом:

<![CDATA[ Ваш HTML-текст ]]>

Не следует забывать, что для корректного отображения HTML-кода во флэшке в соответствующем динамическом текстовом поле нужно поставить галочку Render text as HTML и передавать текст, используя свойство htmlText, а не text, как было показано в предыдущих примерах:

eval("news"+i).nHeader.text = xml.childNodes[0].childNodes[i].attributes.header;
eval("news"+i).nText.htmlText = xml.childNodes[0].childNodes[i].childNodes[0].childNodes[0].nodeValue;

Теперь осталось написать сам XML-файл, содержащий текст новостей с HTML-тегами:
<xml>
<news header="First header">
<text><![CDATA[This is <font color="#ff0000">first</font> text]]></text>
</news>
<news header="Second header">
<text><![CDATA[This is <b>second</b> text]]></text>
</news>
<news header="Третий заголовок">
<text><![CDATA[<b><i>Это текст третьего заголовка</b></i>]]></text>
</news>
</xml>

Ну и напоследок хочу вспомнить, что, если с файлами возиться лень, то XML-данные можно представить обычной текстовой строкой и передать во Flash из HTML-страницы с помощью JavaScript-функции setVariable(). Подробнее о ней будет сказано немножко ниже. В принципе, полученных знаний вполне достаточно для нормальной работы с динамическими данными. Хорошо усвоив этот урок, можно вполне самостоятельно пытаться создавать новостные разделы, меню и прочие вещи, которые можно менять без перекомпиляции основного Flash-объекта. Например, если в вашем меню используются определенные цвета, то гораздо удобнее сделать их переменными, значения которых будут подгружаться из XML-файла с настройками. В случае редизайна сайта это позволит легко поменять всю цветовую гамму несколькими исправлениями XML-документа даже человеку, ничего не знающему про Flash-технологии. На этом с XML-форматом я закончу и перейду к следующему.

Работа по мелочи...
Формат XML удобно использовать тогда, когда объем передаваемых данных большой. Но представим, что нужно всего лишь вывести значение одной переменной (это может быть счетчик посещений в углу экрана). Создавать ради этой мелочи XML-строку или файл и, уж тем более, писать код загрузки и парсинга этого файла совершенно неразумно. Поэтому для подобных целей используются несколько другие приемы. Первый способ — использование текстового файла с переменными особенного формата. Собственно, особенность эта заключается в том, что имя переменной и ее значение разделяются знаком =, а следующая переменная отделяется от предыдущей с помощью &. Для примера создадим в папке приложения файл counter.txt, в котором будет храниться количество посетителей сегодня и количество посетителей вчера:

myCounter=600&yesterday=1100

Вот и все. В переменной myCounter записано сегодняшнее число посетителей, а в переменной yesterday — вчерашнее. Теперь создайте новый Flash- ролик, затем — динамическое текстовое поле (достаточной ширины для отображения двух значений нашего счетчика) и задайте его Instance Name как counter. Для загрузки переменных во Flash служит функция loadVariables. О ее особенностях можно посмотреть в справке, а я пока приведу пример кода в первом кадре таймлайна с использованием этой функции:

// создаем переменную для первого значения и инициализируем ее как пустую
var myCounter:String = "";
// создаем переменную для второго значения и инициализируем ее как пустую
var yesterday:String = "";
// вызываем функцию загрузки переменных с именем нашего файла
_root.loadVariables("counter.txt");
// запускаем таймер для проверки, загрузились ли переменные
gd = setInterval(checkLoading,10);

// функция, проверяющая, загрузились ли переменные.
function checkLoading(){
// если значения переменных не пусты, то они загружены.
if ((myCounter != "")&&(yesterday != "")){
// останавливаем таймер
clearInterval(gd);
//выводим в текстовое поле обе переменные, разделенные дробной чертой.
counter.text = myCounter+"/"+yesterday;
}}

Немного поясню вышеприведенный исходный код. Поскольку ничто в этом мире не происходит мгновенно, было бы неправильным просто присвоить нашему текстовому полю значения загруженных переменных. В Интернете они просто не успеют подгрузиться. Поэтому сразу после начала загрузки (loadVariables) инициализируется таймер, который вызывает каждые 10 мсек. функцию checkLoading. Ну, а функция, в свою очередь, проверяет значения переменных, и, если они не пустые, то останавливает таймер и присваивает текстовому полю соответствующую строку. Аналогичного результата можно добиться, выводя строку нужного формата PHP-скриптом либо любым другим.

<?php
// здесь ваш скрипт может делать любые действия
echo 'myCounter=600&yesterday=1100';
?>

В таком случае вместо имени txt-файла в функции loadVariables должно быть имя соответствующего скрипта. Данный способ удобен для периодических запросов Flash-объекта на сервер для обновления статистики, обновления погоды или любых других небольших данных. Обращение к скрипту будет происходить в фоновом режиме и, что самое главное, без перезагрузки страницы. Для этого способа в первом кадре, скорее всего, придется прописать System.useCodepage=true. Для чего это делается, сказано в разделе, посвященном XML-формату. Если с файлами и скриптами работать не хочется, то можно задействовать еще один способ передачи данных переменной, а именно непосредственное изменение переменной во Flash-объекте. Сделать это можно до инициализации собственных Flash-переменных и в процессе работы Flash-объекта. Внесем небольшие изменения в код счетчика. Напишем вместо _root.loadVariables("counter.txt") следующие строчки кода:

if (filename == undefined) filename = "counter.txt";
_root.loadVariables(filename);

Мы проверяем переменную filename и, если она не определена, присваиваем ей имя "counter.txt". С другой стороны, если в этой переменной будет храниться другое имя файла, то переменные загрузятся из указанного в значении переменной места. Для начала посмотрим, как на HTML-странице объявляется Flash-документ. По умолчанию генерируется следующий код для его вставки:

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase=" http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="550" height="400" id="a-test" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="a-test.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="a-test.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="a-test" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage=" http://www.macromedia.com/go/getflashplayer" />
</object>

Половину из этого хлама можно удалить сразу, но это повод для отдельной статьи. А пока что нас будут интересовать два места в этом коде:
<param name="movie" value="a-test.swf" />
<embed src="a-test.swf"

В них указано имя Flash-ролика (у вас оно, конечно, будет другим). Для передачи значения filename нужно модифицировать эти строки следующим образом:

<param name="movie" value="a-test.swf?filename=counter2.txt" />
<embed src="a-test.swf?filename=counter2.txt "

При запуске HTML-страницы ваш ролик ничего не покажет, поскольку файла counter2.txt пока что нет, зато, создав его, можно убедиться в работоспособности кода. Изменения, которые мы делаем в теге Embed, необязательны, если вы планируете работать только с Internet Explorer. Для правильной работы Flash-ролика под остальными браузерами значение поля src в теге Embed тоже должно быть модифицировано. Основные ограничения этого способа заключаются в том, что максимальная длина строки, которую мы можем передать таким способом, составляет 256 символов, хотя для подобных размеров разумнее уже использовать XML-файл. Еще один способ передачи данных до инициализации Flash-объекта — добавление в HTML-код объявления флэшки параметра FlashVars. Модифицированный код получится следующим:

<object ... ">
...
<param name="movie" value="a-test.swf" />
<param name="FlashVars" value="filename=counter2.txt"/>
...
<embed src="a-test.swf" FlashVars=" filename=counter2.txt " ... />
</object>

Этот метод не имеет ограничения на длину строки, в отличие от предыдущего. Использование дубликата FlashVars в теге Embed также необходимо для совместимости с браузерами, отличными от IE. Для изменения значения переменной в процессе работы Flash-ролика необходимо обратиться к помощи JavaScript. Для этого создадим JavaScript-функцию setMyVar() в HTML-теге <head>, чтобы он выглядел следующим образом:

<head>
<meta http-equiv="Content-Type" content="text/html; charset=koi8-r" />
<title>My first application</title>
<script type="text/javascript">
function setMyVar(newvar)
{
document.atest.setVariable("filename",newvar);
}
</script>
</head>

В теге Object задайте параметр id="atest" (либо любое другое имя), а в теге Embed поставьте параметр name="atest". Скорее всего, они уже заданы, и ваша задача — проверить, чтобы они совпадали. Именно по этому идентификатору (atest) наш JavaScript обращается к флэшке, задавая ей переменную. Теперь поместите где-нибудь линк, вызывающий нашу функцию:

<a href="javascript:setMyVar('counter3.txt')">New var</a>

По нажатию на ссылку переменной filename присвоится значение "counter3.txt". Для проверки можно создать в самом ролике динамическое поле и указать в его свойстве Var "filename". После загрузки страницы при клике на линк в поле должен появиться соответствующий текст. Данных основ вполне должно хватить для создания Flash-приложений, способных оперировать данными из внешних источников, что сделает ваши творения более динамичными, независимыми и управляемыми. На этой ноте пока и закончим.

Паша Либер aka Fireangel, Fireangel@tut.by


Компьютерная газета. Статья была опубликована в номере 07 за 2006 год в рубрике программирование

©1997-2024 Компьютерная газета