Jump to content
Sign in to follow this  
betik

As3/mxml: Tips Dan Tutorial

Recommended Posts

oleh kerana AS3 dan MXML semakin mendapat tempat di Malaysia, elok rasanya diadakan satu thread untuk kita berkongsi sebarang tips atau tutorial berkaitan subjek ini(code h4ck juga boleh, tapi tidak digalakkan dipos di sini kerana ia seolah2 melangkaui apa yang sepatutnya dilakukan dengan cara bersih)...jadi mari kita berkongsi sesuatu supaya kita bertambah ilmu...

setiap pos eloklah dinyatakan tahap supaya senang nak buat rujukan...tahap2nya ialah:
- kosong: untuk pos yang ditujukan kepada pengaturcara yang baru sahaja menggunakan AS3/MXML dan masih tak tahu apa2 tentangnya...
- permulaan: untuk pos yang ditujukan kepada pengaturcara yang dalam peringkat permulaan menggunakan AS3/MXML...
- pertengahan: untuk pos yang ditujukan kepada pengaturcara yang dalam peringkat pertengahan menggunakan AS3/MXML...
- akhir: untuk pos yang ditujukan kepada pengaturcara yang sudah tahu secara mendalam dalam menggunakan AS3/MXML...


//kalau boleh, jangan menggunakan penulisan ala SMS...
//gunakan [code] tag
//kalau silap tempat, dapatlah kiranya Mod mengalihkannya ke ruangan bersesuaian... Edited by betik

Share this post


Link to post
Share on other sites
[b][size="4"][u]XML dan AS3(tahap: permulaan)[/u][/size][/b]

ActionScript ialah bahasa pengaturcaraan yang menggunakan dialek ECMAScript...sejak Actionscript 3.0(AS3), ia telah mengikut spesifikasi ECMAScript 4th draft sepenuhnya berbanding ActionScript 2.0(AS2)...
seiring dengan itu, AS3 sekarang menggunakan E4X(ECMAScript for XML) untuk berurusan dengan XML...

anda boleh mendapatkan maklumat asas di internet dengan mudah tentang penggunaan XML didalam AS3...tapi kebanyakannya tidak menyatakan tentang satu ciri yang penting kepada beginner...i.e: kita boleh menggunakan sebarang AS3 statement atau memanggil fungsi semasa melakukan query/untuk XML tersebut...

caranya, cuma masukkan AS3 statement tersebut kedalam kurungan(parentheses)...
kita mulakan dengan sesuatu yang sangat mudah tetapi sangat berguna, iaitu [font="Courier New"][b]trace[/b][/font] statement...

katakan kita ada satu XML mudah yang disimpan kedalam satu pembolehubah [font="Courier New"]_puteranian[/font]:
[code]var _puteranian:XML = <Puteranian>
<Moderator id="0" username="mod0" email="[email protected]" location="Selangor" registerOn="2005/04/17"/>
<Moderator id="1" username="mod1" email="[email protected]" location="Wilayah Persekutuan" registerOn="2008/12/24"/>
<User id="2" username="user2" email="[email protected]" location="Johor" registerOn="2010/02/03"/>
<Moderator id="3" username="mod3" email="[email protected]" location="Johor" registerOn="2010/02/22"/>
<User id="4" username="user4" email="[email protected]" location="Terengganu" registerOn="2010/08/24"/>
<User id="5" username="user5" email="[email protected]" location="Johor" registerOn="2010/10/17"/>
<User id="6" username="user6" email="[email protected]" registerOn="2005/10/28"/>
<User id="7" username="user7" email="[email protected]" location="Melaka" registerOn="2009/11/01"/>
<User id="8" username="user8" email="[email protected]" location="Selangor" registerOn="2010/06/03"/>
</Puteranian>[/code]
kita hanya ingin trace user id bagi setiap puteranian yang berada di Johor...
katakan anda menggunakan seprti ini:
[code]trace(_puteranian.children().(attribute( "location" )=="Johor").attribute("id"));
[/code]
hasil trace akan mengeluarkan satu string yang berturutan(i.e: 235) dan menyukarkan kita melakukan debugging..jadi anda terfikir untuk menambah [font="Courier New"]for[/font] loop hanya untuk tujuan debugging...
[code]var x1:XMLList = _puteranian.children().(attribute( "location" )=="Johor").attribute("id");
for(var i:uint=0; i<x1.length(); ++i){
trace(x1[i]);
}[/code]
alternatively, kita boleh lakukan sebegini:
[code]_puteranian.children().(attribute( "location" )=="Johor").(trace(attribute("id")));
/*trace result: 2
3
5
*/[/code]
jadi kita boleh melakukan macam2 lagi seperti:
[code]
_puteranian.children().(attribute( "location" )=="Johor").( @["location"]="Johoq" ); //tukar lokasi 'Johor' kepada 'Johoq'
_puteranian.children().(attribute( "location" )=="Johor").(delete parent().children()[childIndex()]); //buang budak2 Johor dari menjadi ahli Putera...haha
[/code]

