PDA

Volledige versie bekijken : Custom events van eigen klasse komen niet aan op stage.


jidijkstra
%Europe/Berlin %697 %2007, 17:44
In mijn project heb ik wat problemen om een event op te laten vangen door de stage...

Er zijn twee klassen: een XML loader klasse LocationDB (die nog wat andere functies vervult) en een EventDispatcher DBEvent die een custom event dispatcht. Deze worden beiden op de stage gebruikt. Er wordt een nieuw LocationDB-object aangemaakt waardoor er een XML wordt ingeladen in die klasse. Als deze is ingeladen wordt er een custom event (DBEvent.XMLLOADED) gedispatcht (?) uit de DBEvent dispatcher, die opgevangen moet worden door de stage. Als ik daar een eventlistener maak voor het betreffende event wordt die functie (doAction) alleen uitgevoerd als ik het event vanaf de stage dispatch. Als ik het event door de LocationDB laat dispatchen wordt het event wel afgevuurd (de trace uit die functie zie ik onstaan, in dit geval "constructor triggered") maar wordt niet door de eventListener opgevangen. Waar heeft dat mee te maken? En hoe los ik dat op? Want na een dag zelf puzzelen, zoeken en frustraties zou ik ERG graag het antwoord een keer weten :P

Thanks vast,

De LocationDB-klasse (waar wat voor dit verhaal overbodige regels zijn verwijderd):

package {
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.*;
import flash.net.*;

public class LocationDB extends EventDispatcher{
public var xmlDB:XML;
public var xmlDBLoader:URLLoader = new URLLoader();

public function LocationDB(file:String) {
xmlDBLoader.addEventListener(Event.COMPLETE, buildObjectArray);
loadDB(file);
}

public function loadDB(file:String):void {
xmlDBLoader.load(new URLRequest(file));
}

public function buildObjectArray(e:Event = null):void {
dispatchEvent(new DBEvent(DBEvent.XMLLOADED));
var count:int;
//hier gebeuren nog wat andere zaken (waaronder het bouwen van een array)
}
}
}


En de DBEvent dispatcher

package {
import flash.events.Event;
public class DBEvent extends Event {
public static const XMLLOADED:String = "xmlloaded";
public function DBEvent(type:String):void{
trace("constructor triggered: " + type);
super(type, true, false);
}
public override function clone():Event {
return new DBEvent(type);
}
}
}


Voor de volledigeheid ook nog de actionscript die ik op de main timeline van een fla bestand heb gezet:

import LocationDB;

stage.addEventListener(DBEvent.XMLLOADED, doAction);
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);

function keyPressed(e:KeyboardEvent):void {
if(e.keyCode == 49) {
var dBase:LocationDB = new LocationDB("xml/locationdb.xml");
trace (dBase);
}
if(e.keyCode == 50) {
dispatchEvent(new DBEvent(DBEvent.XMLLOADED));
}
}

function doAction(e:Event):void {
trace("doAction triggered, event: " + e);
}

Dauntless
%Europe/Berlin %706 %2007, 17:56
Ik volg niet helemaal... Zit de LocationDB dan ergens in de display-list van de stage? (Wat in principe al niet kan aangezien hij de Dispatcher class extend en deze extend de DisplayObject niet en hij implementeert ook IBitmapDrawable niet (dus kan je hem niet toevoegen aan de display list)).

Als je luistert naar events op de stage, moeten deze event ook gebroadcast worden vanuit de stage of een van zijn children.

jidijkstra
%Europe/Berlin %720 %2007, 18:17
...en ik maar denken dat ik AS3 eindelijk een beetje begon te snappen ;)
Maar het klinkt erg logisch: LocationDB maakt idd geen deel uit van de displaylist begrijp ik nu. Ik had min of meer verwacht dat dit zou gebeuren met
var dBase:LocationDB = new LocationDB("xml/locationdb.xml"); maar dat is natuurlijk onzin... daar is addChild voor (right?).
De klasse LocationDB extends de EventDispatcher omdat ik anders geen dispatchEvent() kan aanroepen, wat ik gelezen heb ergens anders in het forum.

Mijn volgende vraag wordt dus: "Hoe kan ik de event die word gedispatched door de LocationDB op de stage terecht komen in deze situatie?"

Dauntless
%Europe/Berlin %731 %2007, 18:33
In AS2 gebruikte ik een Singleton EventBroadcaster class... In AS3 zou ditzelfde principe ook moeten werken...

De AS2 versie:
import mx.events.EventDispatcher;

