how to detect children size change actionscript? - actionscript-3

if i got a container,there were children display object in it.
the container's size was decided by the children's size.when the size change i need to do something to fit the new size.
since i didn't find the kind of event to detect it,i use ENTER_FRAME event,that's quite silly.
keep every last frame's size,and compare to current frame.maybe the size changed after you deal with the enter frame event,so in some cases,you might see the correct result in the next frame.
i don't think it's a good solution for a component.give me you ideas,thanks.

Well there is no standard solution for this. You could create a custom class which overrides the setters of width/height/x/y/scaleX/scaleY/scrollRect .. and maybe some other properties. The children should extend such a class.
I used a boolean to prevent its dispatched multiple times, after a frame the flag will be resetted.
override public function set width(value:Number):void
{
if (value !== super.width && !isNaN(Number(value)) this.dispatchResize();
super.width = value;
}
override public function set height(value:Number):void
{
if (value !== super.height && !isNaN(Number(value)) this.dispatchResize();
super.height = value;
}
override public function set scaleX(value:Number):void
{
if (value !== super.scaleX && !isNaN(Number(value)) this.dispatchResize();
super.scaleX = value;
}
override public function set scaleY(value:Number):void
{
if (value !== super.scaleY && !isNaN(Number(value)) this.dispatchResize();
super.scaleY = value;
}
private var _hasDispatchedResize:Boolean;
protected function dispatchResize():void
{
// do something
if (!this._hasDispatchedResize)
{
this.dispatchEvent(new Event(Event.RESIZE));
this._hasDispatchedResize = true;
this.addEventListener(Event.ENTER_FRAME, handleEnterFrameOnce);
}
}
private function handleEnterFrameOnce(event:Event):void
{
this.removeEventListener(Event.ENTER_FRAME, handleEnterFrameOnce);
this._hasDispatchedResize = false;
}
Now, in the container class you can listen to a Event.RESIZE of the children. Your not really sure if the value actually changed (in case of a frame change on a MovieClip), but in most cases this will work. I've added an extra check inside this setters before dispatching the resize. It depends on the case if this will suit your case.

What I would do is whatever function resizes the children, add your own dispatch event to it that would broadcast that a size change occurred.
//Never forget to import your classes.
import flash.events.EventDispatcher;
import flash.events.Event;
//Our custom listener listens for our custom event, then calls the function we
//want called when children are resized.
addEventListener("childResized", honeyIResizedTheChildren);
function resizingLikeFun(){
datClip.width++;//they be resizin, yo.
dispatchEvent(new Event("childResized"));//Our custom event
}
function honeyIResizedTheChildren(e:Event){
trace("Giant Cheerios");
//Whatever you want to do when the children are resized goes here.
}
I assume some things with that code, so let me know if this doesn't fully apply to you.

Related

Is there a way to remove listeners easier?

so, there´s a easy way to remove all childs from container like this
while (container.numChildren)
{
container.removeChildAt(0);
}
is there a way to remove stage.eventListener in plural (dont know if im using the word right), something like stage.eventListeners? something like that?
thanks!
No, there isn't. There is no mechanism, at least publicly available, that keeps track of event listeners. However, you could do it yourself.
Basically, you need to keep track of each listener that is added. The easiest way would be to extend the classes you want to do this on and override addEventListener().
private var listeners:Vector.<Object>;
override public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false ):void {
var obj:Object = {
type : type,
listener : listener
};
this.listeners.push( obj );
super.addEventListener( type, listener, useCapture, priority, useWeakReference );
}
override public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ):void {
var i:Number, l:Number;
l = this.listeners.length;
for ( i = 0; i < l; i++ ) {
if ( this.listeners[i].type == type && this.listeners[i].listener == listener ) {
this.listeners.splice( i, 1 );
break;
}
}
super.removeEventListener( type, listener, useCapture );
}
public function removeAllEventListeners():void {
var i:Number, l:Number;
l = this.listeners.length;
for ( i = 0; i < l; i++ ) {
super.removeEventListener( this.listeners[i].type, this.listeners[i].listener );
}
this.listeners.splice( 0, this.listeners.length ); //empties the vector
}
If extending a class is not an option for you (either because you are using Flash Pro with the timeline or if it's the stage you need to remove all event listeners from), you could manually add it to the Vector each time you add an event listener. It's not quite as simple as the way I described above, but it would still work.
Do note that you should probably add the useCapture property to the object that is being saved and check for that as well, but I have honestly never set that property to anything but default so I did not include it in my code
In FlashPlayer 11, DisplayObjectContainer supports a removeChildren function that removes all children. So you can replace your while loop with container.removeChildren()
As for removing all eventListeners, there is no built-in method for this. However, you could write something yourself to keep track of all listeners added to a particular object, allowing you to remove them all by looping over the collection.
Unfortunately the short answer is that it is not possible. Per default, event dispatchers do not publically keep track of what listeners are registered. So you cannot loop over some list and just remove them all.
You will have to remember the listeners you add yourself.
There is a very easy solution, Robert Penner's phenomenal Signals library. Here is an example:
import org.osflash.signal.Signal;
public class Rocket()
{
// Signals are used to dispatch messages to interested parties
public var enginesFiredSignal:Signal;
public function Rocket()
{
// You have to instantiate the signal for it to do anything
enginesFiredSignal = new Signal();
}
public function fireEngines():void
{
// Dispatch will notify any interested parties
enginesFiredSignal.dispatch();
}
public function dispose():void
{
// This is the magic call... it removes *all* interested parties.
// No need to iterate through them and removeEventListeners.
enginesFiredSignal.removeAll();
}
}
public class Academy()
{
public var rocket:Rocket;
public function Academy()
{
rocket = new Rocket();
// We are interested to know *when* the engines fire,
// so we "add" ourself to the Signal.
rocket.enginesFiredSignal.add( enginesFiredHandler );
// This call will force the Signal to do its thing
rocket.fireEngines();
// Disposing of the Rocket will automatically remove us from
// its Signal. No further event removal or maintenance required.
rocket.dispose();
}
public function enginesFiredHandler():void
{
trace( "engines were fired" );
}
}
You can go way beyond this to do parameter passing and all sorts of interesting things. I recommend you check out his blog.
http://flashblog.robertpenner.com
Here is a tutorial on how to get started with AS3 Signals:
http://www.youtube.com/watch?v=wB_sWJ5EgXI

AS3: if-function doesn't listen to a boolean in another class

So... I'm working on a chess-game, and trying to make it so that a "public static boolean" (turn) dictates which player can make a move. This boolean is in a class (Board.as) which imports all the classes for all the chess-pieces (e.g. QueenW.as (for the White Queen)).
I've tried multiple ways: Trying to make functions not run anymore, and replacing the pieces (which are buttons) to other objects (non-clickable movieclips). Decided to go with the latter. I've traced the boolean in a chess-piece class, as well as the Board-class, in an ENTER_FRAME function. Both seem to trace it correctly when the value changes.
Problem is: Flash doesn't remove the chess-pieces and replaces them with a non-clickable object, even though the class in which it should happen (Board.as) does listen to the boolean when tracing. Anybody knows a solution?
A little piece of my code, which is relative to the problem:
Board class (which is the Documentclass for my .fla file)
package
{
import QueenWclass; //imports the class used for example.
public class Board extends MovieClip
{
public static var turn:Boolean = new Boolean; //creates public static bool.
var queenW:QueenWclass = new QueenWclass(); //creates aforementioned chess-piece.
var queenWnoturn:QueenWnoturn = new QueenWnoturn; //creates a non-clickable object.
}
public function Board()
{
turn = true;
this.addEventListener(Event.ENTER_FRAME, frameEnter);
addChild(queenW); //adds pieces to the screen.
}
if (turn == true)
{
}
if (turn == false)
{
removeChild(queenW); //Removes chess-piece.
addChild(queenWnoturn); //Adds a non-clickable object.
}
}
And my QueenWclass.as class:
package
{
public class QueenWclass extends MovieClip
{
var queenW:QueenW = new QueenW();
}
public function QueenWclass()
{
addChild(queenW);
this.addEventListener(MouseEvent.CLICK, CLICKqueenW);
}
function CLICKqueenW(event.MouseEvent):void
{
Board.turn = false;
}
}
I hope I wrote this example correctly and understandably. There's no real timelimit to my project as I already had to turn it in an hour ago (but still got a 6/10 because of effort and how far I've come with this rather complex game). I just want to finish it for myself... Thanks in advance!
Maybe the code has not been copied correctly or there is a small problem.
This code:
if (turn == true)
{
}
if (turn == false)
{
removeChild(queenW); //Removes chess-piece.
addChild(queenWnoturn); //Adds a non-clickable object.
}
Will only run once, when "Board" is created, it will not run when the state of "turn" changes.
Well, you have nothing that's listening for the boolean's change. The code that's checking the boolean is located in constructor, while the actual change is done in a MouseEvent.CLICK event listener. You have to either implement a function that's called repeatedly via Event.ENTER_FRAME listening, SetInterval(), or TimerEvent.TIMER (with a timer), or implement a publicly available property as a function, that would check which turn is it and do corresponding actions. The latter is a little better, as it works only when something is changed.
private static var _turn:Boolean=false;
public static function get turn():Boolean { return _turn; } // getter part
public static function set turn(value:Boolean):void // setter part
{
if (_turn==value) return; // no need to change turn
_turn=value;
if (_turn) YouGetATurn(); else EnemyGetsATurn();
// this part is what will get called when you change Board.turn
}

Reduce lifecycle validation calls in resizeable Flex LabelItemRenderer

I've subclassed the LabelItemRenderer class to create an expandable renderer for a spark list in my mobile app.
When the user selects an item, the renderer's size increases, additional data is shown. The renderer basically looks like this (I've removed the parts that don't matter here, so this is basically pseudo code).
public class PositionsGridRenderer extends LabelItemRenderer
{
public function PositionsGridRenderer() {
super();
addEventListener(MouseEvent.CLICK, expandHandler);
}
protected override function createChildren():void {
super.createChildren();
_dg = new DataGroup();
_dg.visible = false;
addChild(_dg);
}
private function expandHandler(event:Event):void {
if(_gridVisible) {
if(!_detailClicked) {
_dg.visible = false;
_gridVisible = false;
}
_detailClicked = false;
} else {
_dg.visible = true;
_gridVisible = true;
}
}
public override function set data(value:Object):void {
if(!value) return;
super.data = value;
var pos:Position = data as Position;
label = pos.positionName;
_dg.dataProvider = pos.positionSymbols;
}
protected override function measure():void {
!_gridVisible ? measuredHeight = 30 : measuredHeight = 30 + getElementPreferredHeight(_dg);
this.height = measuredHeight;
}
protected override function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
setElementSize(labelDisplay, unscaledWidth, 30);
setElementPosition(labelDisplay, 10,10);
if(_gridVisible) {
setElementSize(_dg, unscaledWidth, getElementPreferredHeight(_dg));
setElementPosition(_dg, 0, 30);
} else {
setElementSize(_dg, unscaledWidth, 0);
}
invalidateSize();
}
}
}
Works as expected, I'm just wondering if there's a way to reduce the amount of validation calls this renderer does when I expand it.
If it is clicked to be expanded the layoutContents and measure functions are both called three times in the following order: layoutcontents -> measure, layoutcontens -> measure, layoutcontents -> measure.
I'd understand them being called once because I invalidate the size, but three times seems odd.
Does anyone know why this is happening, or maybe even how to prevent this from happening?
The real question was why is the component going through three full renderer cycles? After some disussion, this is what we came across:
The first time the invalidate cycle is triggered is when a mouse down, or possibly a touch begin event occurs. This puts the component into the hover state; which causes a visual change in the component.
The second time the invalidate cycle is triggered is when the item is selected. This puts the renderer in the down state; causing a different visual indicator to be drawn.
The third invalidate cycle is caused by the component's own code; when layoutContents() calls invalidatesize()