dan katakan anda hanya ingin mendapatkan XMLList untuk puteranian bertaraf USER yang sudah berdaftar lebih dari 6 bulan...
dan kita tahu untuk melakukan demikian, ia tidak semudah dengan hanya melakukan query biasa terhadap XML tersebut...
jadi kita boleh lakukan begini:
[code]
var _tempXML:XMLList = _puteranian.children().(name()=="User" && elapseMonth(attribute("registerOn"))>6);
trace(_tempXML);
/*trace result: <User id="2" username="user2" email="[email protected]" location="Johor" registerOn="2010/02/03"/>
<User id="6" username="user6" email="[email protected]" registerOn="2005/10/28"/>
<User id="7" username="user7" email="[email protected]" location="Melaka" registerOn="2009/11/01"/>
*/

function elapseMonth(startDate:String):uint{
var d1:Date = new Date(startDate);
var d2:Date = new Date();
var dateDiff:Date = new Date(d2.time - d1.time);

dateDiff.date += 1;
dateDiff.fullYear -= 1970;
return (dateDiff.fullYear*12)+dateDiff.month+1;
}[/code]

dan ia juga berguna untuk mendapatkan result yang tidak berulang...contohnya, setiap puteranian dianggap orang yang sama sekiranya menggunakan email yang sama...kita mahu mendapatkan semua ahli putera yang unik sahaja...kita boleh lakukan begini:
[code]
var _uniqueMemberList:XMLList = new XMLList();
_puteranian.children().(_uniqueMemberList = getUniqueList(attribute("email"),_uniqueMemberList));
trace(_uniqueMemberList);
/*trace result: <Moderator id="0" username="mod0" email="[email protected]" location="Selangor" registerOn="2005/04/17"/>
<Moderator id="1" username="mod1" email="[email protected]" location="Wilayah Persekutuan" registerOn="2008/12/24"/>
<User id="2" username="user2" email="[email protected]" location="Johor" registerOn="2010/02/03"/>
<Moderator id="3" username="mod3" email="[email protected]" location="Johor" registerOn="2010/02/22"/>
<User id="4" username="user4" email="[email protected]" location="Terengganu" registerOn="2010/08/24"/>
<User id="6" username="user6" email="[email protected]" registerOn="2005/10/28"/>
<User id="8" username="user8" email="[email protected]" location="Selangor" registerOn="2010/06/03"/>
*/

function getUniqueList(val:Object, xmlList:XMLList):XMLList{
if(!xmlList.attribute(val.name()).contains(val)){
xmlList += val.parent();
}
return xmlList;
}[/code]

pendek kata, feature ini sangat berguna apabila kita ingin berurusan dengan XML... Edited by betik

Share this post


Link to post
Share on other sites
[u][b][size="5"]AS3/mxml(tahap: permulaan)[/size][/b][/u]

aku pernah kena buat satu custom colour picker untuk projek aku...Flex punya color picker agak haprak sebab takde mode yang ada color spectrum tu...sebab Flex kedekut sangat dia punya advanced color picker, kita kena buat custom...
ada 3 jalan penyelesaian:
1- beli component atau cari free component/class...tapi yang jumpa semuanya ada limitation penting(e.g: error bila guna atas flex/bersaiz bagak/slow tahap akar/etc)...dan yang paling penting, tak dapat buat localization...
2- subclass ColorPicker yang dah ada dalam flex...tapi skin parts dia menyebabkan kita punya published swf bosar...dan tak boleh re-used atas pure as3
3- bela kuku kaki sambil buat color picker sendiri...

aku ambik jalan penyelesaian #3 iaitu bela kuku kaki...

jadi, daripada benda tu berabuk takde guna, elok kiranya aku berkongsi kat sini...
WARNING: class ni pun banyak limitation dan jugak tahap kaki...5jam setengah punya kerja memang macam ni jadinya :D

cara guna:
- as3: kegunaan biasa dah ada dalam READ_ME.txt dan ColorPickerExample.as..selebihnya boleh explore :D

