Sunday, 19 December 2010

MouseButton and HandCursor

Every now and then you may need to use a Sprite or MovieClip with a EventListener. And chances are that 99% of these times it will be a MouseEvent, since these objects are easy to place in custom classes and build an entire interface with Actionscript... at least that will be 100% of the times for me ;)

So in these cases you don't want to make Button objects, but still be able to feedback the user that in "this" object contains an action with the mouse.

Well simply placing three lines of code you can show your user the hand cursor (like it appears in a web page), and have the behaviour of a button. Furthermore the third line prevents the hand cursor not to be active if the button has text in it. Those lines go like this:


var button:MovieClip = new MovieClip();
button.buttonMode  = true;
button.useHandCursor = true;
button.mouseChildren=false;
addChild(button);

Cheers

Tuesday, 12 October 2010

How to count number of lines in a TextField

I was facing this dilema to correct a game were in the textfields the text can have 1, 2 or 3 lines. Depending on the number of lines I wanted to center the text vertically so that when it had only 1 line, it would append a new line "\n" before the text.

This only applies to normal dynamic textfields, not TFL. These have their own methods to center text both horizontally and vertically... much easier.

There's a property called numLines, but this only states that the chosen TextField will have an x number of lines that you specify, you can't use it to return the number of text lines that you see.

So you have to make another type of calculation, and the textfield must be multiline.

var myHeight:Number = myText.height; //store the height of the textfield
myText.multiline = true; //don't need to do this, if it is in the properties panel
myText.wordWrap = true; //same as above

myText.height = myHeight; // here is the trick, try removing this line
trace("DYNAMIC TEXTFIELD HAVE "+myText.maxScrollV+" LINES");

Cheers

Time lapse for FLVPlayback

This is one of those cases when the Adobe documentation is rare, or doesn't help much.

I had a hard time figuring out how to place a bar that would grow along with the playback of a movie in the FLVPlayback component.

First make a custom MovieClip with a rectangle (it can be another shape, but usually rectangles are best for these) and give it a name: myPlayhead

You must have already the FLVPlayback component on stage, with a given name. I will name it: myFLVplayback

For the script we will need to import a class to do this: VideoEvent. This class has several events you can use to get info from the FLVplayback when playing video (http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/fl/video/VideoEvent.html)

import flash.video.VideoEvent;


//The movie doesn't start
myFLVPlayback.stop();


//Rewinds automatically if the video is stopped or it reaches the end.
//By default it's false
myFLVPlayback.autoRewind=true;


//Adds the listener for each time the playhead updates it's position

myFLVPlayback.addEventListener(VideoEvent.PLAYHEAD_UPDATE, videoHandler);


function videoHandler(evt:VideoEvent):void{
var totalSeconds:Number = myFLVPlayback.totalTime;
var elapsedSeconds:Number = myFLVPlayback.playheadTime;

var percent:Number=Math.round((elapsedSeconds/totalSeconds)*100);
        
        //The custom seekBar will change it's size according the the percentage
        //of the played movie
seekBt.progressMc.scaleX=percent;
}


Cheers

Custom controls for the FLVPlayback video component

This time I had to help a friend that needed to play some videos in a fancy interface.

The problem was that he didn't want the buttons that Flash provides for video playback, and wanted some flexibility towards mouse events, i.e., beeing able to hide or show the buttons with custom events or mouse events.

For now I will tell how I changed the controls.

First of all you need to make some custom MovieClips for the buttons. One MovieClip per button.

(this is valid for actionscripting the interface, or placing the components directly on stage)

Place the FLVplayback component in the stage and give it a name: myFlvplayback

Place the buttons where you want on the interface, and give them a name: playBt, pauseBt, stopBt,... and so on.

Now the script. You have to associate the buttons to the video component, stating what each button will do, like this:


myFlvplayback.playButton=playBt;
myFlvplayback.stopButton=stopBt;
myFlvplayback.seekBar=seekBt;
myFlvplayback.pauseButton=pauseBt;

That's it. If you run the movie, you'll see that this is enough to custom controls for this video component.