AS3 reflection. How to find out if a method is overridden?

Is it possible to use AS3 reflection to find out if a method was overridden?
I need a method like:
protected function isOverriden(methodName:string) : bool
{
//magic here!
//...
return awesomeLocalVariable;
}
So, I pass in the method name as a string and the isOverridden method yields true only if and only if the object has a method of that name and it is overridden from its original implementation.
Any idea on how to code the magic there?
Thanks.
Edit: As requested, the context of the problem:
I'm building a framework for creating AS3 games. I want to provide "components" for my game objects, each component provides functionality to the game object it is applied. Components are based on events (onClick, onUpdate, onShapeCollision, etc) I need this code in the Component class, so I can register only the events that the actual Component-derived class implements (overrides).
Example component:
public class CTrace extends ScriptComponent
{
public override function onClick(event:MouseEvent = null):void
{
trace(Owner.Id);
}
}
The framework should register the onClick method as the event handler for the MouseEvent.CLICK event because it overrides the default implementation.
Why do I need the default implementation? Because I want the classes to override the supported methods so there will be a compile time error if the user tries to use an unsupported event.
Does that makes sense?
Here is a try. The function is static and it may be used to check any class or object regardless of the class in which it is implemented. If you give it the type, it will use it, if you give it an instance, it will get the type by itself. The inner logic is just to check the given type description for the function we are looking for, if such exists and is declared by the class, it will check if the method also exists in the parent. And if both exists, enjoy, it means it is overridden.
/**
* Returns true only if the method name given is declared by
* the source class, and any parent class.
*/
static public function isOverridden(source:*, methodName:String):Boolean {
var parentTypeName:String = getQualifiedSuperclassName(source);
if (parentTypeName == null) {
return false;
}//if
var typeName:String = getQualifiedClassName(source);
var typeDesc:XML = describeType(getDefinitionByName(typeName));
var methodList:XMLList = typeDesc.factory.method.(#name == methodName);
if (methodList.length() > 0) {
//Method exists
var methodData:XML = methodList[0];
if (methodData.#declaredBy == typeName) {
//Method is declared in self
var parentTypeDesc:XML = describeType(getDefinitionByName(parentTypeName));
var parentMethodList:XMLList = parentTypeDesc.factory.method.(#name == methodName);
return parentMethodList.length() > 0;
}//if
}//if
return false;
}//isOverridden
And just in case it is needed, the imports required for it to work:
import flash.utils.describeType;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
import flash.utils.getQualifiedSuperclassName;
And to use it:
trace(isOverridden(ChildrenClass, "overriddenMethod")); //true
trace(isOverridden(ChildrenClass, "onlyChildMethod")); //false
trace(isOverridden(ChildrenClass, "onlyParentMethod")); //false
If you are asking inside the same object you can
overriden = (this[stringNameOfMethod] instanceOf Function && super[stringNameOfMethod] instanceOf Function);
If not, try using describeType. Check if there's a method with the name and check the "declaredBy" attribute. Voila!
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/utils/package.html#describeType()
Sorry, I'm in the middle of a migration from CS3 to Flash builder so for now I cannot ensure my ideas work correctly. But I will be back.

Flash: Listen to all events of a type with one eventlistener

It's not a matter of life or death but I wonder if this could be possible:
I got a couple of events from one type of custom event (FormEvent) now I got a FormListener that listens to all those events and handles them according to the event type. Instead of adding one eventListener at the time I wish to add all events at once.
so now it looks like this:
private function addListeners():void {
addEventListener(FormEvent.SHOW_FORM, formListener);
addEventListener(FormEvent.SEND_FORM, formListener);
addEventListener(FormEvent.CANCEL_FORM, formListener);
}
private function formListener(event:formEvent):void {
switch(event.type){
case "show.form":
// handle show form stuff
break;
case "send.form":
// handle send form stuff
break;
case "cancel.form":
// handle cancel form stuff
break;
}
}
but instead of adding every event one at the time I would rather be doing something like
private function addListeners():void {
addEventListener(FormEvent.*, formListener);
}
I wonder if something like this is possible, i would love it. I work with loads of events :)
You only really need one event listener in this case anyhow. That listener will be listening for any change with the form and a parameter equal to what the change was becomes available to the event listener function. I will show you, but please remember that this is a pseudo situation and normally I wouldn't dispatch an event off of something as simple as a method call because the dispatch is implied so there is no real need to listen for it.
First the Custom Event
package com.yourDomain.events
{
import flash.events.Event;
public class FormEvent extends Event
{
//Public Properties
public static const CANCEL_FORM:int = "0";
public static const SHOW_FORM:int = "1";
public static const SEND_FORM:int = "2";
public static const STATE_CHANGED:String = "stateChanged";
//Private Properties
private var formState:int;
public function FormEvent(formState:int):void
{
super(STATE_CHANGED);
formState = formState;
}
}
}
So we have just created our custom event class and we have set it up so that we can catch the state through the listener function as I will demonstrate once done with the pseudo form class that will dispatch the for said custom event.
Remember that this is all hypothetical as I have no idea what your code looks like or how your implementing things. What is important is to notice that when I dispatch the event I need to send a parameter with it that reflects what the new state is.
package com.yourDomain.ui
{
import flash.events.Event;
import flash.events.EventDispatcher;
import com.yourDomain.events.FormEvent;
public class Form extends EventDispatcher
{
public function Form():void
{
//Anything you want form to do upon instantiation goes here.
}
public function cancelForm():void
{
dispatchEvent(new Event(FormEvent.CANCEL_FORM);
}
public function showForm():void
{
dispatchEvent(new Event(FormEvent.SHOW_FORM);
}
public function sendForm():void
{
dispatchEvent(new Event(FormEvent.SEND_FORM);
}
}
}
And finally we create the document class that will listen for it. Please know that I realize it isn't logical to create a listener that fires when you call a method of a class because you obviously know you called the method, but for this example it will due.
package com.yourDomain.ui
{
import com.yourDomain.ui.Form;
import com.yourDomain.events.FormEvent;
//Form is in the same package so we need not import it.
public class MainDocumentClass
{
private var _theForm:Form;
public function MainDocumentClass():void
{
_theForm = new Form();
_theForm.addEventListener(FormEvent.STATE_CHANGED, onFormStateChange, false, 0, true);
/*
The following three method calls each cause the
FormEvent.STATE_CHANGE event to be dispatched.
onFormStateChange is notified and checks what
the last change actually was.
*/
_theForm.cancelForm();
_theForm.showForm();
_theForm.sendForm();
}
private function onFormStateChange(e:FormEvent):void
{
switch(e.formState)
{
case CANCEL_FORM:
trace('The form was canceled');
break;
case SHOW_FORM:
trace('The form was revealed');
break;
case SEND_FORM:
trace('The form was sent');
break;
}
}
}
}
I hope that this was helpful, its late and I may have to revise some things later, but this should help get an understanding of how to make your own events and to customize how things work.
I don't know of any routines that let you do that directly, but you could write your own. The syntax here won't be perfect, but here's a first pass:
private function addMultipleEventListeners( evts:Array, callback:function ):void
{
for each( var evt:Event in evts )
{
addEventListener( evt, callback );
}
}
You could then call that routine like so:
var evts:Array = [ FormEvent.SHOW_FORM, FormEvent.SEND_FORM, FormEvent.CANCEL_FORM ];
addMultipleEventListeners( evts, formListener );