-mxml:
cara paling senang,
1) kasi ejas sikit kat class com.taukala.colorPicker.ColorPicker.as...ColorPicker tu extends Sprite, tukar dia extends UIComponent...
2) guna dia pakai as3 atau pun mxml...as3 semua orang dah tahu, so contoh untuk mxml:[code]
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:tk="com.taukala.colorPicker.*"
minWidth="500" minHeight="400"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
import com.taukala.colorPicker.event.ColorEvent;
import com.taukala.colorPicker.util.ColorUtil;

private function onCreationComplete():void{
cp.switchToProLabel = "Hantam saja la";
cp.switchToEasyLabel = "Adoii";
cp.panelAnchor = this as DisplayObject;
}

private function onColorChange(e:ColorEvent):void{
label.text = "kaler da tukar jadi: "+ColorUtil.toColorString(e.newColor);
}

private function onPreviewColorChange(e:ColorEvent):void{
label.text = "kaler prebiu: "+ColorUtil.toColorString(e.newColor);
}

]]>
</fx:Script>
<tk:ColorPicker id="cp" colorChange="onColorChange(event)" previewColorChange="onPreviewColorChange(event)"
rollOverColor="0x00CC00" selectedColor="0xA34C26" top="10" left="10"/>
<s:Label id="label" bottom="0"/>
</s:Application>[/code]

4) list of properties/events: ada dalam READ_ME.txt...dan ada banyak jugak tak letak...yang biasanya akan digunakan je aku letak...aku juga seorang pemalas :D
3) kalau nak tukar skin, kena kasi tukar sendiri....all class included...

walaupun class ni cuma simple2 je, mungkin dapat membantu kalau nak cepat dan memerlukan sesuatu yang simple sahaja...
dan jangan bimbang...class tu adalah hakmilik penuh aku...walaupun dibuat untuk memenuhi projek client aku, tapi yang colorpicker ni tak termasuk dalam dia punya properiatry...
(kecuali ada satu package yang digunakan untuk membuat color spectrum(di ambil dari [url="http://mykhel.blogspot.com/2010/06/color-spectrum-chart-as3.html"]sini[/url]), walaupun colorspectrum class tu free, tapi sila ikut dia punya term)...

[url="http://taukala.com.my/bytes/colorpicker/cp-sample.html"]SAMPLE[/url]
[url="http://taukala.com.my/bytes/colorpicker/SimpleColorPicker.rar"]SOURCE FILE[/url] Edited by betik

Share this post


Link to post
Share on other sites
[u][b][size="5"]Event dan EventDispatcher(tahap: kosong)[/size][/b][/u]

ada orang tanya, mana post untuk tahap kosong? macam mana nak belajar kalau takde benda2 asas?...
jadi kita mulakan dengan sesuatu yang akan digunakan secara meluas dalam event-driven application iaitu [url="http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/events/Event.html"]Event[/url] dan [url="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/EventDispatcher.html"]EventDispatcher[/url]...

asasnya, semua class perlu subclass EventDispatcher supaya dapat menghantar event2(e.g: DisplayObject, sesuatu yang sangat asas, merupakan class yang extends EventDispatcher)..

tapi bagaimana kalau kita nak supaya Event kita mengandungi data selain data yang typical dalam sesuatu event(e.g: type, currenttarget, etc)?
satu-satunya cara: kita perlu membuat satu custom event yang subclass flash.events.Event/event lain...
contohnya, kita mempunyai satu class yang menghantar event DATA_COMPLETE...event itu akan mempunyai data lengkap yang kita sudah susun ke dalam satu Array
jadi kita boleh membuat sebegini:
[code]package com.test.events{
import flash.events.Event;

public final class CustomEvent extends Event{
public static const DATA_COMPLETE:String = "dataComplete";
private var _value:Array;

public function CustomEvent(value:Array, type:String, bubbles:Boolean=false, cancelable:Boolean=false) {
super(type,bubbles,cancelable);
_value = value;
}

public function get value():Array{ return _value;}
}
}[/code]

kita ada 1 class Test (extend EventDispatcher) yang akan menghantar event tersebut...jadi kita boleh menghantar sebegini:
[code] this.dispatchEvent(new CustomEvent(new Array(1,2,3), CustomEvent.DATA_COMPLETE)); [/code]

dan mendengar event tersebut:
[code]var t:Test = new Test();
t.addEventListener(CustomEvent.DATA_COMPLETE, onDataComplete, false, 0, true);
function onDataComplete(e:CustomEvent):void{
trace("length of array: "+e.value.length);
}[/code]

note: disamping menggunakan removeEventListener explicitly, selalu gunakan weak reference = true ketika mendengar sesuatu event...

bagaimana pula dengan static function nak hantar event?...sila lihat post seterusnya... Edited by betik

Share this post