A further step is to make a custom interface with the all buttons inside a single MovieClip. Then you can make your custom autohide function with some MouseEvents.

Cheers

Thursday, 2 September 2010

Logical XOR

Flash doesn't have a logical XOR, only a bitwise XOR. That means that you can only do bitwise operations, or swap number and attribute the result to a variable.

Well I needed a Boolean XOR, like the one with Truth Tables that we know from logical programming. Well this little function takes care of this:

public function xor(lhs:Boolean, rhs:Boolean):Boolean {


return !( lhs && rhs ) && ( lhs || rhs );

}



Cheers,
Leonel

Wednesday, 1 September 2010

Fullscreen... and back

To get your stage into full screen you must do in two steps. The first is to set your stage display state in ActionScript. The other is to allow this scaling in the HTML page.

First of all, place a MovieClip in the stage, by code, or directly in the stage. Give it a instance name.
I will cal mine: fullScr

Associate with fullScr an event listener to catch a mouse event to execute the full screen:

fullScr.addEventListener(MouseEvent.CLICK, handleMouseClick);

Write the handleMouseClick function that will set the display state of the stage. This function will detect the current display state, and change it accordingly. If the display state is NORMAL, it will set it to FULLSCREEN. Otherwise it will be NORMAL again:


private function handleMouseClick(me:MouseEvent):void
{
   if (stage.displayState == StageDisplayState.NORMAL) {
     try{
        stage.displayState=StageDisplayState.FULL_SCREEN;
     }
     catch (e:SecurityError){
        //if you don't complete STEP TWO below, you will get this SecurityError
       trace("an error has occured. please modify the html file to allow    
       fullscreen mode");
     }
   }else {
  try{
       stage.displayState=StageDisplayState.NORMAL;
     }
     catch (e:SecurityError){
       //if you don't complete STEP TWO below, you will get this SecurityError
       trace("an error has occured. please modify the html file to allow 
       fullscreen mode");
     }
  }
}

If you have a video component like FLVPlayback, when you hit fullscreen, this component will take over the screen because it is on top of everything else. To prevent this add this line:

myPlayer.fullScreenTakeOver=false;

Cheers,
Leonel

Wednesday, 18 August 2010

Datagrid with image and events

At my company I'm making an application that has many datagrids to manage information that comes from external XML files. Many are related to one another, and some of them are meant to choose online documents, that will be used by chosen users (also in datagrids). One of the requests was to be able to choose a document in the dataGrid and preview the document.


The first question was, what type of visual aid would there be for the user to see that "you can click here to preview the document"?


Since this is a Flash application, there was not much sense to use text aids, like a link in HTML, so I wanted to place an icon for the user to click.


The next question is: How do you place an image in a dataGrid cell?


Not a trivial one to answer, since the Adobe documentation is not very clear in some aspects, and is incomplete in most cases, and this is one of them.


You can change the cell rendering option using the cellRenderer class. But how do you do it?


After long research over the internet, most users and cases are documented in AS2 using the mx.core.UIComponent. In AS3 is the fl.core.UIComponent, but I stumbled in poor documentation.


So searching a bit further I discovered an example from Adobe itself, and it's in this webpage:
http://www.adobe.com/devnet/flash/quickstart/datagrid_pt3/


I tried to adapt this solution to my problem, and it looked promising, since it was in AS3, and it was exactly what I wanted. Guess what? No errors, but it didn't work either. I changed all variables, made the associations right, but some new classes, new MovieClips in the library... but nothing worked.


Adobe forums... nothing!


Finnaly, I found forum entry in Kirupa.com, and the user EssencLord, presented a simple solution to overcome this problem. His solution was exactly whath I was looking for.




1. Make a separate class and give it a name that will identify it has a cell renderer class. I called it MCCellRenderer, because it will use a MovieClip with an icon inside, to be displayed in the dataGrid:

package src{

import fl.controls.listClasses.CellRenderer;
import flash.utils.getDefinitionByName;

public class MCCellRenderer extends CellRenderer {
        public function MCCellRenderer():void {


               var instance = new (getDefinitionByName("eye")); // Create Instance of "eye"
               this.addChild(instance);    // Add this instance to the CellRenderer
               instance.name = "eye";    // Name this new instance
           
               // Position Movie in Cell
               instance.x += 18;
               instance.y += 10;
        }
}





2. Then in the main class, associate this class with the cell you want in the dataGrid:

var verCol:DataGridColumn = new DataGridColumn("Ver");
verCol.cellRenderer = MCCellRenderer;

If you want to associate a MouseEvent with that particular cell, you can do it like this:

1.Add an event listener do the datagrid:
MyDataGrid.addEventListener(MouseEvent.CLICK, onClickCell);


2. And here's and function that will detect which cell was clicked, and react accordingly:


function onClickEvent (E:Event){
// if a record is selected
if (eventGrid.selectedIndex != -1)
{
// Check if user clicked on the Info Icon CellRender of the selected Row
var Row = eventGrid.selectedIndex;
// get selected row
var Col = eventGrid.getColumnIndex("Ver");
// the column of cells with MCCellRenderer
if (E.target == eventGrid.getCellRendererAt(Row, Col))
MyFunction ();

}
}


Keep it simple

Friday, 13 August 2010

Function Call Back

This is very handy feature when programming in AS3.


Imagine that you make a AlertBox class, that when called, it makes a custom Alert message. Imagine then, that you can put 1, 2 or 3 buttons, according to the message type. 


If you want to reuse this class in several projects, it's a good idea to tell the different buttons what or where to return when it's pressed.


That's were the Function Call Back enters. After a couple of hours and some search in the web, I came up with a solution, which I want to share.


It's very simple.
First declare in your class a variable that will keep the function reference later on, like this:

private var callBack:Function;

Then when you instantiate the Class, one of the arguments, of the constructor or function, will keep the function name, like this:

createAlert(..., callback){
...
}

Finally inside the constructor or function, you have to attribute the argument to the variable you defined earlier, like this:

callBack=callback;

Then place this variable as a call of some event, like this:

button1.addEventListener(MouseEvent.CLICK, callBack);

... and that's it.


If you want to call the call back function with arguments, instead of a event, just call this way:


typeOfMessage=1;


callback(typeOfMessage);

Sunday, 30 May 2010

Cloning arrays

On the other day I had the need to copy an Array. It was not the first time, but this time I had to make a temporary copy of another array, and put some new data in it and make some calculations.

In this process my need to change the original Array, was none.

The problem is that if you make something like this:

var myArray:Array=new Array("Blue", "Yellow");
var myCopyArray:Array=new Array();


myCopyArray=myArray;

... it works to some extent. You can also do this with concat or slice commands.
This method makes a shallow copy, which means that the copy only stores the reference to the original values, that is, it only stores the memory reference where the values are stored. This means that if you change one of the arrays, the other will change also... This was not what I meant.

I needed to make a hard copy of the array, change it, without changing the original. During the research, I found this generic solution presented in a webpage by Adobe at the following link:
https://www.adobe.com/livedocs/flash/9.0/main/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00000092.html

As always I transcript the code to here and make a brief explanation.

So the function looks like this:

import flash.utils.ByteArray;

function clone(source:Object):*
{
    var myBA:ByteArray = new ByteArray();
    myBA.writeObject(source);
    myBA.position = 0;
    return(myBA.readObject());
}
Basically what this does is it goes to the memory and gets what is stored in the place where the original Array is referenced. To get the original data and not the reference, it uses the ByteArray class and stores the data in the myBA:ByteArray object. The ByteArray class insures that it copies whatever is stored in the original Array, i.e, Numbers, String, DisplayObjects, whatever...

Pretty handy...

Friday, 21 May 2010

How to erase a element in a Array

I came across this problem recently.


The Array has some methods, and the most common are PUSH, that lets you put an element in a Array. This element will go to the end.


And then you have the POP method, but this only gets the last element in a Array.


Now the question is "How do I delete an element giving the position for that element"?


It's a bit trickier, and it's done using another method called SPLICE.


The syntax is:
splice(startIndex:int, deleteCount:uint... values)


This means:
- startIndex -> Initial position were you want to delete. It's the index position of the Array;
- deleteCount ->This is how many elements you want to delete, that includes the index position you specified in startIndex. If you say 1, then it will delete the index position that you've specified. If you say 2, then it's the index position, and the next element... and so on.


As an example:


var myList:Array=new Array("Blue", "Red", "Green", "Black,"White");


myList.splice(2,2);
trace(myList); //Blue, Red, White


//Green is Index number 2, and then deletes two elements including Green, which are Green and Black

Tuesday, 4 May 2010

Instantiate an SWF as a Class

At the company where I work we add the need to make several SWF that needed to come up, in different timings and according to user navigation. But since we wanted to keep the final application as light as possible, we had the need not to instantiate all the external swf at once.


After some research we found and implemented the following solution: instantiate external swf's as separate classes:


package{
  import flash.display.MovieClip;
  import flash.events.Event;
  import flash.net.URLRequest;
  import flash.display.Loader;
  
  public class Load extends MovieClip
  {
     var storeLoadedObject:Class; //Self explained ;)
     var loader:Loader;
    
     public function Load():void{
         loader=new Loader();
         loader.load(new URLRequest("example.swf"));
         loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeObjForEdit);
     }


     public function completeObjForEdit(evt:Event):void{
         var className:String="com.gameRoot"; //Document class of loaded file
          storeLoadedObject=loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
        makeLotsOfCopies();
     }


     public function makeLotsOfCopies():void{
        for(var i:uint=0; i<20;i++){
           var copy:MovieClip=new storeLoadedObject();
           addChild(copy);
           copy.x=i*5;
           copy.y=i*5;
        }
     }
  }
}


And that's it.

Sunday, 25 April 2010

Video smoothing in AS3

This is an issue I found that has made serious difficulties to developers  about video playback. If you have a video file in flv format, and it has a size that is smaller than the stage where you want to show it in, Flash pixelizes the video. This is the default behaviour of Flash when playing video. 

There's a workaround but it's a direct one, as it was in AS2.

In AS2 you would do like this:

myVideoObject.smoothing = true | false

In AS3 you have to get to the "smoothing" in other way. Like this:
 
videoLoader.getVideoPlayer(videoLoader.activeVideoPlayerIndex).smoothing = true; 
 
 Where videoLoader is the FLVPlayback component that you insert with ActionScript.

Monday, 12 April 2010

How to load an external SWF

This is how you can load an external SWF, using the progress event to make some sort of preloader.



import flash.net.URLRequest;
import flash.display.Loader;
import flash.events.Event;
import flash.events.ProgressEvent;


function startLoad() {
   var mLoader:Loader = new Loader();
   var mRequest:URLRequest = new URLRequest(“MouseActions.swf”);
   mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onCompleteHandler);
   mLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,onProgressHandler);
   mLoader.load(mRequest);
}


function onCompleteHandler(loadEvent:Event) {
   addChild(loadEvent.currentTarget.content);
}


function onProgressHandler(mProgress:ProgressEvent) {
   var percent:Number = mProgress.bytesLoaded/mProgress.bytesTotal;
   trace(percent);
}

startLoad();

Tuesday, 19 January 2010

XML loading class

This article is about loading xml data, on files or webservices. Sometimes, some projects require that you must read lots of information that are scattered throught many xml files or webservice. This class will help you not to recode every time you must read such type of information. Instead you only have to read the data acording to the response you receive.

So if you use this class, you just need to instatiate an object and call the file/webservice. Instantiate a new object for each file/webservice.

This code was copied from the website: http://www.mediareason.com/blog/?p=20

package com.func
{
import flash.net.URLLoader
import flash.net.URLRequest
import flash.xml.XMLDocument;
import flash.errors.*
import flash.events.*
public class LoadXML
{
private var loader : URLLoader;
private var mainXML : XML;
public function LoadXML() {
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest("http://.../test.xml"));
}
private function onComplete(evt:Event)
{
try
{
mainXML = new XML(loader.data)
trace(mainXML);
} catch(e:Error)
{
trace("Error: " + e.message)
return;
}
}
}
}