Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   Статьи (http://www.flasher.ru/forum/forumdisplay.php?f=101)
-   -   ActionScript 3 Tips and Tricks (http://www.flasher.ru/forum/showthread.php?t=85645)

MerlinTwi 16.10.2006 12:51

40. Событие Render
 
В AS3 добавлено новое событие Event.RENDER (flash.events.Event.RENDER), которое рассылается после enterFrame, но перед тем, как экран флеш-плеера обновится. Это событие не генерится автоматически, его нужно активировать самостоятельно, вызвав метод stage.invalidate(); (flash.display.Stage.invalidate()), после чего, перед обновлением экрана все объекты приаттаченные на stage (и только они) получат событие Event.RENDER. Если флэш плеер не занимается рендерингом (например окно минимизировано в таск бар), то Event.RENDER не рассылается.

Пример:
Код:

var sprite:Sprite = new Sprite();
stage.addChild(sprite);

sprite.addEventListener(Event.ENTER_FRAME, enterFrame);
sprite.addEventListener(Event.RENDER, render);
stage.addEventListener(MouseEvent.CLICK, click);

function enterFrame(event:Event):void {
    trace("enter frame");
}
function render(event:Event):void {
    trace("render");
}
function click(event:MouseEvent):void {
    trace("click");
    stage.invalidate();
}

Output:
Код:

enter frame
enter frame
enter frame
click
enter frame
render
enter frame
enter frame
enter frame
enter frame
...


MerlinTwi 16.10.2006 12:52

47. Картинки из библиотеки (library)
 
Еще во Flash 8 была добавлена возможность загрузить картинку из библиотеки (library) используя linkage ID и метод BitmapData.loadBitmap(). В ActionScript 3 это делается несколько иначе, т.к. все мувиклипы, фреймы и объекты в библиотеке ассоциированы с классами. Классы для картинок в библиотеке являются потомками BitmapData ( flash.display.BitmapData) и перед добавлением картинки к какому-либо визуальному объекту нужно ее связать с классом Bitmap (flash.display.Bitmap), поскольку непосредственно BitmapData приаттачить нельзя.
В качестве примера, добавьте в библиотеку картинку, в диалоге linkage поставьте галочку «Export for ActionScript» и укажите название класса «RomeImage» (этот класс будет сгенерирован автоматически при публикации SWF). Далее нужно написать код для добавления этой картинки на экран:
Код:

// создаем класс RomeImage с содержимым картинки
var romeImageData:RomeImage = new RomeImage();

// Создаем Bitmap на основе нашей картинки
var romeImageBitmap:Bitmap = new Bitmap(romeImageData);

// и добавляем этот Bitmap на экран
addChild(romeImageBitmap);


MerlinTwi 16.10.2006 13:08

48. Изменения в typeof
 
Оператор typeof позволяет узнать базовый тип любого объекта/переменной. Это не информация о классе, а просто тип переменной связанной с объектом. Для детальной информации о классе следует использовать instanceof, getQualifiedClassName, describeType.
В ActionScript 1 и 2, typeof возвращал такие значения:
  • boolean
  • function
  • movieclip
  • null
  • number
  • object
  • string
  • undefined
ВActionScript 3 typeof возвращает:
  • boolean
  • function
  • number
  • object
  • string
  • xml
  • undefined
Убрали MovieClip и null, теперь это тоже самое, что object. Добавили xml. Новые типы переменных int и uint распознаются как number. И простейшие типы переменных boolean, number, string, созданные через конструктор распознаются правильно, а не как object, как было в AS1/AS2.
Код:

// AS1 & AS2
trace(typeof new XML()); // object

trace(typeof my_mc); // movieclip

trace(typeof null); // null

trace(typeof true); // boolean
trace(typeof 1); // number
trace(typeof ""); // string

trace(typeof new Boolean()); // object
trace(typeof new Number()); // object
trace(typeof new String()); // object

Код:

// AS3
trace(typeof new XML()); // xml

trace(typeof my_mc); // object

trace(typeof null); // object

trace(typeof true); // boolean
trace(typeof 1); // number
trace(typeof ""); // string

trace(typeof new Boolean()); // boolean
trace(typeof new Number()); // number
trace(typeof new String()); // string

trace(typeof int(1)); // number
trace(typeof uint(1)); // number


MerlinTwi 17.10.2006 12:06

49. getBounds() и getRect()
 
Как и в ActionScript 1 и 2, в ActionScript 3 есть метод getBounds() (flash.display.DisplayObject.getBounds()), который вычисляет границу мувиклипа в системе координат любого объекта на timeline. Но в ActionScript 3 метод getBounds возвращает Rectangle (flash.geom.Rectangle) вместо Object со свойствами xMin, xMax, yMin, yMax, как это было раньше.
В ActionScript 3 еще добавлен новый метод getRect() (flash.display.DisplayObject.getRect()), который выполняет аналогичную задачу, но в отличие от getBounds не учитывает толщину линий (strokes on shapes).
В качестве наглядного примера, красный прямоугольник высчитывается по getBounds, а синий по getRect:
Код:

var sprite:Sprite = new Sprite();
sprite.graphics.beginFill(0x999999);
sprite.graphics.lineStyle(10, 0x333);
sprite.graphics.drawCircle(100, 100, 50);
sprite.graphics.endFill();
addChild(sprite);

addChild(createRectShape(sprite.getRect(this), 0x0000FF));
addChild(createRectShape(sprite.getBounds(this), 0xFF0000));

function createRectShape(rect:Rectangle, color:uint):Shape {
    var rectShape:Shape = new Shape();
    rectShape.graphics.lineStyle(0, color);
    rectShape.graphics.drawRect(rect.left, rect.top, rect.width, rect.height);
    return rectShape;
}


MerlinTwi 17.10.2006 12:19

50. Разница между for..in и for each..in
 
В ActionScript 3 добавлен новый вид цикла for each..in который работает аналогично прежнему for..in, но перебираются значения объекта, а не ключи. Пример:
Код:

var object:Object = new Object();
object.name = "senocular";
object.id = 2867;
object.isModerator = true;
for each (var value:* in object){
    trace(value);
}
/* Output:
true
2867
senocular
*/

// А прежний for..in:
for (var key:String in object){
    trace(key + ": " + object[key]); // object[key] is value
}
/* Output:
isModerator: true
id: 2867
name: senocular
*/

Учтите, в цикле for each..in нельзя получить значение ключа.
Так же обратите внимание, что ActionScript 3 в циклах (for..in и for each..in) сохраняет порядок элементов в массиве, а ActionScript 1 и 2 перебирал в порядке обратном добавлению элементов. См. пример:
Код:

var array:Array = new Array();
array[1] = 1;
array[0] = 2;
array[2] = 3;
for (var key:String in array){
    trace("array[" + key + "] = "+ array[key]);
}

Output AS 1 & AS 2:
Код:

array[2] = 3
array[0] = 2
array[1] = 1

Output AS 3:
Код:

array[0] = 2
array[1] = 1
array[2] = 3


MerlinTwi 17.10.2006 12:25

51. Значения по умолчанию для параметров функции
 
ActionSript 3 дает возможность присвоить значения по умолчанию для параметров функций. Такие параметры становятся необязательными при вызове функции.
Код:

function method(required:String, optional:String = "default"):void {
    trace(required +" "+optional);
}
method("Hello"); // "Hello default"

Необязательные параметры со значениями по умолчанию должны стоять после обязательных параметров.
Код:

// Так неправильно:
function method(required:String = "default", optional:String):void { ... }
// [Compiler] Error #1138: Required parameters are not permitted after optional parameters.


MerlinTwi 18.10.2006 12:18

52. Неопределенное количество аргументов
 
Для того, чтобы в ActionSript 3 функция могла принимать любое количество аргументов необходимо использовать специальный параметр три точки
«... название_переменной» (Keyword: ...(rest)), который всегда располагается последним, после перечисления обязательных аргументов. Переданные аргументы записываются в указанную переменную в виде массива:
Код:

usingRest(1, 2, 3, 4);
function usingRest(required:Number, ... optionalArgs):void {
    trace(required); // 1
    trace(optionalArgs); // [2, 3, 4]
}


MerlinTwi 18.10.2006 12:25

53. arguments
 
В ActionScript 3, как и в ActionScript 1 и 2, любая функция содержит arguments (Top level arguments), куда в виде массива записываются все переданные аргументы. Но в ActionScript 3 убрали свойство arguments.caller, для получения ссылки на вызвавшую функцию советуют передавать эту ссылку самостоятельно в виде аргумента функции.
Код:

args("a", 1);
function args(str:String, num:Number):void {
    trace(arguments); // ["a", 1]
}


MerlinTwi 18.10.2006 13:06

54. Поддержка пространства имен (Namespaces)
 
ActionScript 3 поддерживает пространство имен (namespaces) в классах по аналогии с namespaces в XML. Пространство имен можно сравнить с пакетами (packages), поскольку пакеты позволяют создать несколько различных классов с одинаковым названием, но описанных в разных пакетах, так и пространство имен дает возможность определить несколько разных методов и свойств с одним названием внутри одного класса.
Для использования пространства имен необходимо этому пространству присвоить уникальное название с помощью ключевого слова namespace (namespace Keyword). После объявления пространства имен его название можно использовать аналогично public, private…
Пример:
Код:

package {
   
    public class UsingNameSpaces {
       
        public namespace company;
        public namespace individual;
       
        company var value:int = 10;
        individual var value:int = 2;
       
        public function UsingNameSpaces(){
        }
       
        company function showValue() {
        }
       
        individual function showValue() {
        }
    }
}

Здесь было объявлено два пространства имен «company» и «individual», которые использовались для разделения свойств «value» и методов «showValue», которые хоть они и имеют одинаковые названия, конфликта не возникает, поскольку расположены в разных пространствах имен.
При объявлении пространства имен можно дополнительно указать URI.
Код:

package {
   
    public class UsingNameSpaces {
       
        public namespace company = "http://www.example.com/company";
        public namespace individual = "http://www.example.com/individual";
       
        company var value:int = 10;
        individual var value:int = 2;
       
        public function UsingNameSpaces(){
        }
       
        company function showValue() {
        }
       
        individual function showValue() {
        }
    }
}


MerlinTwi 18.10.2006 13:18

55. Оператор (::) для уточнения пространства имен
 
При вызове метода класса, или обращении к свойству, описанному внутри пространства имен, необходимо уточнить из какого именно пространства имен нужно вызвать метод. Один из способов сделать это - использовать оператор :: (name qualifier operator). Указываете название пространства имен, два двоеточия и название метода (namespace::member).
Код:

package {
   
    public class UsingNameSpaces {
       
        public namespace company = "http://www.example.com/company";
        public namespace individual = "http://www.example.com/individual";
       
        company var value:int = 10;
        individual var value:int = 2;
       
        public function UsingNameSpaces(){
            company::showValue(); // traces 10
            individual::showValue(); // traces 2
        }
       
        company function showValue() {
            trace(company::value);
        }
       
        individual function showValue() {
            trace(individual::value);
        }
    }
}

Несмотря на то, что метод showValue расположен внутри такого же пространства имен, что и выводимое свойство value, необходимо уточнять пространство имен.

MerlinTwi 19.10.2006 13:01

56. Динамичность класса не наследуется
 
Как и в ActionScript 2 в ActionScript 3 есть возможность создавать динамические классы, для этого используется ключевое слово dynamic (dynamic keyword). В динамические классы можно добавлять новые свойства в процессе выполнения кода.
В AS2, когда создаете новый класс, наследованный от динамического класса, он по умолчанию так же становился динамическим.
Код:

// superclass.as
dynamic class superclass {
}

// subclass.as
class subclass extends superclass {
}

// main movie
var instance:subclass = new subclass();
trace(instance.anything); // Ошибки не будет, т.к. subclass унаследовал динамичность

В AS3 это не так, любой класс не является динамическим, если это явно не указано ключевым словом dynamic.
Код:

// superclass.as
package {
    dynamic class superclass {
    }
}

// subclass.as
package {
    class subclass extends superclass {
    }
}

// main movie
var instance:subclass = new subclass();
trace(instance.anything); // Ошибка, свойство не определено, т.к. класс не динамический


MerlinTwi 19.10.2006 13:23

57. Создание события mouseWithin (мышка внутри)
 
В Director Lingo есть удобное событие mouseWithin, которое генерится каждый фрейм, если мышь находится в пределах мувиклипа. Это событие довольно просто реализовать на ActionScript 3. (Конечно можно и на ActionScript 1 или 2, но придется заюзать методы onRollOver и onEnterFrame, а они могут понадобиться и для других целей. В AS3 такой проблемы нет, т.к. вы можете на одно событие навесить сколько угодно слушателей.)
Итак, для реализации события mouseWithin нужно, по событию входа мышки на мувиклип подключиться к событию enterFrame и рассылать каждый фрейм mouseWithin, пока мышь не покинет пределы мувиклипа.
Код:

// Реализация события mouseWithin
private function addMouseWithin(event:MouseEvent):void {
    addEventListener(Event.ENTER_FRAME, mouseWithin);
}
private function removeMouseWithin(event:MouseEvent):void {
    removeEventListener(Event.ENTER_FRAME, mouseWithin);
}
private function mouseWithin(event:Event):void {
    dispatchEvent(new MouseEvent("mouseWithin"));
}

// В конструкторе
public function MySpriteClass() {
    addEventListener(MouseEvent.MOUSE_OVER, addMouseWithin);
    addEventListener(MouseEvent.MOUSE_OUT, removeMouseWithin);
}

Для обработки такого события просто добавьте слушателя:
Код:

addEventListener("mouseWithin", mouseWithinHandler);

MerlinTwi 19.10.2006 13:38

58. Ключевое слово final
 
Ключевое слово final (Toplevel final keyword) можно использовать для предотвращения переопределения метода класса или запрещения наследования от класса.
Если метод помечен как final, классы потомки не смогут переопределить его (override).
Код:

final public function methodName() { … }
Если пометить весь класс как final, то от него нельзя будет создать потомков. Например:
Код:

// superclass.as
package {
    final public class finalclass {
    }
}

// subclass.as
package {
    class subclass extends finalclass {
    }
}

Компилятор выдаст ошибку: Error #1016: Base class is final.
Примечание: нет смысла делать final методы в final классе, т.к. от этого класса все равно не может быть наследников, а значит, и нет возможности переопределить методы.

MerlinTwi 20.10.2006 11:51

59. MXMLC: SWF Metadata Tag
 
Если вы используете MXMLC (компилятор из Flex 2 SDK) для компиляции ActionScript 3 SWFs, вы можете использовать SWF metadata tag для назначения свойств флешки. Всего можно установить 4 свойства:
  • width
  • height
  • frameRate
  • backgroundColor
Пример:
Код:

package {
    [SWF(width="500", height="450", frameRate="24", backgroundColor="#FFFFFF")]
    public class MyApp extends Sprite {

    }
}

После компиляции будет создан SWF размером 500x450 с белым фоном и частотой кадров 24.

MerlinTwi 20.10.2006 12:35

60. Класс Proxy
 
В ActionScript 3 добавлен новый класс Proxy (flash.utils.Proxy) для замены методов addProperty и __resolve из ActionScript 1 и 2.
  • addProperty(prop:String, getFunc:Function, setFunc:Function) : Boolean
    использовался для динамического добавления getter/setter свойств в объект или класс. Параметры:
    prop - имя создаваемого свойства объекта.
    getFunc - функция, которая вызывается для возвращения значения свойства;
    setFunc - функция, которая вызывается для установки значения свойства;
  • __resolve - ссылка на определяемую пользователем функцию, которая вызывается, если ActionScript код ссылается на неопределенное свойство или метод.
В ActionScript 3 вы можете создать класс наследник от класса Proxy, который предоставляет следующие возможности:
  • Перехватить чтение свойства
  • Перехватить запись свойства
  • Перехватить проверку свойства (на наличие)
  • Перехватить удаление свойства
  • Перехватить вызов метода
  • Перехватить установку атрибутов
  • Использовать [] для доступа к свойствам
Хоть возможности Proxy и превосходят то, что было доступно в AS1 и AS2, здесь есть и свои недостатки. Вы обязаны наследоваться от класса Proxy, нельзя унаследоваться от произвольного класса, например от Display objects, и получить возможности класса Proxy.
Proxy классы обычно используются для создания набора переменных с гибкими возможностями (например, Flex классы ArrayCollection, ListCollectionView, XMLListCollection).

Proxy класс более детально будет рассмотрен в следующих советах

MerlinTwi 20.10.2006 12:44

61. Оператор «in»
 
В ActionScript 3 добавлен новый оператор in (in operator), который используется для проверки наличия свойства в объекте. Это аналогично hasOwnProperty, но работает и для унаследованных свойств.
Код:

trace("PI" in Math);        // true
trace("myProperty" in Math); // false

Так же оператор in можно использовать для проверки правильности номера индекса в массиве:
Код:

var myArray:Array = ["zero", "one", "two"];
trace(0 in myArray); // true
trace(1 in myArray); // true
trace(2 in myArray); // true
trace(3 in myArray); // false


MerlinTwi 23.10.2006 13:18

62. Методы getProperty и setProperty класса Proxy
 
Методы getProperty и setProperty используются для управления необъявленными свойствами класса. Если какое-то свойство явно не объявлено в классе, то при попытке прочитать его значение, будет вызван метод getProperty. Аналогично при записи вызывается метод setProperty.
Как и все методы класса Proxy, getProperty и setProperty объявлены в пространстве имен «flash_proxy» (flash.utils.flash_proxy), чтобы не возникало конфликтов с публичными методами. Для переопределения этих методов в своем классе обязательно нужно указывать пространство имен flash_proxy.
Пример: класс CustomObject наследуется от Proxy и переопределяет методы getProperty и setProperty для управления динамическими свойствами.
Код:

package {
   
    import flash.utils.Proxy;
    import flash.utils.flash_proxy;
   
    dynamic public class CustomObject extends Proxy {
       
        public var classProperty:String = "classProperty"; // объявленное свойство
        private var customProperties:Object = new Object(); // хранилище динамических свойств
       
        public function CustomObject() {
        }
       
        // Вызывается при чтении динамического свойства
        override flash_proxy function getProperty(name:*):* {
            if (name in customProperties) {
                return customProperties[name];
            }
            return "Property does not exist";
        }
        // Вызывается при записи в динамическое свойство
        override flash_proxy function setProperty(name:*, value:*):void {
            customProperties[name] = "Property "+name+": "+value;
        }
    }
}

Код:

// Пример использования
var myObj:CustomObject = new CustomObject();
trace(myObj.foo); // Property does not exist
myObj.foo = "bar";
trace(myObj.foo); // Property foo: bar

trace(myObj.classProperty); // classProperty
myObj.classProperty = "bar";
trace(myObj.classProperty); // bar

Обратите внимание, что методы getProperty и setProperty вызываются при попытке доступа к необъявленным свойствам класса. В примере, при доступе к явно объявленному свойству «classProperty» эти методы не вызывались.

MerlinTwi 23.10.2006 13:43

63. instance name мувиклипа на сцене
 
Во Flash 9, когда вы размещаете MovieClip на сцене (timeline) с указанием имени (instance name), автоматически происходят сразу два действия:
1. Вы присваиваете методу «name» этого мувиклипа строку эквивалентную instance name.
2. Вы создаете переменную в timeline с именем аналогичным instance name мувиклипа со ссылкой на этот мувиклип.
Флеш делает все это самостоятельно при компиляции SWF файла, чтобы помочь вам управлять мувиклипами на сцене. Важно понимать, что при создании мувиклипами средствами ActionScript, эти действия не выполняются автоматически.
К примеру:
Код:

// Здесь «my_mc» это instance name мувиклипа на сцене
trace(my_mc); // [object MovieClip]
trace(my_mc.name); // my_mc

// Создаем мувиклип с помощью AS и добавляем его к my_mc
var another_mc:MovieClip = new MovieClip();
another_mc.name = "child_mc";
my_mc.addChild(another_mc);

// Переменная для доступа к созданному мувиклипу
// автоматически не создается в  my_mc
trace(another_mc);  // [object MovieClip]
trace(my_mc.child_mc); // undefined
trace(my_mc.another_mc); // undefined

Если нужно получить ссылку на мувиклип (или любой DisplayObject) по его имени (свойству name), нужно использовать метод getChildByName();
Код:

trace(my_mc.getChildByName("child_mc"));  // [object MovieClip];
MerlinTwi: В дополнение к примеру стоит отметить, что поскольку любой мувиклип размещенный на timeline является динамическим, то можно просто создать в нем новое свойство ссылающееся на созданный мувиклип.
Код:

// Создаем мувиклип с помощью AS и добавляем его к my_mc
var another_mc:MovieClip = new MovieClip();
another_mc.name = "child_mc";
my_mc.addChild(another_mc);
// создаем в my_mc новое свойство «another_mc» для ссылки на новый мувиклип
my_mc.another_mc = another_mc;
// проверяем
trace(my_mc.another_mc); // [object MovieClip]


MerlinTwi 23.10.2006 13:57

65. Константы
 
ActionScript 3 позволяет определять константы, для этого используется ключевое слово const (const keyword), которое записывается вместо var (var keyword). Обычно константы записывают большими буквами, разделяя слова подчеркиванием, например, Event.ENTER_FRAME это константа.
Пример объявления константы:
Код:

package {
   
    import flash.display.Sprite;

    public class MyClass extends Sprite {

        public const MY_CONSTANT:String = "constant";

        public function MyClass () {
        }
    }
}

Константу нельзя переопределить. Хотя, если константа является ссылкой на объект, например массивом, то в такой массив можно добавлять новые элементы, или удалять старые, но нельзя присвоить константе ссылку на другой массив.
Код:

package {
   
    import flash.display.Sprite;

    public class MyClass extends Sprite {

        public const MY_CONSTANT:Array = new Array(1,2,3);

        public function MyClass () {
            MY_CONSTANT.push(4); // ok
            MY_CONSTANT = new Array(5,6,7); // error
        }
    }
}


MerlinTwi 24.10.2006 12:58

66. Аналог duplicateMovieClip
 
В ActionScript 3 больше нет метода duplicateMovieClip, но его можно частично воспроизвести. Например, такая функция:
Код:

package com.senocular.display {
   
    import flash.display.DisplayObject;
    import flash.geom.Rectangle;
   
    /**
    * duplicateDisplayObject
    * Создает дубликат DisplayObject,
    * аналогично методу duplicateMovieClip из AVM1
    * @param target Дублируемый объект
    * @param autoAdd Если true, то созданный дубликат будет добавлен
    * на сцену туда же, где и оригинал
    * @return возвращает ссылку на дубликат
    */
    public function duplicateDisplayObject(target:DisplayObject, autoAdd:Boolean = false):DisplayObject {
        // Создаем дубликат (constructor в кавычках, чтобы компилятор не ругался в strict mode)
        var targetClass:Class = target["constructor"];
        var duplicate:DisplayObject = new targetClass();
       
        // Дублируем свойства
        duplicate.transform = target.transform;
        duplicate.filters = target.filters;
        duplicate.cacheAsBitmap = target.cacheAsBitmap;
        duplicate.opaqueBackground = target.opaqueBackground;
        if (target.scale9Grid) {
            var rect:Rectangle = target.scale9Grid;
            // Баг, Flash 9 возвращает scale9Grid в  20 раз больше чем на самом деле
            rect.x /= 20, rect.y /= 20, rect.width /= 20, rect.height /= 20;
            duplicate.scale9Grid = rect;
        }
       
        // Добавить дубликат к target.parent
        // если autoAdd установлен в true
        if (autoAdd && target.parent) {
            target.parent.addChild(duplicate);
        }
        return duplicate;
    }
}

Как видите, все довольно просто, создается новый объект из того же класса, что и оригинал и дублируются свойства: transform, фильтры, кэширование и т.п.
Замечание: на данный момент во Flash Player 9 есть баг, некорректно возвращается свойство scale9Grid, эта функция исправляет ошибку, но когда баг в плеере исправят, строчку:
Код:

rect.x /= 20, rect.y /= 20, rect.width /= 20, rect.height /= 20;
нужно будет удалить.
Пример использования функции:
Код:

import com.senocular.display.duplicateDisplayObject;

// Создаем дубликат мувиклипа myOldSprite
// автоматически добавляя его на экран (autoAdd=true)
var newInstance: DisplayObject = duplicateDisplayObject(myOldSprite, true);
newInstance.x += 100; // сдвинуть правее, чтобы увидеть дубликат

Учтите, эта функция не является полным аналогом duplicateMovieClip, т.к. не может продублировать что-то динамически нарисованное, только объекты, имеющие свой класс.

MerlinTwi 24.10.2006 13:15

67. Метод callProperty класса Proxy
 
Если в классе наследнике от Proxy попытаться вызвать несуществующий метод, то будет вызван метод callProperty, которому передается название метода с массивом аргументов. Это можно использовать для создания динамических методов.

Как и все методы класса Proxy, callProperty объявлен в пространстве имен «flash_proxy» (flash.utils.flash_proxy), чтобы не возникало конфликтов с публичными методами. Для переопределения метода в своем классе обязательно нужно указывать пространство имен flash_proxy.

Пример: класс CustomObject наследуется от Proxy и переопределяет метод callProperty для управления динамическими методами.
Код:

package {
   
    import flash.utils.Proxy;
    import flash.utils.flash_proxy;
   
    dynamic public class CustomObject extends Proxy {
       
        private var variables:Object = new Object(); // хранилище переменных
       
        public function CustomObject() {
        }
       
        // Вызывает при обращении к динамическому методу
        override flash_proxy function callProperty(name:*, ... args):* {
           
            // Преобразуем name в строку
            name = String(name);
           
            var callType:String = name.slice(0,3); // get или set
            var callVariable:String = name.slice(3); // название переменной
           
            switch(callType) {
               
                case 'get':
                    // Если «get», то возвращает значение переменной, если она существует
                    if (callVariable in variables) {
                        return variables[callVariable];
                    }
                    return null;
                   
                case 'set':
                    // Если «set» запомним новую переменную
                    variables[callVariable] = args[0];
            }
        }
    }
}

В этом примере, с использованием callProperty, реализованы динамические методы для чтения и записи «переменных».
Код:

// пример использования
var myObj:CustomObject = new CustomObject();
trace(myObj.getMyVar()); // null
myObj.setMyVar("foo");
trace(myObj.getMyVar()); // foo
myObj.setMyVar("bar");
trace(myObj.getMyVar()); // bar
trace(myObj.MyVar); // не существует (get property error)

В начале getMyVar возвращает null, поскольку переменная «MyVar», еще не определена. После записи при помощи setMyVar, getMyVar возвращает записанное значение.

MerlinTwi 24.10.2006 13:42

68. Копирование динамической графики с помощью класса Proxy
 
В ActionScript 3 нет возможности непосредственно скопировать графику созданную в Graphics. Но можно использовать класс Proxy для записи всех вызванных методов, чтобы потом их воспроизвести при копировании. Например, такой вариант:
Код:

package com.senocular.display {
   
    import flash.display.Graphics;
    import flash.utils.flash_proxy;
    import flash.utils.Proxy;
   
    /**
    * Класс для расширения graphics с возможностью создания копии рисунка
    */
    public class GraphicsCopy extends Proxy {
       
        private var _graphics:Graphics;
        private var history:Array = new Array();
       
        /**
        * Ссылка на graphics
        */
        public function get graphics():Graphics {
            return _graphics;
        }
        public function set graphics(g:Graphics):void {
            _graphics = g;
           
            // Создаем копию рисунка на новом Graphics
            copy(this);
        }
       
        /**
        * Конструктор
        * @param graphics Ссылка на Graphics рисование по которому  запоминаем
        */
        public function GraphicsCopy(graphics:Graphics = null) {
            _graphics = graphics;
        }
       
        /**
        * Копирует сюда рисунок из другого класса GraphicsCopy
        */
        public function copy(graphicsCopy:GraphicsCopy):void {
            var hist:Array = graphicsCopy.history;
            history = hist.slice();
            if (_graphics) {
                var i:int;
                var n:int = hist.length;
                _graphics.clear();
                for (i=0; i<n; i += 2) {
                    _graphics[hist[i]].apply(_graphics, hist[i + 1]);
                }
            }
        }
       
        // Перехват вызова несуществующего метода, с помощью этого
        // метода PROXY и реализована запись процесса рисования.
        override flash_proxy function callProperty(methodName:*, ... args):* {
            methodName = String(methodName);
            switch(methodName) {
                case "clear":
                    history.length = 0;
                    break;
                default:
                    history.push(methodName, args);
            }
            if (_graphics && methodName in _graphics) {
                return _graphics[methodName].apply(_graphics, args);
            }
        }
    }
}

Теперь достаточно при рисовании вместо свойства graphics использовать экземпляр класса GraphicsCopy, который запоминает историю вызванных при рисовании методов и может ее воспроизвести, при создании копии.
Пример:
Код:

// MyClass.as Класс наследник от Shape с использованием GraphicsCopy
package {
   
    import flash.display.Shape;
    import com.senocular.display.GraphicsCopy;
   
    class MyShape extends Shape {
       
        // свойство graphicsCopy
        private var _graphicsCopy:GraphicsCopy;
        public function get graphicsCopy():GraphicsCopy {
            return _graphicsCopy;
        }
       
        // Конструктор
        function MyShape(){
            _graphicsCopy = new GraphicsCopy(graphics);
        }
    }
}

Код:

// Пример использования
// Что-то рисуем на shape 1
var shape1:MyShape = new MyShape();
shape1.graphicsCopy.beginFill(0xFF80);
shape1.graphicsCopy.lineStyle(2, 0);
shape1.graphicsCopy.drawRect(0, 0, 50, 50);

// Делаем копию рисунка на  shape2
var shape2:MyShape = new MyShape();
shape2.graphicsCopy.copy(shape1.graphicsCopy);

// Добавляем на экран
addChild(shape1);
addChild(shape2);
shape2.x += 100;

В результате вы увидите два зеленых квадрата, первый нарисован нами (метод drawRect), а вторая копия воспроизведена автоматически.

MerlinTwi 24.10.2006 13:46

69. TextField.appendText()
 
В класс TextField (flash.text.TextField) добавлен новый метод appendText (flash.text.TextField.appendText()), который добавляет новый текст в конец существующего в текстовом поле (обычный текст, не html). Этот метод аналог TextField.text += newText, но работает гораздо быстрее.
Код:

var my_tf = new TextField();
my_tf.text = "Hello";
my_tf.appendText(" world!");  // my_tf.text == "Hello world!"


MerlinTwi 25.10.2006 11:46

70. Директива include
 
В ActionScript 3 оставлена директива include для вставки кода из другого файла, только изменился синтаксис, теперь не нужно писать символ #
Код:

include "scripts/myscript.as"
Храните подключаемые внешние файлы вне папки class path чтобы они не интерпретировались как классы.

MerlinTwi 25.10.2006 12:00

71. Двойное объявление переменной
 
В ActionScript 1 и 2 можно было объявить одну переменную дважды в одной области видимости и это не вызывало ошибки. Например:
Код:

// ActionScript 1 и 2 
var i:Number = 1;
var i:String = 2;

В ActionScript 3 так нельзя. Вы можете только один раз объявить переменную в одной области видимости. Так же нельзя изменить тип переменной.
Код:

// ActionScript 3 
var i:Number = 1;
var i:String = 2; // ERROR: duplicate definition or undefined property

В ситуации, где переменная используется в разных блоках for или if, вы должны объявить ее до блока:
Код:

// Это НЕВЕРНО для ActionScript 3
// (но работает в AS1 и AS2)
if (my_btn.enabled == true) {
    var returnValue:Number = 1;
}else{
    var returnValue:Number = 0;
}
if (provideAsString == true) {
    var returnValue:String = "1";
}else{
    var returnValue:String = "0";
}

Правильно для AS3 в данной ситуации будет так:
Код:

// ПРАВИЛЬНО для ActionScript 3
var returnValueNum:Number;
var returnValueStr:String;
if (my_btn.enabled == true) {
    returnValueNum = 1;
}else{
    returnValueNum = 0;
}
if (provideAsString == true) {
    returnValueStr = "1";
}else{
    returnValueStr = "0";
}

Если же неприемлемо использование двух переменных и обязательно должна быть одна, которая должна принимать как числовое значение так и строку, нужно использовать универсальный тип переменных *
Код:

// Также ПРАВИЛЬНО для ActionScript 3
var returnValue:*;
if (my_btn.enabled == true) {
    returnValue = 1;
}else{
    returnValue = 0;
}
if (provideAsString == true) {
    returnValue = "1";
}else{
    returnValue = "0";
}


MerlinTwi 25.10.2006 12:39

72. mouseEnabled и блокирование событий
 
В ActionScript 1 и 2, в случае если несколько мувиклипов, обрабатывающих события от мышки, частично или полностью перекрываются, то события от мышки (onPress, onRollOver …) получал только верхний мувиклип. Для того чтобы событие от мышки мог получить нижестоящий мувиклип, нужно или удалить верхний или удалить у него все обработчики событий мышки, тогда мувиклип станет «прозрачен» для событий от мышки.
В ActionScript 3 ситуация примерно аналогичная, но проверка на то, прозрачен мувиклип для событий от мышки или нет, лежит не на наличие обработчиков этих событий, а на свойстве mouseEnabled (flash.display.InteractiveObject.mouseEnabled).
Если mouseEnabled=true (так по умолчанию), то мувиклип перехватывает и обрабатывает события от мышки. Если же mouseEnabled=false, то он становится прозрачен для мышки и события смогут получать и мувиклипы лежащие под ним.

В примере ниже, рисуются два частично перекрывающихся круга. Внизу "circle 1" и над ним "circle 2". При клике мышкой по любому кругу вызывается обработчик события "click", который показывает по какому кругу кликнули и переключается свойство mouseEnabled для верхнего "circle 2". Таким образом, если кликать мышкой в область, где круги пересекаются, то сначала событие перехватывает верхний "circle 2", а на второй клик реагирует уже нижний "circle 1", т.к. у вернего круга mouseEnabled=false;
Код:

function createCircle(name:String = ""):Sprite {
    var circle:Sprite = new Sprite();
    circle.name = name;
    circle.graphics.lineStyle(0);
    circle.graphics.beginFill(0xFF8080);
    circle.graphics.drawCircle(50, 50, 50);
    return circle;
}

var circle1:Sprite = createCircle("circle 1");
var circle2:Sprite = createCircle("circle 2");
circle2.x += 25;

addChild(circle1);
addChild(circle2);

circle1.addEventListener(MouseEvent.MOUSE_DOWN, click);
circle2.addEventListener(MouseEvent.MOUSE_DOWN, click);

function click(e) {
    trace(e.target.name + " clicked."); // trace name
 
    // Выключаем/включаем реакцию circle2 на события от мышки
    circle2.mouseEnabled = !circle2.mouseEnabled;
}


MerlinTwi 26.10.2006 12:53

73. Свойство mouseChildren
 
Новое свойство mouseChildren (flash.display.DisplayObjectContainer.mouseChildren) позволяет включать и выключать возможность реагировать на события от мышки для всех вложенных мувиклипов. Присвоить mouseChildren=false аналогично, если присвоить mouseEnabled=false для всех вложенных мувиклипов, после этого никто из вложенных мувиклипов не будет реагировать на события от мышки.
Это может быть полезно, если вы создаете свою кнопку наследуясь от MovieClip или Sprite со вложенными мувиклипами. По умолчанию в обработчике события такой кнопки Event.target будет не сама кнопка, а вложенный в нее мувиклип. Пример:
Код:

// Сама кнопка
var spriteButton:Sprite = new Sprite();
spriteButton.name = "spriteButton";
spriteButton.mouseChildren = true;

// Рисунок внутри кнопки
var spriteGraphics:Sprite = new Sprite();
spriteGraphics.name = "spriteGraphics";
spriteGraphics.graphics.beginFill(0x4080A0);
spriteGraphics.graphics.drawCircle(50, 50, 25);

// добавляем на экран
spriteButton.addChild(spriteGraphics);
addChild(spriteButton);

// слушаем событие click
spriteButton.addEventListener(MouseEvent.CLICK, click);
function click(evt:MouseEvent):void {
    trace(evt.target.name);
}

Если mouseChildren = true; (по умолчанию)
Код:

//output
spriteButton

Если mouseChildren = false;
Код:

// output
 spriteGraphics


MerlinTwi 26.10.2006 13:25

74. Различия между rollOver/rollOut и mouseOver/mouseOut
 
Любой интерактивный объект (flash.display.InteractiveObject) имеет две пары очень похожих событий rollOver/rollOut и mouseOver/mouseOut. Оба они реагируют на то когда мышка входит в область объекта или покидает ее. Различия в поведении этих событий возникают только тогда, когда есть вложенные мувиклипы. События rollOver и rollOut не обращают внимание на наличие вложенных мувиклипов, когда мышка войдет в область объекта сработает rollOver, и только когда мышка покинет объект сработает rollOut. События mouseOver и mouseOut будут срабатывать на перемещение мышки над вложенными мувиклипами. mouseOver сработает когда мышка войдет в область объекта, если при дальнейшем движении курсор мышки войдет в область вложенного мувиклипа сработает mouseOut, когда мышка покинет вложенный объект вновь сработает mouseOver, хотя при этих перемещениях курсор мыши фактически не покидал области объекта. События mouseOver и mouseOut будут вести себя аналогично rollOver и rollOut если установить mouseChildren=false;
Лучше всего посмотреть пример:
Код:

// Основная кнопка
var spriteButton:Sprite = new Sprite();
spriteButton.name = "spriteButton";
spriteButton.graphics.beginFill(0xFF0000);
spriteButton.graphics.drawCircle(50,50, 50);
//spriteButton.mouseChildren=false;

// Вложенный в кнопку Sprite
var spriteGraphics:Sprite = new Sprite();
spriteGraphics.name = "childrenSprite";
spriteGraphics.graphics.beginFill(0x00FF00);
spriteGraphics.graphics.drawCircle(50,50, 15);

// Добавляем на экран
spriteButton.addChild(spriteGraphics);
addChild(spriteButton);

// События
spriteButton.addEventListener(MouseEvent.ROLL_OVER, evnt);
spriteButton.addEventListener(MouseEvent.ROLL_OUT, evnt);
spriteButton.addEventListener(MouseEvent.MOUSE_OVER, evnt);
spriteButton.addEventListener(MouseEvent.MOUSE_OUT, evnt);

function evnt(e:MouseEvent):void {
    trace(e.type, "on", e.target.name);
}

При однократном проведении мышки через всю кнопку:
Код:

// output
rollOver on spriteButton
mouseOver on spriteButton
mouseOut on spriteButton
mouseOver on childrenSprite
mouseOut on childrenSprite
mouseOver on spriteButton
mouseOut on spriteButton
rollOut on spriteButton

Если убрать комментарий со строки spriteButton.mouseChildren=false; то различий в поведении событий roll и mouse не будет.

MerlinTwi 26.10.2006 13:36

75. DisplayObjectContainer contains()
 
Метод contains (flash.display.DisplayObjectContainer.contains()) позволяет определить является ли указанный мувиклип вложенным в этот мувиклип (причем не важно в какой степени вложенности).
Пример:
Код:

var king:Sprite = new Sprite();
var queen:Sprite = new Sprite();
var jack:Sprite = new Sprite();
var joker:Sprite = new Sprite();

queen.addChild(jack);
king.addChild(queen);
addChild(king);
addChild(joker);

// В итоге получилась такая вложенность
// king
//  queen
//    jack
// joker
// проверяем:

trace(king.contains(queen)); // true
trace(king.contains(jack)); // true
trace(king.contains(joker)); // false

Если нужно проверить только первый уровень вложенности, то можно просто сравнить свойство parent.
Код:

trace(queen.parent == king); // true
trace(jack.parent == king); // false


MerlinTwi 27.10.2006 12:32

76. Освобождение слушателей событий при удалении с экрана
 
Когда имеете дело с отображаемыми объектами (display objects) в ActionScript 3 важно помнить, что они могут существовать даже если не добавлены на экран (display list) и при этом продолжают получать и обрабатывать события на которые подписаны (очевидно, кроме событий от мышки). К примеру, событие enterFrame будет продолжать вызываться, и после удаления объекта с экрана. Это существенное отличие от ActionScript 1 и 2, т.к. там удаление мувиклипа с экрана означало и удаление всех событий.
Если вы хотите, чтобы событие типа enterFrame не вызывалось, когда объект удален с экрана, вы должны самостоятельно удалять подобные события. Сделать это просто, достаточно прописать обработчики событий Event.ADDED (вызывается когда объект добавлен к другому отображаемому объекту) и Event.REMOVED (вызывается когда объект удаляется от родителя).
Код:

var sprite:Sprite = new Sprite();
sprite.addEventListener(Event.ADDED, addEnterFrame);
sprite.addEventListener(Event.REMOVED, removeEnterFrame);

// Добавляем/удаляем обработчик события enter frame
function addEnterFrame(evt:Event):void {
    trace("added");
    sprite.addEventListener(Event.ENTER_FRAME, enterFrame);
}
function removeEnterFrame(evt:Event):void {
    trace("removed");
    sprite.removeEventListener(Event.ENTER_FRAME, enterFrame);
}
function enterFrame(evt:Event):void {
    trace("Time: " + getTimer());
}

// Добавляем/удаляем объект по клику мышки
stage.addEventListener(MouseEvent.CLICK, addRemove);
function addRemove(evt:Event):void {
    if (this.contains(sprite)) {
        this.removeChild(sprite);
    }else{
        this.addChild(sprite);
    }
}

Код:

// output
added
Time: 813
Time: 927
removed
added
Time: 2509
Time: 2597
removed


MerlinTwi 27.10.2006 12:54

77. Определение добавления или удаления со Stage
 
В предыдущем примере показано как определить добавление и удаление объекта от другого отображаемого объекта. Но если с экрана будет удален наш родительский объект, то события ADDED/ REMOVED не сработают. Как же отследить реальное добавление или удаление с экрана? Можно воспользоваться классом StageDetection (com.senocular.events.StageDetection)
Код:

package com.senocular.events {
   
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.utils.Dictionary;
   
    /**
    * StageDetection
    * Позволяет DisplayObject реагировать на добавление и удаление со stage
    *
    * Использовать в конструкторе
    * var stageDetect:StageDetection = new StageDetection(this);
    * stageDetect.addEventListener(StageDetection.ADDED_TO_STAGE, addedToStage);
    * stageDetect.addEventListener(StageDetection.REMOVED_FROM_STAGE, removedFromStage);
    */
    public class StageDetection extends EventDispatcher {
       
        protected var target:DisplayObject;
        protected var parents:Dictionary;
        protected var detect:String;
       
        public static const ADDED_TO_STAGE:String = "addedToStage";
        public static const REMOVED_FROM_STAGE:String = "removedFromStage";
       
        /**
        * Конструктор
        */
        public function StageDetection(targetObject:DisplayObject) {
            target = targetObject;
           
            // Определяем за чем нужно следить, за добавлением или удалением
            detect = (target.stage == null) ? Event.ADDED : Event.REMOVED;
           
            // Обновляем слушателей у родителей
            updateListeners();
        }
       
        /**
        * Обновляет слушателей событий ADDED и REMOVED
        */
        protected function updateListeners(newDetect:String = null):void {
           
            // Очищаем старые listeners у родителей
            if (parents) {
                for (var key:Object in parents) {
                    key.removeEventListener(detect, stageCheck, false);
                }
            }
           
            // Запомним на какие события подписываемся
            if (newDetect) detect = newDetect;
               
            // Прописывает слушателей этого события у родителей
            parents = new Dictionary(true);
            var parent:DisplayObject = target;
            while (parent) {
                parent.addEventListener(detect, stageCheck, false, 0, true);
                parents[parent] = true;
                parent = parent.parent;
            }
        }
       
       
        /**
        * В обработчике ADDED и REMOVED проверяем наличие stage
        */
        protected function stageCheck(evt:Event):void {
           
            // Реагируем только на оригинальный источник события
            if (evt.target != evt.currentTarget) return;
               
            // evt.type или ADDED или REMOVED
            switch(evt.type) {
               
                // Кого-то из родителей добавили к другому объекту
                case Event.ADDED:
                   
                    // stage доступен, значит нас добавили на экран
                    if (target.stage != null) {
                       
                        // Рассылаем событие и обновляем listeners
                        dispatchEvent(new Event(ADDED_TO_STAGE));
                        updateListeners(Event.REMOVED);
                       
                    // stage недоступен, значит просто добавили к другому объекту
                    } else {
                       
                        // Список родителей изменился, значит обновим listeners
                        updateListeners();
                    }
                    break;
                   
                // Родителя удалили со stage
                case Event.REMOVED:
                   
                    // Рассылаем событие и обновляем listeners
                    dispatchEvent(new Event(REMOVED_FROM_STAGE));
                    updateListeners(Event.ADDED);
                    break;
            }
        }
    }
}

MerlinTwi: На данный момент этот класс стоит рассматривать только в ознакомительных целях, как можно красиво реализовать решение поставленной задачи. Практической пользы нет, т.к. с очередным обновлением Flash плеера были добавлены события ADDED_TO_STAGE и REMOVED_FROM_STAGE позволяющие DisplayObject 'у проверять доступность stage. Событие ADDED_TO_STAGE отправляется DisplayObject 'у когда он один или дерево объектов с ним добавляются на сцену, REMOVED_FROM_STAGE при удалении.

MerlinTwi 27.10.2006 13:21

78. Фазы событий
 
В ActionScript 3 для отображаемых объектов добавлены три фазы распространения событий:
  • Захват, capturing
  • Обработка, targeting
  • Подъем, bubbling

1. захват (capturing)
Событие начинается со stage и спускается вниз, вызывая обработчики связанные с этим событием у всех объектов (если же обработчика не существует, ничего не происходит).
Код:

      event
+---------|----+
| parent  |    |
|  +------V-+  |
|  |child  |  |
|  |        |  |
|  +--------+  |
+--------------+

2. обработка (targeting)
На стадии обработки вызывается обработчик события объекта которому собственно оно и предназначалось.
Код:

+--------------+
| parent      |
|  +------ -+  |
|  |child  |  |
|  |event X |  |
|  +--------+  |
+--------------+

3. подъем (bubbling)
Перебираются все объекты, начиная с "виновника" события и до stage.
Код:

+---------^----+
| parent  |    |
|  +------|-+  |
|  |child | |  |
|  | event| |  |
|  +--------+  |
+--------------+

По умолчанию addEventListener подписывает обработчик события только на две фазы 2 (обработка) и 3 (подъем). Это позволяет вашему обработчику события реагировать на событие у себя а так же на это событие и у детей.
Если вы хотите реагировать на стадию захвата (1), необходимо в addEventListener указать третьим параметром true.
Код:

target.addEventListener("event", listener, true);
При этом обработчик события будет реагировать только на стадию захвата. Если вы хотите подписать обработчик события на все стадии нужно вызвать addEventListener дважды:
Код:

target.addEventListener("event", listener, true); // захват
target.addEventListener("event", listener, false); // обработка и подъем


MerlinTwi 30.10.2006 12:31

79. Определение фазы события
 
Обработчик события в ActionScript 3 может определить фазу события (см. предыдущий совет о фазах событий) по свойству eventPhase, которое может принимать три значения:
  • EventPhase.CAPTURING_PHASE
  • EventPhase.AT_TARGET
  • EventPhase.BUBBLING_PHASE
Пример:
Код:

var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

circle.addEventListener(MouseEvent.CLICK, clickCircle);
circle.addEventListener(MouseEvent.CLICK, clickCircle, true);
stage.addEventListener(MouseEvent.CLICK, clickStage);
stage.addEventListener(MouseEvent.CLICK, clickStage, true);

function clickCircle(evt:MouseEvent):void {
    trace("clickCircle: " + getPhaseName(evt.eventPhase));
}
function clickStage(evt:MouseEvent):void {
    trace("clickStage: " + getPhaseName(evt.eventPhase));
}

function getPhaseName(phase:int):String {
    switch(phase) {
        case EventPhase.CAPTURING_PHASE:
            return "Capturing Phase";
        case EventPhase.AT_TARGET:
            return "At Target Phase";
        case EventPhase.BUBBLING_PHASE:
            return "Bubbling Phase";
    }
    return "Error: No Phase Detected";
}

Кликнув по кругу:
Код:

//output
clickStage: Capturing Phase
clickCircle: At Target Phase
clickStage: Bubbling Phase

Кликнув вне круга:
Код:

//output
clickStage: At Target Phase


MerlinTwi 30.10.2006 12:46

80. Предотвращение распространения события
 
Если нужно пресечь дальнейшее распространение события можно использовать методы stopPropagation() (flash.events.Event.stopPropagation()) или stopImmediatePropagation() (flash.events.Event.stopImmediatePropagation()).
stopPropagation – предотвращает дальнейшее распространение события в стадии захвата (capturing) или подъема (bubbling)
stopImmediatePropagation – блокирует вообще все, в том числе и фазу обработка (targeting).
Смотрим пример:
Код:

var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

circle.addEventListener(MouseEvent.CLICK, clickCircle1);
circle.addEventListener(MouseEvent.CLICK, clickCircle2);
stage.addEventListener(MouseEvent.CLICK, clickStage);

function clickCircle1(evt:MouseEvent):void {
    // evt.stopPropagation();
    // evt.stopImmediatePropagation();
    trace("clickCircle1");
}
function clickCircle2(evt:MouseEvent):void {
    trace("clickCircle2");
}
function clickStage(evt:MouseEvent):void {
    trace("clickStage");
}

При клике мышкой по кругу (circle), без использования прерывания распространения события:
Код:

//output
clickCircle1
clickCircle2
clickStage

Срабатывают два обработчика событий на клик мышки "clickCircle1","clickCircle2" фаза обработка (targeting) и обработчик "clickStage" на фазе подъема (bubbling).

Если убрать комментарий с evt.stopPropagation();
Код:

//output с использованием stopPropagation
clickCircle1
clickCircle2

В обработчике "clickCircle1" вызывается stopPropagation, которое предотвращает дальнейшее распространение события по фазам захвата (capturing) и подъема (bubbling), поэтому далее нормально вызывается обработчик "clickCircle2" в фазе обработка (targeting), но не вызывается "clickStage", поскольку он уже на фазе подъема (bubbling).

Если убрать комментарий с evt.stopImmediatePropagation();
Код:

// output с использованием stopImmediatePropagation
clickCircle1

В этом случае не будет вызван даже обработчик "clickCircle2" в фазе обработка (targeting).

MerlinTwi 30.10.2006 13:21

81. Глобальные события
 
В ActionScript 1 и 2 имелись события onMouseDown и onMouseUp, которые вызывались во всех объектах не зависимо от того, где находилась мышка. В ActionScript 3 такие события распространяются только на те объекты, над которыми находится мышка в момент клика. К примеру, если ваш мувиклип обрабатывает события MOUSE_DOWN и MOUSE_UP, а пользователь нажал кнопку мыши над мувиклипом, отвел мышку в сторону и отпустил кнопку, то событие MOUSE_UP ваш мувиклип не получит. Для обработки события глобально в ActionScript 3 нужно добавлять обработчик события к Stage. Поскольку именно к stage добавляются все отображаемые объекты, то через него проходят все события.
Одна маленькая, но важная деталь. Что если вы добавили обработчик события к stage, а какой-то мувиклип использовал предотвращение распространения события (см. предыдущий совет), в этом случае ваш обработчик события вызван не будет. Значит нужно добавлять обработчик события к stage на фазу захвата (capture). Но если пользователь кликнет по пустому месту, где нет никаких объектов, то целью события в этом случае будет stage и ваш обработчик события на фазу захвата (capture) вызван не будет, поскольку stage в данном случае участвует только в фазе обработка (targeting). Получается, для того чтобы поймать событие глобально в любом случае, нужно добавить два обработчика события к stage на фазе захвата (capture) и на фазе обработка (targeting). Поскольку нельзя поставить обработчик события только на фазу обработка (targeting), то нужно будет фазу подъема (bubbling) игнорировать в обработчике.
Пример:
Код:

var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

// Используем stage для глобальной обработки mouse up
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp, true);
function mouseUp(evt:MouseEvent):void {
    // обрабатываем только фазы capturing и target
    if (evt.eventPhase == EventPhase.BUBBLING_PHASE) return;
   
    trace("global mouseUp");
}

// пусть мувиклип circle предотвращает распространение события mouse up
circle.addEventListener(MouseEvent.MOUSE_UP, mouseUpCircle);
function mouseUpCircle(evt:MouseEvent):void {
    trace("mouseUpCircle");
    evt.stopPropagation();
}

Даже не смотря на то, что circle вызывает stopPropagation для предотвращения распространения события, в этом случае наш глобальный обработчик будет вызван на фазе захвата (capturing)
Код:

//клик вне мувиклипа
global mouseUp
 
// клик по мувиклипу circle
global mouseUp
mouseUpCircle


MerlinTwi 31.10.2006 15:44

82. Реализация onReleaseOutside в AS3
 
В ActionScript 3 нет события onReleaseOutside, но его можно реализовать самостоятельно. Поскольку события mouseDown и mouseUp привязаны к объекту, то узнать о том, что кнопка мышки была отпущена за пределами объекта можно только слушая событие mouseUp глобально (см. предыдущий совет). Единственное, нужно добавить проверку отпустили кнопку мыши над тем же объектом (тогда это будет просто mouseUp) или над другим (тогда это будет onReleaseOutside).
Пример:
Код:

// в этой переменной запоминаем ссылку на объект по которому кликнули
var clicked:DisplayObject;

var circle:Sprite = new Sprite();
circle.graphics.beginFill(0x4080A0);
circle.graphics.drawCircle(50, 50, 25);
addChild(circle);

function mouseDown(evt:MouseEvent):void {
    trace("mouseDown");
    clicked = circle;
}
function mouseUp(evt:MouseEvent):void {
    trace("mouseUp");
}
function mouseUpOutside(evt:MouseEvent):void {
    trace("mouseUpOutside");
}

// обработчик события на клик мышкой по объекту circle
circle.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
// обработчики событий для глобального mouseUp (подробнее см. предыдущий совет)
stage.addEventListener(MouseEvent.MOUSE_UP, captureMouseUp);
stage.addEventListener(MouseEvent.MOUSE_UP, captureMouseUp, true);

function captureMouseUp(evt:MouseEvent):void {
    if (evt.eventPhase == EventPhase.BUBBLING_PHASE) return;
   
    // убедимся, что кликнули по нужному нам объекту
    if (clicked == circle) {
        clicked = null; // очищаем ссылку для следующих кликов
       
        var target:Sprite = evt.target as Sprite;
        // отпустили кнопку над circle
        if (target == circle) {
            mouseUp(evt);
        }else{
            // отпустили кнопку мыши вне circle
            mouseUpOutside(evt);
        }
    }
}

В этом примере мы реализовали аналог событий onPress, onRelease, и onReleaseOutside из AS1 и AS2.
Нажать и отпустить кнопку мыши над кругом (объект circle):
Код:

//output
mouseDown
mouseUp

Нажать кнопку мыши над кругом, а отпустить в другом месте:
Код:

//output
mouseDown
mouseUpOutside

Примечание: вместо того чтобы использовать переменную clicked, можно было бы в обработчике клика мышки mouseDown прописать слушателей на глобальный mouseUp, а в обработчике captureMouseUp удалить их.

MerlinTwi 01.11.2006 13:48

83. Flash 9: Document Class
 
В ActionScript 3 можно назначить свой класс (Document class) для главного timeline, который фактически будет являться root для всех отображаемых объектов. Назначить Document class можно в панели Property Inspector для документа (когда ничего не выделено) или же в диалоге ActionScript 3 Settings который расположен: File > Publish Settings > Flash [закладка] > Settings... [кнопка для ActionScript 3]. Просто впишите в поле "Document class" название вашего класса.
Document Class обязательно должен наследоваться от Sprite (flash.display.Sprite) или его потомков. Если будете использовать главную timeline, то document class лучше наследовать от MovieClip (flash.display.MovieClip) т.к. MovieClip поддерживает фреймы.
Document Class обязательно должен быть публичным (public).
Пример:
Код:

package {

    import flash.events.Event;
    import flash.display.MovieClip;
   
    public class CustomDocument extends MovieClip {
       
        public function CustomDocument() {
            addEventListener(Event.ADDED, checkChildren);
            checkChildren(new Event("initialize"));
        }
       
        private function checkChildren(evt:Event):void {
            // Позволяем добавить только один объект на root
            if (numChildren > 1) {
                throw (new Error("This movie can have only one child instance"));
            }
        }
    }
}

Этот класс запрещает разместить на root более одного объекта. Обратите внимание, что метод checkChildren нужно отдельно вызвать в конструкторе, т.к. объекты могли быть добавлены на timeline до того, как создан обработчик события Event.ADDED (размещение в IDE на timeline до публикации).

MerlinTwi 01.11.2006 13:56

84. Доступ к stage и root
 
Stage (сцена) – это фактически первый контейнер в который загружаются все остальные отображаемые объекты, включая root. Stage может быть только один, а вот root может быть сколько угодно, по одному на каждую загруженную внешнюю флешку.
Все отображаемые объекты (flash.display.DisplayObject) имеют свойства stage и root. До тех пор, пока объект не добавлен на сцену (stage) эти свойства равны null. Свойство stage, когда доступно, всегда ссылается на объект stage. А вот свойство root в разный случаях содержит разные ссылки:
  • Для stage, свойство root всегда ссылается на stage
  • Для всех объектов, расположенных на главной timeline, свойство root ссылается на главную timeline
  • Если объект добавлен прямо на сцену (stage) из любой timeline, свойство root для него и его детей будет ссылаться на stage
  • Для объектов расположенных в подгруженном внешнем SWF-файле, свойство root ссылается на главную timeline этого SWF-файла
  • Для загруженных картинок, свойство root ссылается на экземпляр класса Bitmap, куда была загружена картинка
Помните, что только после добавления на сцену, или к любому другому объекту уже добавленному на сцену, есть доступ к свойствам stage и root. Это серьезное отличие от ActionScript 1 и 2 и на то есть две причины:
  1. В AS1 и 2 мувиклипы в момент своего создания уже были добавлены на сцену. В AS3 это не так. Фактически только document class (см. предыдущий совет) изначально расположен на stage (во Flash 9 еще и объекты, расположенные на timeline в IDE до публикации).
  2. В AS1 и 2 все скрипты и классы были определены внутри timeline или мувиклипов, как результат отовсюду был доступ к свойству _root.В AS3 классы не привязаны к timeline и могут не иметь доступа к root или stage.
Это сильно ограничивает наш доступ к stage или root в скриптах. К примеру, не отображаемый класс в AS3 вообще не может получить доступ к stage или root, только если получит ссылку извне.
Код:

package {
   
    import flash.display.Stage;

    public class CustomObject {
   
        private var stage:Stage;
   
        public function CustomObject(stageRef:Stage) {
       
            // Ссылку на stage нужно передавать как параметр конструктора
            stage = stageRef;
        }
    }
}

Отображаемые объекты в момент создания в конструкторе не имеют доступа к stage и root (если только они не были расположены на timeline до компиляции)
Код:

package {
   
    import flash.display.Sprite;
    import flash.events.Event;

    public class CustomDisplayObject extends Sprite {
   
        public function CustomObject() {
       
            // stage недоступен до тех пор,
            // пока объект не будет добавлен на сцену
            trace(stage); // null
        }
    }
}

Это создает определенные трудности. Нужно добавлять дополнительный обработчик события добавления на сцену (ADDED_TO_STAGE), из которого уже вызывать функцию init для совершения необходимых действий.
Другой вариант решения создать статичное свойство в каком-либо классе, в которое прописать ссылку на root или stage и уже к этому свойству обращаться из любого места. Лучше всего для этого подойдет document class, т.к. он изначально имеет доступ к stage и сам является root.
Пример:
Код:

package {
   
    import flash.display.DisplayObject;
    import flash.display.MovieClip;
    import flash.display.Stage;
   
    public class TopLevel extends MovieClip {
       
        public static var stage:Stage;
        public static var root:DisplayObject;
           
        public function TopLevel() {
            TopLevel.stage = this.stage;
            TopLevel.root = this;
        }
    }
}

Доступ к stage из неотображаемого класса:
Код:

package {
       
    public class RandomClass {
           
        public function RandomClass() {
            trace(TopLevel.stage); // [object Stage]
        }
    }
}

В идеале стоит писать код так, чтобы не было необходимости в создании классов подобных TopLevel и жесткой привязке к нему. Особенно если в разработке участвуют несколько программистов. Определять доступность stage лучше по событию.

MerlinTwi 01.11.2006 14:00

85. use namespace
 
Для уточнения используемого пространства имен (см. совет 54) вместе с оператором «::» (name qualifier operator), можно использовать use namespace (use namespace directive). use namespace определяет пространство имен на весь блок кода (package, class, method). use namespace нельзя дважды использовать в одном блоке, но можно обратиться к другому пространству имен через оператор «::»
Пример:
Код:

package {
 
    public namespace company = "http://www.example.com/company";
    public namespace individual = "http://www.example.com/individual";
       
    public class UsingNameSpaces {
     
        use namespace individual;
       
        company var value:int = 10;
        individual var value:int = 2;
     
        public function UsingNameSpaces(){
            showValue(); // traces individual::2
            company::showValue(); // traces company::2;
        }
     
        company function showValue() {
            trace("company::" + value);
        }
     
        individual function showValue() {
            trace("individual::" + value);
        }
    }
}

В этом легко запутаться, хоть все и логично. Здесь директивой use namespace было назначено по умолчанию для всего класса использовать пространство имен individual. Соответственно первый вызов метода showValue без указания пространства имен это аналог individual::showValue. Во втором вызове явно указано пространство имен company, но поскольку в самом методе showValue используется свойство value без указания пространства имен, то по умолчанию берется то, что назначено use namespace, т.е. individual и метод company::showValue выводит 2 (individual::value) а не 10 (company::value).

MerlinTwi 02.11.2006 18:30

87. Класс Error, ошибки выполнения
 
Flash Player 9 с ActionScript 3 имеет встроенные средства для отображения и предупреждения об ошибках в коде во время выполнения. Если у вас установлена отладочная версия плеера (debug versions player), то появляется диалог с подробным описанием возникшей ошибки.
Возможные типы ошибок можно найти здесь (Top Level) или в пакете (flash.errors). Вы можете создавать свои типы ошибок, расширяя класс Error, и создавать ее с помощью ключевого слова throw (throw statement). Эта возможность была и в ActionScript 1 и 2, но раньше плеер на ошибку никак не реагировал.
Пример ошибки, которая возникает во время выполнения:
Код:

var value:* = new Array();
trace(value.getChildAt(0));

Попробуйте запустить SWF с этим кодом в браузере. Здесь генерится ошибка TypeError (Top Level TypeError), поскольку value типа Array и не имеет метода getChildAt.
Дополнительная информация об ошибках выполнения.


Часовой пояс GMT +4, время: 09:55.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.