Link to post
Share on other sites
[size="5"][u][b]static function dan EventDispatcher(tahap: kosong)[/b][/u][/size]

bagaimana dengan static function didalam sesebuah class ingin menghantar event?...sebagaimana kita tahu, kita tidak dapat boleh buat macam contoh dari pos diatas...
dan anda akan bertanya, kenapa static function nak hantar event?...kan elok dia return terus data tu?...
sesetengah senario memerlukan kita berbuat demikian...sebagai contoh yang hampir praktikal (yang aku dapat fikir sekarang :D):

aku ada buat class nama CSVParser yang digunakan untuk parse csv data(berbentuk string) ke dalam bentuk xml...class tu ada 1 static function untuk membuat perkara tersebut...jadi kita akan gunakan function tu sebagai contoh...

katakan tiba-tiba, kita bukan saja hendak parse data dari string yang sudah ada, tapi hendak memuatturun csv data dari remote file dan parse data tersebut kemudiannya...untuk calss itu kelihatan kemas dan mudah digunakan, kita mahu meletakkan satu static function bernama loadAndParse() ke dalam class tersebut...tapi, macam mana nak hantar event nak beritahu csv file tu sudah siap dimuatturun dan diparse?...

jadi, kita boleh buat satu static EventDispacther yang bertindak sebagai penghantar event tersebut, dan beberapa static function yang bertindak sebagai penolong(supaya kita dapat menulis listener seperti class2 biasa)...

kita mulakan dengan constructor class tu...kita hanya mahu class itu digunakan sebabagi static class(bukan singleton ;)) :
[code]public function CSVParser(){
throw new IllegalOperationError("CSVParser class is used as static class so it must not be instantiated.");
}[/code]

dan buat satu function untuk load csv tersebut...function itu akan return immediately(sementara csv itu di-load dan di-parse)...
[code]public static function loadAndParse(url:String):void{
var urlStream:URLStream = new URLStream();
urlStream.addEventListener (SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, true);
urlStream.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
urlStream.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
urlStream.load(new URLRequest(url));
}[/code]

sekarang apa yg kita perlukan?...1 static EventDispatcher..
[code]private static var _dispatcher:EventDispatcher;
public static function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
if(!_dispatcher) _dispatcher = new EventDispatcher();
_dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}

public static function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void{
if(!_dispatcher) _dispatcher.removeEventListener(type, listener, useCapture);
}

public static function hasEventListener(type:String):Boolean {
if(!_dispatcher) return false;
return _dispatcher.hasEventListener(type);
}[/code]

dan dalam event2 listener kita, kita akan hantar 1 custom event memberitahu tentang apa dah jadi kepada pendengar...
kita perlukan 1 custom event class...jadi, dengan mengaplikasikan custom event dari pos di atas:
[code]package com.test.events{
import flash.events.Event;

public final class CSVParserEvent extends Event{
public static const PARSE_COMPLETE:String = "parseComplete";
public static const PARSE_ERROR:String = "loadError";
public static const LOAD_ERROR:String = "loadError";
public static const READ_ERROR:String = "readError";

private var _parsedXML:XML;

public function CSVParserEvent(parsedXML:XML, type:String, bubbles:Boolean=false, cancelable:Boolean=false){
super(type,bubbles,cancelable);
_parsedXML = parsedXML;
}

public function get parsedXML():XML{
return _parsedXML;
}
}
}[/code]

dan kita hantar event2 tu...contohnya semasa IOError event:
[code]private static function onIOError(e:IOErrorEvent):void{
removeURLStream(e.currentTarget as URLStream); //buat satu function untuk buang semua listener dari URLStream kita tadi
if(_dispatcher) _dispatcher.dispatchEvent(new CSVParserEvent(null, CSVParserEvent.LOAD_ERROR));
}[/code]