class be.dauntless.utils.EventBroadcaster
{
private static var __instance:EventBroadcaster;

private var dispatchEvent:Function;
public var addEventListener:Function;
public var removeEventListener:Function;

private function EventBroadcaster()
{
EventDispatcher.initialize(this);
}

public static function getInstance():EventBroadcaster
{
if(EventBroadcaster.__instance == undefined)
{
EventBroadcaster.__instance = new EventBroadcaster();
}
return EventBroadcaster.__instance;
}

public function broadcastEvent(p_eName:String, p_object:Object, p_target:Object):Void
{
var ob:Object = new Object();
ob.target = p_target;
ob.type = p_eName;
ob.data = p_object;
dispatchEvent(ob);
}
}

BernardV
%Europe/Berlin %841 %2007, 21:12
Ik neem aan dat je maar 1 LocationDB in je code gebruikt, dan kun je ook van LocationDB een singleton class maken en dan in frame 1 op je main timeline al een listeren aanmaken.
Beetje het idee van een singleton eventdispatcher, alleen dan dus een singleton class :)

jidijkstra
%Europe/Berlin %337 %2007, 09:05
Even denken hoor, die Singleton pattern is nieuw voor mij, nog nooit eerder gebruikt. Wat ik erover lees is dat een Singleton klasse gebruikt wordt als er maar één (1) instantie van die klasse mag zijn. Als de klasse dan ergens opnieuw wordt geinstantieerd , krijg je de waarde van de al eerder geinstantieerde klasse terug. Toch? (prachtige pattern trouwens, nu ik 'm doorheb. Kan ik wel vaker gebruiken denk ik! Voor de meelezers: Singletons in AS3 kunnen op deze manier: http://www.cynergysystems.com/blogs/page/andrewtrice?entry=singleton_in_as3)

Hoe helpt mij dat precies in mijn situatie? Dat zie ik namelijk nog niet helemaal. Want BernardV, als ik mijn LocationDB als singleton maak, hoe weet mijn timeline dan dat het XML bestand is geladen? Daar wilde ik juist een event voor laten dispatchen naar de timeline toe. Op "mijn" bovenstaande manier lukt dat niet en ik kan me zo voorstellen dat dat met een singleton ook niet kan.

Misschien heb ik jullie met mijn code een beetje in de war gebracht, dit was namelijk een testscenario waarbij een LocationDB wordt aangemaakt als je op de "2"-toets drukt. Dat heb ik gedaan om aan te geven dat de event niet op de stage aankwam als er een event werd gedispatched in een object ipv op de timeline in frame 1.

Eigenlijk moet het zo zijn dat er in het begin van frame 1 van de timeline een LocationDB wordt aangemaakt en wanneer deze klaar is moet er een aantal dingen op de timeline in die zelfde eerste frame gebeuren met een paar arrays die in LocationDB zitten. Oftewel: frame 1 moet luisteren naar LocationDB naar het bericht dat hij klaar is met laden en het aanmaken van een paar arrays en dergelijke. Dan pas mag er een aantal andere dingen gebeuren. Een soort Loader.COMPLETE, maar dan met mijn LocationDB.

Voor de duidelijkheid de code zoals deze in mijn main timeline staat op frame 1. Deze is dus eigenlijk zonder de eventListener voor de keyboardevents. IRL wordt er niet op een KeyboardEvent gewacht:

import LocationDB;
addEventListener(DBEvent.XMLLOADED, doAction);
var dBase:LocationDB = new LocationDB("xml/locationdb.xml");
function doAction(e:Event):void {
trace("doAction triggered, event: " + e);
}

Dauntless
%Europe/Berlin %376 %2007, 10:02
In plaats van dat je de addEventListener toepast op je stage, pas je hem toe op de LocationDB zelf:


var dBase:LocationDB = LocationDB.getInstance();
dBase.addEventListener(DBEvent.XMLLoaded, doAction);

Voor de rest mag je hem nog gewoon gebruiken in de classes waar je hem in je beginsituatie al ingebruikte.

jidijkstra
%Europe/Berlin %359 %2007, 09:37
In één woord: Briljant! :P

Precies wat ik nodig had. De LocationDB heb ik nu als singleton ingericht, wat mij ook de gelegenheid geeft om afzonderlijke SWF's makkelijker te testen, zonder de hele applicatie te hoeven publishen elke keer.
Hij dispatcht nu custom events en doordat ik de listener aan de dBase instance toepas, komen deze events ook op de stage terecht.

Dauntless & BernardV, thanks a million for the eye-opener!