jadi kita punya full class akan nampak macam ni:
[code]package com.test{
import com.test.events.CSVParserEvent;

import flash.errors.IllegalOperationError;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;
import flash.net.URLRequest;
import flash.net.URLStream;
import flash.utils.ByteArray;

public final class CSVParser{
public function CSVParser(){
throw new IllegalOperationError("CSVParser class is used as static class so it must not be instantiated.");
}

private static var _dispatcher:EventDispatcher;
private static const CSV_REGEX:RegExp = /,(?=(?: [^\"] *\" [^\"] *\")* (?![^\"]*\"))/gx;

public static function loadAndParse(url:String):void{
var urlStream:URLStream = new URLStream();
urlStream.addEventListener (SecurityErrorEvent.SECURITY_ERROR, onSecurityError, false, 0, true);
urlStream.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);
urlStream.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
urlStream.load(new URLRequest(url));
}

private static function onComplete(e:Event):void{
var urlStream:URLStream = e.currentTarget as URLStream;
if(urlStream.bytesAvailable>0){
var b:ByteArray = new ByteArray();
var str:String = null;
try{
urlStream.readBytes(B);
str = b.toString();
}catch(err:Error){
if(_dispatcher) _dispatcher.dispatchEvent(new CSVParserEvent(null, CSVParserEvent.READ_ERROR));
return;
}finally{
removeURLStream(urlStream);

b.clear();
b = null;
}

var xml:XML = parse(str);
if(_dispatcher) _dispatcher.dispatchEvent(new CSVParserEvent(xml, xml? CSVParserEvent.PARSE_COMPLETE : CSVParserEvent.PARSE_ERROR));
}else{
if(_dispatcher) _dispatcher.dispatchEvent(new CSVParserEvent(null, CSVParserEvent.READ_ERROR));
}

}

private static function onSecurityError(e:SecurityErrorEvent):void{
removeURLStream(e.currentTarget as URLStream);
if(_dispatcher) _dispatcher.dispatchEvent(new CSVParserEvent(null, CSVParserEvent.LOAD_ERROR));
}

private static function onIOError(e:IOErrorEvent):void{
removeURLStream(e.currentTarget as URLStream);
if(_dispatcher) _dispatcher.dispatchEvent(new CSVParserEvent(null, CSVParserEvent.LOAD_ERROR));
}

private static function removeURLStream(urlStream:URLStream):void{
urlStream.removeEventListener(Event.COMPLETE, onComplete);
urlStream.removeEventListener (SecurityErrorEvent.SECURITY_ERROR, onSecurityError);
urlStream.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);
if(urlStream.connected) urlStream.close();
urlStream = null;
}

//----------- parse CSV data...tidak penting dalam pos ini -------------
public static function parse(raw:String):XML{
var xml:XML = <CSV/>;
try{
var fieldString:String = raw.replace(/[^]^.*/sm, "");
var dataString:String = raw.replace(/^.*?$[\s]*/m, "");
var data:Array = dataString.match(/^.+?$/gm);
var numRow:int = data.length;

if(numRow){

var field:Array = fieldString.split(CSV_REGEX);
var numField:int = field.length;

var i:int = 0;
var j:int = -1;
var node:XML = <Holiday/>;
var t:Array = String(data[0]).split(CSV_REGEX);

for(; i<numRow; j=-1, ++i, xml.appendChild(node), node=<Holiday/>, t=String(data[i]).split(CSV_REGEX))
while((++j)<numField){node.@[field[j]] = t[j]};
}

}catch(err:Error){
return null;
}

return xml;
}

//---------
public static function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
if(!_dispatcher) _dispatcher = new EventDispatcher();
_dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
}

public static function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void{
if(!_dispatcher) _dispatcher.removeEventListener(type, listener, useCapture);
}

public static function hasEventListener(type:String):Boolean {
if(!_dispatcher) return false;
return _dispatcher.hasEventListener(type);
}
}
}[/code]


jadi kiat boleh gunakan static class kita tu macam class2 biasa:
[code]import com.test.CSVParser;
import com.test.events.CSVParserEvent;

CSVParser.addEventListener(CSVParserEvent.LOAD_ERROR, onCSVError, false, 0, true);
CSVParser.addEventListener(CSVParserEvent.PARSE_COMPLETE, onParseComplete, false, 0, true);
CSVParser.addEventListener(CSVParserEvent.PARSE_ERROR, onCSVError, false, 0, true);
CSVParser.addEventListener(CSVParserEvent.READ_ERROR, onCSVError, false, 0, true);
CSVParser.loadAndParse("test.csv");

function onCSVError(e:CSVParserEvent):void{
trace("ada error: "+e.type);
removeListener();
}

function onParseComplete(e:CSVParserEvent):void{
trace("csv in xml format:\n"+e.parsedXML.toXMLString());
removeListener();
}

function removeListener():void{
CSVParser.removeEventListener(CSVParserEvent.LOAD_ERROR, onCSVError);
CSVParser.removeEventListener(CSVParserEvent.PARSE_ERROR, onCSVError);
CSVParser.removeEventListener(CSVParserEvent.PARSE_ERROR, onCSVError);
CSVParser.removeEventListener(CSVParserEvent.PARSE_COMPLETE, onParseComplete);
}[/code]

//ayat aku memang berbelit2...aku karangan memang fail..kalau ada tak jelas boleh tanya...mungkin akan ada orang dapat jelaskan dengan lebih jelas... Edited by betik

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×
×
  • Create New...