2007年1月22日 星期一

第五章.数组


第五章.数组



5.16.读取联合数组


5.16. 读取联合数组

问题
我要怎样遍历联合数组
解决办法
使用 for . . . in 语句
讨论

基于整形下标的数组可以通过for 语句进行循环遍历,但是,用关键字作索引的联合数组就不能这样遍历了,还好,联合数组可以通过 for . . . in 语句进行遍历访问。该语句会访问指定对象所有可用的属性,语法如下:
for (key in object) {
// Actions
}
for . . . in 语句不需要循环变量更新语句,决定循环次数的是对象的属性个数。注意这key 就是存储每个属性名称的:
var members:Object = new Object( );
members.scribe = "Franklin";
members.chairperson = "Gina";
members.treasurer = "Sindhu";

// 使用 for . . . in 语句遍历所有元素
for (var sRole:String in members) {
// 显示:
// treasurer: Sindhu
// chairperson: Gina
// scribe: Franklin
trace(sRole + ": " + members[sRole]);
}


5.15.创建联合数组


5.15. 创建联合数组

问题
我要创建用名称元素作为索引的数组
解决办法
创建联合数组
讨论

用联合数组其每个元素都有特定的含义,这一点原来的数组类型是做不到的。
var aMembers:Array = new Array("Franklin", "Gina", "Sindhu");
联合数组在其他的语言叫做哈希表,在 ActionScript 里它就是Object 类的一个实例,联合数组使用名称元素来代替数字下标,该名称也被称为关键字或属性,说关键字更好理解些,它关联了元素值,两者一一对应。
创建联合数组不是用Array 类而是Object类创建的,它就是 Object 类的一个实例,理论上 Object 类是任何类的基类。所有的对象都能作为联合数组,但是除非有特殊需要,最好还是用 Object 类创建。
用{ } ,而且用逗号分开每个键值对,键值对之间用:,像下面:
var memebers:Object = {scribe: "Franklin",
chairperson: "Gina",
treasurer: "Sindhu"};
也可以像下面那样创建联合数组:
var members:Object = new Object( );
members.scribe = "Franklin";
members.chairperson = "Gina";
members.treasurer = "Sindhu";
有两种方法访问联合数组内容,一种是通过访问属性名称(关键字):
trace(members.scribe); // 显示: Franklin
另一种就像数组那样,把关键字作为下标来访问,用[ ] 符号:
trace(members["scribe"]); // 显示: Franklin
这种方式更加灵活,可以在数组中进行遍历,对于动态生成的关键值和内容这种访问方式是最好的,例如:
var members:Object = new Object();
members.councilperson1 = "Beatrice";
members.councilperson2 = "Danny";
members.councilperson3 = "Vladamir";
for (var i:int = 1; i <= 3; i++) {
trace(members["councilperson" + i];
}

数组访问方式在循环语句里经常用到:
var members:Object = new Object( );
members["councilperson"] = "Ruthie";
trace(members.councilperson); // 显示 Ruthie
members.councilperson = "Rebecca";
trace(members["councilperson"]); // 显示: Rebecca


5.14.比较数组


5.14. 比较数组

问题
我该怎么知道两个数组是否相等呢
解决办法
循环数组,一一比较对应位置的每个元素
讨论

因为数组是引用类型,使用=操作符只能对比引用是否指向同一内存空间,如:
var letters:Array = ["a", "b", "c", "d"];
var lettersPointer:Array = letters;
trace(letters == lettersPointer); // 显示: true
但是如果数组内容相同,但是在不同的内存空间,=操作就会返回false:
var letters1:Array = ["a", "b", "c", "d"];
var letters2:Array = ["a", "b", "c", "d"];
trace(letters1 == letters2]; // 显示: false
因此,比较数组应该比较数组的每个元素是否相等:
var equivalent:Boolean = true;
for(var i:int = 0; i < letters1.length; i++) {
if(letters1[i] != letters2[i]) {
equivalent = false;
break;
}
}
trace(equivalent); // 显示: true
另外还可以用ArrayUtilities.equals( ) 方法,该方法需要两个参数:两个数组引用,返回布尔值说明是否相等:
var letters1:Array = ["a", "b", "c", "d"];
var letters2:Array = ["a", "b", "c", "d"];
trace(ArrayUtilities.equals(letters1, letters2));
// 显示: true
默认,两个不同排列的数组是不相等的,除非提供第3个参数为true表示忽略数组排列顺序:
var letters1:Array = ["a", "b", "c", "d"];
var letters2:Array = ["b", "a", "d", "c"];
trace(ArrayUtilities.equals(letters1, letters2));
// 显示: false
trace(ArrayUtilities.equals(letters1, letters2, true));
// 显示: true
equals( ) 方法用起来很简单,下面是它的代码:
public static function equals(arrayA:Array,
arrayB:Array,
bNotOrdered:Boolean):Boolean {

// 如果两个数组长度不同
if(arrayA.length != arrayB.length) {
return false;
}

// 创建拷贝,不影响原数组
var arrayACopy:Array = arrayA.concat( );
var arrayBCopy:Array = arrayB.concat( );

// 如果忽略排列顺序
if(bNotOrdered) {
arrayACopy.sort( );
arrayBCopy.sort( );
}

// 循环比较
// 如果不匹配,删除拷贝,返回false
for(var i:int = 0; i < arrayACopy.length; i++) {
if(arrayACopy[i] != arrayBCopy[i]) {
delete arrayACopy;
delete arrayBCopy;
return false;
}
}

// 否则相等,删除数组,返回true
delete arrayACopy;
delete arrayBCopy;
return true;
}


5.13.取得数组元素的最大值和最小值


5.13. 取得数组元素的最大值和最小值

问题
我要获取数字数组的最大和最小元素
解决办法
经过数字排序,然后读取数组的第一个和最后一个元素
讨论

要想快速的取得最大值和最小值,先进行排序,看下面:
var scores:Array = [10, 4, 15, 8];
scores.sort(Array.NUMERIC);
trace("Minimum: " + scores[0]);
trace("Maximum: " + scores[scores.length - 1]);
如果不破坏原有数组顺序,可先复制数组:
也可使用 ArrayUtilities.min( )ArrayUtilities.max( ) 方法。


5.12.数组元素的随机排序


5.12. 数组元素的随机排序

问题
我要打乱数组元素的顺序
解决办法
使用 sort( ) 方法和自定义比较函数返回随机的正数或负数
讨论

很多情况我们需要得到一个随机排列的数组,比如有个游戏需要产生随机的字母。
有很多种方法达到这个目的,但是最简单的办法就是创建自定义比较函数,返回随机的正数或负数,把该函数引用传递给sort( ) 方法:
下面的比较函数就能达到目的:
function randomSort(elementA:Object, elementB:Object):Number {
return Math.random( ) - .5
}
Math.random( ) 返回0.0 到 1.0. 减去0.5 ,正好有一半的几率是负数,一半为正数,因此这个数组经过随机排序
看下面的随机排序例子:
var numbers:Array = new Array( );
for(var i:int=0;i<20;i++) {
numbers[i] = i;
}
numbers.sort(randomSort);
for(var i:int=0;i<numbers.length;i++) {
trace(numbers[i]);
}


5.11.实现自定义排序


5.11. 实现自定义排序

问题
我要自定义数组排序
解决办法
把自定义比较的函数引用传递给sort( ) 方法
讨论

如果要自定义排序,可用sort( ) 方法和自定义比较函数。sort( ) 方法重复调用比较函数对两个数组元素进行比较,比较函数接受两个参数即数组元素(我们称为a和b),根据具体的排序方式返回正数,负数或0。如果返回负数,a排在b前,如果返回0,位置不变,如果返回正数,a排在b后,直到所有元素对比完毕。
下面有个例子对字符串数组进行自定义排序,比如是一个歌曲名数组,在排序时忽略字符串中含有的"The" 字母,首先看看默认的排序:
var bands:Array = ["The Clash",
"The Who",
"Led Zeppelin",
"The Beatles",
"Aerosmith",
"Cream"];
bands.sort( );
for(var i:int = 0; i < bands.length; i++) {
trace(bands[i]);
/* 输出:
Aerosmith
Cream
Led Zeppelin
The Beatles
The Clash
The Who
*/
}
给 sort( ) 方法传递bandNameSort 比较函数:
var bands:Array = ["The Clash",
"The Who",
"Led Zeppelin",
"The Beatles",
"Aerosmith",
"Cream"];
bands.sort(bandNameSort);
for(var i:int = 0; i < bands.length; i++) {
trace(bands[i]);
/*输出
Aerosmith
The Beatles
The Clash
Cream
Led Zeppelin
The Who
*/
}

function bandNameSort(band1:String, band2:String):int
{
band1 = band1.toLowerCase( );
band2 = band2.toLowerCase( );
if(band1.substr(0, 4) == "the ") {
band1 = band1.substr(4);
}
if(band2.substr(0, 4) == "the ") {
band2 = band2.substr(4);
}
if(band1 < band2) {
return -1;
}
else {
return 1;
}
}
bandNameSort( ) 函数把字符串元素转换为小写,然后检测是否含有"The ",如果有则剪切掉,取剩余字符串进行比较


5.10.数组排序


5.10. 数组排序

问题
我要进行数组排序
解决办法
使用 sort( ) 方法,对于对象数组可以用 sortOn( ) 方法
讨论

使用 sort( ) 方法就可以对数组进行排序,没有参数是进行升序排序,对于字符内容采用 Unicode 编码排序
var words:Array = ["tricycle", "relative", "aardvark", "jargon"];
words.sort( );
trace(words); // 显示: aardvark,jargon,relative,tricycle
如果要进行降序排序,需要传递参数Array.DESCENDING 常量:
var words:Array = ["tricycle", "relative", "aardvark", "jargon"];
words.sort(Array.DESCENDING);
trace(words); // 显示: tricycle,relative,jargon,aardvark
上面的例子没有考虑大小写问题,比如:
var words:Array = ["Tricycle", "relative", "aardvark", "jargon"];
words.sort( );
trace(words); // 显示: Tricycle,aardvark,jargon,relative
使用Array.CASEINSENSITIVE 常量忽略大小写进行排序:
var words:Array = ["Tricycle", "relative", "aardvark", "jargon"];
words.sort(Array.CASEINSENSITIVE);
trace(words); // 显示 aardvark,jargon,relative,Tricycle
如果对数字内容的数组排序,则根据第一个数字的ASCII 排序:
var scores:Array = [10, 2, 14, 5, 8, 20, 19, 6];
scores.sort( );
trace(scores); // 显示: 10,14,19,2,20,5,6,8
使用 Array.NUMERIC 常量才能正常对数字排序:
var scores:Array = [10, 2, 14, 5, 8, 20, 19, 6];
scores.sort(Array.NUMERIC);
trace(scores); // 显示:2,5,6,8,10,14,19,20
还有两个常量Array.UNIQUESORTarray.RETURNINDEXEDARRAY. ,如果你只是对含有唯一元素的数组排序就可以用Array.UNIQUESORT ,Flash只会对这样的数组排序,不满足条件 sort( ) 返回0,且不进行排序:
var ranking:Array = [2,5,6,3,1,1,4,8,7,10,9];
var sortedRanking:Object = ranking.sort(Array.UNIQUESORT);
trace(sortedRanking); // 显示: 0
trace(ranking); // 显示:2,5,6,3,1,1,4,8,7,10,9
Array.RETURNINDEXEDARRAY 得到排序后数组元素的下标顺序,但不改变原始数组:
var words:Array = ["tricycle", "relative", "aardvark", "jargon"];
var indices:Array = words.sort(Array.RETURNINDEXEDARRAY);
trace(words); // 显示: tricycle,relative,aardvark,jargon
trace(indices); // 显示: 2,3,1,0
for(var i:int = 0; i < words.length; i++) {
/* 显示
aardvark
jargon
relative
tricycle
*/
trace(words[indices[i]]);
}
可以用操作符(|)联合使用这些常量:
var words:Array = ["Tricycle", "relative", "aardvark", "jargon"];
words.sort(Array.CASEINSENSITIVE | Array.DESCENDING);
trace(words); // 显示: Tricycle,relative,jargon,aardvark
有时候你想反转数组该怎么办呢?sort( ) 方法并没这功能,这是可以用 reverse( ) 方法:
var words:Array = ["tricycle", "relative", "aardvark", "jargon"];
words.reverse( );
trace(words); // 显示: jargon,aardvark,relative,tricycle
上面的部分讨论了怎么对字符串和数字进行排序,对于对象数组可用 sortOn( ) 方法,该方法需要一个字符串参数指定名称属性对其排序:
var cars:Array = new Array();
cars.push({make: "Honda", year: 1997, color: "maroon"});
cars.push({make: "Chrysler", year: 2000, color: "beige"});
cars.push({make: "Mercedes", year: 1985, color: "blue"});
cars.push({make: "Fiat", year: 1983, color: "gray"});
// 对 year 属性进行排序 cars.sortOn("year"):
for (var i:int = 0; i < cars.length; i++) {
/* 显示:
gray 1983 Fiat
blue 1985 Mercedes
maroon 1997 Honda
beige 2000 Chrysler
*/
trace(cars[i].color + "\t" +
cars[i].year + "\t" +
cars[i].make);
}
sortOn( ) 方法也可以一次对多个字段进行排序,看下面的代码:
var cars:Array = new Array( );
cars.push({make: "Honda", year: 1997, color: "maroon"});
cars.push({make: "Chrysler", year: 2000, color: "beige"});
cars.push({make: "Mercedes", year: 1985, color: "blue"});
cars.push({make: "Fiat", year: 1983, color: "gray"});
cars.push({make: "Honda", year: 1992, color: "silver"});
cars.push({make: "Chrysler", year: 1968, color: "gold"});
cars.push({make: "Mercedes", year: 1975, color: "green"});
cars.push({make: "Fiat", year: 1983, color: "black"});
cars.push({make: "Honda", year: 2001, color: "blue"});
cars.push({make: "Chrysler", year: 2004, color: "orange"});
cars.push({make: "Mercedes", year: 2000, color: "white"});
cars.push({make: "Fiat", year: 1975, color: "yellow"});
// 对两个字段排序
cars.sortOn(["year", "make"]);
for (var i:int = 0; i < cars.length; i++) {
/* 显示:
gold 1968 Chrysler
yellow 1975 Fiat
green 1975 Mercedes
black 1983 Fiat
gray 1983 Fiat
blue 1985 Mercedes
silver 1992 Honda
maroon 1997 Honda
beige 2000 Chrysler
white 2000 Mercedes
blue 2001 Honda
orange 2004 Chrysler
*/
trace(cars[i].color + "\t" +
cars[i].year + "\t" +
cars[i].make);
}
下面的例子,先对 make, 再对year排序:
cars.sortOn(["make", "year"]);

for (var i:int = 0; i < cars.length; i++) {
/* 显示:
gold 1968 Chrysler
beige 2000 Chrysler
orange 2004 Chrysler
yellow 1975 Fiat
black 1983 Fiat
gray 1983 Fiat
silver 1992 Honda
maroon 1997 Honda
blue 2001 Honda
green 1975 Mercedes
blue 1985 Mercedes
white 2000 Mercedes
*/
trace(cars[i].color + "\t" +
cars[i].year + "\t" +
cars[i].make);
}
sortOn( ) 方法也可用那些数组常量完成降序,忽略大小写等排序:
cars.sortOn("year", Array.DESCENDING);

for (var i:int = 0; i < cars.length; i++) {
/* 显示:
beige 2000 Chrysler
maroon 1997 Honda
blue 1985 Mercedes
gray 1983 Fiat
*/
trace(cars[i].color + "\t" +
cars[i].year + "\t" +
cars[i].make);
}

5.9.存储多维数据


5.9. 存储多维数据

问题
我要怎样存储多组相关的数据
解决办法
使用多维数组存储
讨论

除了一维数组,还可以创建多维数组,比如 beginGradientFill() 方法(在第7章讨论)使用三个平行数组表示colors, alphas, 和ratios ,每个数组都有相同的下标。
创建平行数组,然后操作同一下标的元素,使用平行数组,很容易访问同一下标的相关元素,比如:
var colors:Array = ["maroon", "beige", "blue", "gray"];
var years:Array = [1997, 2000, 1985, 1983];
var makes:Array = ["Honda", "Chrysler", "Mercedes", "Fiat"];

// 循环这些数组,因此数组的长度相同,可以用任何一个数组的length属性,下面的例子使用了 //makes.length.
for (var i:int = 0; i < makes.length; i++) {
trace("A " + colors[i] + " " +
years[i] + " " +
makes[i]);
// 显示:
// A maroon 1997 Honda
// A beige 2000 Chrysler
// A blue 1985 Mercedes
// A gray 1983 Fiat
}
需要注意的是,如果改变了数组长度,必须同时修改其他数组。
另一种方法就是创建多维数组,它是数组的数组:
// 创建数组 cars,然后用数组组装填充,每个元素都是数组包含3个元素 (color, year, make).
var cars:Array = new Array();
cars.push(["maroon", 1997, "Honda"]);
cars.push(["beige", 2000, "Chrysler"]);
cars.push(["blue", 1985, "Mercedes"]);
cars.push(["gray", 1983, "Fiat"]);
// 循环遍历数组
for (var i:int = 0; i < cars.length; i++) {
// 显示:
// A maroon 1997 Honda
// A beige 2000 Chrysler
// A blue 1985 Mercedes
// A gray 1983 Fiat
TRace("A " + cars[i][0] + " " +
cars[i][1] + " " +
cars[i][2]);
}

下面的代码用二重循环遍历二维数组:
for (var i:int = 0; i < cars.length; i++) {
for (var j:int = 0; j < cars[i].length; j++) {
TRace("Element [" + i + "][" + j + "] contains: " +
cars[i][j]);
}
}

从上面的例子来看,很难区别cars[i][0] 和 cars[i][j]。但如果任何数组的长度发生变化这时cars[i][0]这种表示就很被动,需要修改代码才行。
另外对象数组的使用也很类似,只是多了个名称属性。对象数组用名称属性代替数字下标来索引元素:
// 创建数组cars,填充对象
// 每个对象有个make 属性,year 属性和 color 属性
var cars:Array = new Array();
cars.push({make: "Honda", year: 1997, color: "maroon"});
cars.push({make: "Chrysler", year: 2000, color: "beige"});
cars.push({make: "Mercedes", year: 1985, color: "blue"});
cars.push({make: "Fiat", year: 1983, color: "gray"});


// 遍历数组
for (var i:int = 0; i < cars.length; i++) {
trace("A " + cars[i].color + " " +
cars[i].year + " " +
cars[i].make);
}


2007年1月21日 星期日

5.8.创建数组的拷贝


5.8. 创建数组的拷贝

问题
我要复制一份数组,内容完全一样,只是不同的引用
解决办法
使用 concat( ) 方法或 slice( ) 方法,另外还可以使用ArrayUtilities.duplicate( ) 方法,duplicate( ) 方法可以创建递归复制
讨论

因为数组是复合类型,因此它的比较和复制都和基本类型不同。一个变量指向数组但是实际上它并不包含数组数据,它只是指向内存中存放数组数据的位置。从优化的角度考虑,基本类型的占用空间往往很小,但是符合类型如数组可以变得很大,如果我们在日常操作中经常复制整个数组是非常不明智的,因此当你要复制数组时,ActionScript 并不是生成独立的一份拷贝,看下面的例子:
首先我们看看基本类型是怎么复制的:
// 赋值数字5给变量
var quantity:int = 5;
// 拷贝 quantity的值 给另一个变量 newQuantity.
var newQuantity:int = quantity;
// 改变 quantity的值
quantity = 29;
trace(quantity); // 显示: 29
trace(newQuantity); // 显示: 5
我们看到,两者是互不影响的,也就是说基本变量拷贝的是值
现在我们看看数组的操作,和上面的例子不同,两个变量实际上都指向了同一个数组存贮空间。当letters 变量改变数组内容时,也影响到newLetters变量:
// 赋值数组.
var letters:Array = ["a", "b", "c"];
// 拷贝 letters 到newLetters.
var newLetters:Array = letters;
// 两个数组包含相同内容
trace(letters); // 显示: "a,b,c"
trace(newLetters); // 显示: "a,b,c"
// 改变letters的值
letters = ["d", "e", "f"];
// 另一个数组也变了
trace(letters); // 显示: "d,e,f"
trace(newLetters); // 显示: "d,e,f" (而不是 "a,b,c")
其实这就像文件夹的两个快捷方式那样,两个快捷方式虽然名字不同,但是都指向同一个文件,无论哪个对文件夹操作,另一个快捷方式也发生变化。
如果真要复制数组,可以调用数组的 concat( ) 方法:
// 给数组赋值.
var letters:Array = ["a", "b", "c"];
//用concat( ) 创建新的数组
var newLetters:Array = letters.concat( );
// 两个数组内容一样
trace(letters); // 显示: "a,b,c"
trace(newLetters); // 显示: "a,b,c"
// 改变letters的值
letters = ["d", "e", "f"];
//不像上面的例子,这次两个数组内容不同了.
trace(letters); // 显示: "d,e,f"
trace(newLetters); // 显示: "a,b,c"
也可以用 slice( ) 方法代替 concat( ), 例如:
var newLetters:Array = letters.slice(0);
concat( ) 或 slice( ) 方法复制一维整型下标的数组还可以,但是如果是多维的联合数组就不行了,对于联合数组,不能使用 concat( ) 或 slice( ) 方法,对于多维数组,用 concat( ) 或 slice( ) 只能复制顶层的数组,内部的就不能复制了,看下面的代码:
var coordinates:Array = new Array( );
coordinates.push([0,1,2,3]);
coordinates.push([4,5,6,7]);
coordinates.push([8,9,10,11]);
coordinates.push([12,13,14,15]);

// 复制
var coordinatesDuplicate:Array = coordinates.concat( );

// 替换元素
coordinatesDuplicate[0][0] = 20;
trace(coordinates[0][0]); //显示: 20

// 替换顶层元素.
coordinatesDuplicate[1] = [21,22,23,24];
trace(coordinates[1]); // 显示: 4,5,6,7
上面的代码coordinates 是个二维数组,coordinatesDuplicate 是coordinates的复制。但是,虽然是复制,有些元素仍然引用了原始数组元素,这就意味你改变了一个,会影响另一个数组的内容。实际上从上面的代码可以看出,只复制了顶层的数组,这一部分是互不关联的。
要想完全的复制数组,需要使用递归。ArrayUtilities.duplicate( ) 方法就是这样的做法,默认下它也只复制一层数组,当第2个参数设为true时则是递归复制:
// 创建二维数组
var coordinates:Array = new Array( );
for(var i:int = 0; i < 4; i++) {
coordinates[i] = new Array( );
for(var j:int = 0; j < 4; j++) {
coordinates[i].push(String(i) + "," + String(j));
}
}

// 复制coordinates.
var newCoordinates:Array = ArrayUtilities.duplicate(coordinates, true) as Array;
// 替换
newCoordinates[0][0] = "a";
// 使用toString() 方法输出
trace(ArrayUtilities.toString(coordinates));
trace(ArrayUtilities.toString(newCoordinates));
下面的例子演示用 duplicate( ) 方法复制联合数组:
var coordinatesMap:Object = new Object( );
coordinatesMap.a = [{a: 1},{b: 2}, {c: 3}, {d: 4}];
coordinatesMap.b = [{a: 1},{b: 2}, {c: 3}, {d: 4}];
coordinatesMap.c = [{a: 1},{b: 2}, {c: 3}, {d: 4}];
coordinatesMap.d = [{a: 1},{b: 2}, {c: 3}, {d: 4}];
var newCoordinatesMap:Object = ArrayUtilities.duplicate(coordinatesMap, true);
newCoordinatesMap.a[0] = {r: 5};
trace(ArrayUtilities.toString(coordinatesMap));
trace(ArrayUtilities.toString(newCoordinatesMap));
两个例子可以看到,复制的数组改变不会影响原始数组。


5.7.转换数组为字符串


5.7. 转换数组为字符串

问题
我要把数组转换为字符串
解决办法
使用 join( ) 方法
讨论

ActionScript 提供内建的方法 join( ) 可以快速把数组转换为字符串(数组中的元素不管什么类型都将转换为字符串),该方法接受个参数作为分隔符:
var letters:Array = ["a", "b", "c"];
trace(letters.join("|")); // 显示: a|b|c
如果不指定分隔符,默认为逗号:
var letters:Array = ["a", "b", "c"];
trace(letters.join()); // 显示: a,b,c


当 join( ) 的分隔符为逗号,其效果和toString( ) 一样。实际上当我们直接输出数组时系统就是调用toString( ) 方法进行转换的,例如:
var letters:Array = ["a", "b", "c"];
trace(letters); // 显示: a,b,c


5.6.转换字符串为数组


5.6. 转换字符串为数组

问题
我有一堆字符串,想把它转换为数组。
解决办法
使用String.split( ) 方法
讨论

String 类的split( ) 方法把字符串转换为数组,但前提是字符串中含有统一的分割符,比如 Susan,Robert,Paula 字符串分割符为逗号
split( ) 方法接受两个参数:
分割符
用分割符来分割字符串,如果没定义,则把整个字符串作为数组的第一个元素
数量
分割出的最大元素个数,如果没定义,则全部放入数组。
可以使用空格符作为分割符:
var list:String = "Peter Piper picked a peck of pickled peppers";
var words:Array = list.split(" ");
split( ) 方法在用URLLoader 对象读取数据时经常用到,比如你接受服务器的一些姓名字符串:
names=Michael,Peter,Linda,Gerome,Catherine
这是用split( ) 方法转换为数组:
// URLLoader 读取数据
var namesData:String = _loader.data;
var names:Array = namesData.split(",");


5.5.在数组中间插入元素


5.5. 在数组中间插入元素

问题
我要在数组中间插入元素u
解决办法
使用splice( ) 方法
讨论

splice( ) 方法不仅可以删除元素,也可以插入元素,插入的元素放到第2个参数之后,当第2个参数为0代表插入元素:
var letters:Array = ["a", "b", "c", "d"];
//插入三个元素,起始位置为1
letters.splice(1, 0, "r", "s", "t");

// letters 现在包含的元素有:
// "a", "r", "s", "t", "b", "c", "d".
for (var i:int = 0; i < letters.length; i++) {
trace(letters[i]);
}

你也可以删除和插入同时执行:
var letters:Array = ["a", "b", "c", "d"];
//删除2个,插入3个
letters.splice(1, 2, "r", "s", "t");
// myArray 现在的元素
// "a", "r", "s", "t", and "d".
for (var i:int = 0; i < letters.length; i++) {
trace(letters[i]);
}


5.4.删除数组元素


5.4. 删除数组元素

问题
我要删除一个或多个数组元素,或移动数组元素
解决办法
splice( ) 方法删除指定位置的元素,pop( ) 删除尾部元素,shift( ) 删除首部元素
讨论

删除指定位置的元素使用splice( ) 方法,它需要两个参数:
start
开始下标
deleteCount
删除的元素个数,如果没有定义,则从起始位置到末尾全部删除:
var letters:Array = ["a", "b", "c", "d"];
//从下标1开始删除1个元素
letters.splice(1, 1);
// 显示结果,现在只剩三个
// "a", "c", 和 "d".
for (var i:int = 0; i < letters.length; i++) {
trace(letters [i]);
}
splice( ) 方法也返回一个新的包含删除的元素数组:
var letters:Array = ["a", "b", "c", "d"];
//删除两个元素,从0位置开始
var deleted:Array = letters.splice(0, 2);
// 显示: "a","b".
for (var i:int = 0; i < deleted.length; i++) {
trace(deleted[i]);
}
删除首部和尾部的元素可用shift( ) 和pop( ) 方法。 shift( ) 方法删除首部第一个元素,然后返回该元素,pop( ) 方法删除尾部的元素并返回该值:
var letters:Array = ["a", "b", "c", "d"];
trace(letters.shift( ));
trace(letters.pop( ));

//显示剩下的元素
for (var i = 0; i < letters.length; i++) {
trace(letters[i]);
}
在for循环里删除原始,也要修改下标值,下面的代码演示不更新下标值变量出现的情况:
var numbers:Array = new Array(4, 10);
numbers[4] = 1;
trace(numbers); // 显示: 4,10,undefined,undefined,1
for(var i:int = 0; i < numbers.length; i++) {
if(numbers[i] == undefined) {
numbers.splice(i, 1);
}
}

trace(numbers); // 显示: 4,10,undefined,1
上面的代码本来是期望上出全部undefined 元素的,结果只删除一个,调试运行,看看发生了什么:
1。前两个循环什么都没做,因为都不是undefined.
2。第三次找到 undefined 然后删除它,这时,第4个和第5个元素下移变成了第3个和第4个元素
3。下一循环检测第4个元素,也就是最后一个,这时忽略了第3个元素也就是那个undefined 元素,因此,当删除元素,应该把小标变量-1,代码应该这样:
var numbers:Array = new Array(4, 10);
numbers[4] = 1;
trace(numbers); // 显示: 4,10,undefined,undefined,1
for(var i:int = 0; i < numbers.length; i++) {
if(numbers[i] == undefined) {
numbers.splice(i, 1);
i--;
}
}

trace(numbers); // 显示: 4,10,1


5.3.搜索匹配的数组元素


5.3. 搜索匹配的数组元素

问题
我要找出指定值得数组元素
解决办法
用for 语句和break 语句就能找到匹配的元素。另外用ArrayUtilities.findMatchIndex( ), ArrayUtilities.findLastMatchIndex( ),ArrayUtilities.findMatchIndices( ) 方法
讨论

用for循环查找第一个匹配的元素后,用break立即返回,这样就实现功能了。
break应该在if语句里进行判断,是否找到匹配元素,找到则执行break推出循环,否则继续查找。
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];

// 指定要搜索的内容
var match:String = "b";

for (var i:int = 0; i < letters.length; i++) {

// 检测当前元素是否匹配
if (letters[i] == match) {
trace("Element with index " + i +
" found to match " + match);
break;
}
}
也可以找到匹配的最后一个元素,这就需要倒序遍历数组:
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];

var match:String = "b";
for (var i:int = letters.length - 1; i >= 0; i--) {
if (letters[i] == match) {
trace("Element with index " + i +
" found to match " + match);
break;
}
}
使用自定义类ArrayUtilities 类更简单,它在 ascb.util 包中,首先导入它:
import ascb.util.ArrayUtilities;
ArrayUtilities 类有三个方法来查找匹配的元素findMatchIndex( ), findLastMatchIndex( ), 和 findMatchIndices( )。 findMatchIndex( ) 方法至少需要两个参数:一个指向数组的引用和需要匹配的值,返回第一个匹配的元素下标,如果找不到返回-1:
var letters:Array = ["a", "b", "c", "d"];
trace(ArrayUtilities.findMatchIndex(letters, "b"));
// 显示: 1
trace(ArrayUtilities.findMatchIndex(letters, "r"));
// 显示: -1
也可以指定搜索的起始下标作为第三个参数:
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];
trace(ArrayUtilities.findMatchIndex(letters, "a", 1));
// 显示: 4
如果第三个参数为true,则返回部分匹配的元素:
var words:Array = ["bicycle", "baseball", "mat", "board"];
trace(ArrayUtilities.findMatchIndex(words, "s", true));
// 显示: 1
如果你想部分匹配又想指定起始搜索下标,可以把起始下标作为第四个参数。
findLastMatchIndex( ) 方法返回最后一个匹配的元素下标
findMatchIndices( ) 方法返回所有匹配的元素下标数组:
var letters:Array = ["a", "b", "c", "d", "a", "b", "c", "d"];
trace(ArrayUtilities.findMatchIndices(letters, "b"));
// 显示: 1,5
也可以设定为部分匹配,指定第三个参数为true:
var words:Array = ["bicycle", "baseball", "mat", "board"];

trace(ArrayUtilities.findMatchIndices(words, "b", true));
// 显示: 0,1,3
ArrayUtilities 方法内部也是用for循环来实现的,现在我们看看代码,下面是findMatchIndex( ) 方法的代码:



public static function findMatchIndex(array:Array, element:Object):int {
// Use a variable to determine the index
// from which to start. Use a default value of 0.
var startingIndex:int = 0;

// By default don't allow a partial match.
var partialMatch:Boolean = false;

// If the third parameter is a number,
// assign it to nStartingIndex.
// Otherwise, if the fourth parameter is a number,
// assign it to nStartingIndex instead.
if(typeof arguments[2] == "number") {
startingIndex
= arguments[2];
}

else if(typeof arguments[3] == "number") {
startingIndex
= arguments[3];
}


// If the third parameter is a Boolean value,
// assign it to partialMatch.
if(typeof arguments[2] == "boolean") {
partialMatch
= arguments[2];
}


// Assume no match is found.
var match:Boolean = false;

// Loop through each of the elements of the array
// starting at the specified starting index.
for(var i:int = startingIndex;
i
< array.length; i++) {

// Check to see if the element either matches
// or partially matches.
if(partialMatch) {
match
= (array[i].indexOf(element) != -1);
}

else {
match
= (array[i] == element);
}


// If the element matches, return the index.
if(match) {
return i;
}

}


// The following return statement is only reached
// if no match was found. In that case, return -1.
return -1;
}


public static function findMatchIndices(array:Array,
element:Object, partialMatch:Boolean
= false):Array {
var indices:Array
= new Array( );
var index:
int = findMatchIndex(array,
element,
partialMatch);
while(index != -1) {
indices.push(index);
index
= findMatchIndex(array,
element,
partialMatch,
index
+ 1);
}

return indices;
}



5.2.遍历数组成员


5.2. 遍历数组成员

问题
我要访问数组的每个元素
解决办法
利用 for 循环来遍历数组,使用下标返回元素。
讨论

for循环的初始变量从0开始,结束为array.length-1,因为是从下标0开始的:
var letters:Array = ["a", "b", "c"];
for (var i:int = 0; i < letters.length; i++) {
trace("Element " + i + ": " + letters[i]);
}
也可以降序遍历数组,循环变量从array.length-1开始到0:
var letters:Array = ["a", "b", "c"];
for (var i:int = letters.length - 1; i >= 0; i--){
trace("Element " + i + ": " + letters[i]);
}
有很多情况需要用循环遍历所有元素,比如,获得了包含sprite的数组, 然后把每个sprite的x坐标+1:
for (var i:int = 0; i < sprites.length; i++){
sprites[i].x++;
}
可以把数组长度存在变量中,免得每次循环都要重新计算:
var length:int = sprites.length;
for (var i:int = 0; i < length; i++){
sprites[i].x++;
}

这样做可以提高Flash性能,因为不用每次循环都去计算长度了,但是有个前提,就是没有进行插入删除操作来改变长度值,否则就要每次计算长度才行。


5.1.在数组首部和尾部添加元素


5.1. 在数组首部和尾部添加元素

问题
我要再添加新元素到数组
解决办法
push( ) 方法在数组尾部添加元素,unshift( ) 方法在数组首部插入元素
讨论

Array.push( ) 方法把元素加在数组的尾部,也可以一次添加多个值:
var array:Array = new Array();
array.push("val 1", "val 2");
也可以在数组指定的下标位置设置值,下标位置在0到Array .length - 1之间:
array[array.length] = "val 3";
如果下标不存在,数组会自动扩充,扩充的空间当中,没有被赋值的自动以"undefined"填充:
var letters:Array = ["a", "b", "c"];
letters[5] = "f";
通过unshift( ) 方法在数组首部插入新元素:
// 创建四个元素的数组
// "a", "b", "c", "d".
var letters:Array = new Array( );
letters.push("a", "b", "c", "d");

// 添加"z" ,其他元素依次向下移动
letters.unshift("z");

for (var i:int = 0; i < letters.length; i++) {
trace(letters[i]);
}
到底把数据插入到哪里需要根据具体实际需要,比如要达到(LIFO) 目的,我们需要 Array.push( ) 和 Array.pop( ) 成对使用。


5.0.简介


5.0. 简介

数组保存一组相关联的数据,组织和处理这些数据。数组概念在生活中是很常见的,比如菜谱,包含姓名,地址,生日等地址本都是数组原理。
在ActionScript中,有两种数组:整型下标和联合数组,都是组织相关数据,只是访问数据的方法不同而已。


整型下标数组:
数组的每个元素都用唯一的整数下标来索引。通过下标排序,起始值为0,每个元素保存在索引指定的位置,这就像抽屉一样。


联合数组 :
用字符串关键字作为每个元素的索引。


首先创建数组,有两种构造方法,
// 创建空的数组
var array:Array = new Array();

// 创建数组时指定数组长度
var array:Array = new Array(elements);

//创建数组时加入多个元素
var array:Array = new Array(element0,...elementN);
直接用数组符号也可以创建一个数组,这是很简洁的方式创建数组:
var letters:Array = ["a", "b", "c"];
Array 类提供了一些方法修改数组内容或者返回新的数组
使用数组操作符(中括号加上下标索引)来读取和设置内容,如:
//设置第5个元素为"apples"
// (下标从0开始).
items[4] = "apples";

// 显示第5个元素
trace(items[4]); // 显示: apples
ActionScript 并不关心数组里存的是什么类型的数据,可以是字符串,数字,布尔值和引用的类型,而且不像其他语言,同一个数组可以存不同类型的数据,例如:
var data:Array = ["a", 2, true, new Object()];
还有点不同,数组在创建时可以不用指定数组长度。


2007年1月20日 星期六

第四章.数字和数学


第四章.数字和数学


4.0. 简介


4.1.数字的不同表现形式


4.2.不同数字类型之间的转换


4.3.四舍五入


4.4.格式化输出


4.5.不使用掩码进行数字格式化


4.6.格式化货币数字


4.7.生成随机数


4.8.模拟硬币投掷


4.9.模拟骰子


4.10.产生唯一的随机数


4.11.转换角度计算


4.12.计算两点之间的距离


4.12.计算两点之间的距离


4.12. 计算两点之间的距离

问题
我要计算两点之间的距离
解决办法
根据勾股定理,使用 Math.pow( ) 和 Math.sqrt( ) 联合计算
讨论

通过勾股定理可以计算出两点之间的距离(直线)。一个三角形,最长边的平方等于其他两边的平方和:
a2 + b2 = c2

根据这个公式可以计算出两点之间的距离,a 是两点X坐标的差值,b 是两点Y坐标的差值:
var c:Number = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));


4.11.转换角度计算


4.11. 转换角度计算

问题
我要计算角度及转换为合适的单位
解决办法
使用 Unit 和 Converter 类
讨论

影片剪辑的_rotation 属性使用角度计算的。如果用弧度而不是角度就有些麻烦了。首先要把弧度值转换为角度值,再赋值给 _rotation 属性,而且,大多数人喜欢用角度计算。还好,把弧度转换为角度挺容易,只要180/Math.PI.,角度转换为弧度就反一下,Math.PI/180. ,而且使用自定义的Unit 和Converter 类更简单。
这两个类都是自定义类,在ascb.unit 包中,首先创建Unit 实例,然后描述需要转换的单位类型, Unit.DEGREE, Unit.RADIAN, 和 Unit.GRADIAN 常量返回新的Unit 对象表示各种单位。Unit 对象有些属性,包括name, category, label, 和labelPlural:
var degree:Unit = Unit.DEGREE;
trace(degree.name); // 显示: degree
trace(degree.category); // 显示: angle
trace(degree.label); // 显示: degree
trace(degree.labelPlural); // 显示: degrees
使用getConverterTo( ) 方法,传递 Unit 对象作为,得到converter对象,看下面的代码得到弧度向角度转换的对象:
var converter:Converter = Unit.DEGREE.getConverterTo(Unit.RADIAN);
一旦得到Converter 实例,运行convert( ) 方法,指定值进行转换:
trace(converter.convert(90));
convertWithLabel( ) 方法输出字符串:
var converterToRadians:Converter = Unit.DEGREE.getConverterTo(Unit.RADIAN);
var converterToDegrees:Converter = Unit.RADIAN.getConverterTo(Unit.DEGREE);
trace(converterToRadians.convertWithLabel(1));
trace(converterToRadians.convertWithLabel(57.2957795130823));
trace(converterToDegrees.convertWithLabel(1));
trace(converterToDegrees.convertWithLabel(0.0174532925199433));

/*
显示:
0.0174532925199433 radians
1 radian
57.2957795130823 degrees
1 degree
*/
如果执行相反操作,使用getConverterFrom( ) 方法:
var converter:Converter = Unit.DEGREE.getConverterFrom(Unit.GRADIAN);
trace(converter.convert(100));
trace(converter.convert(23));


4.10.产生唯一的随机数


4.10. 产生唯一的随机数

问题
我要产生唯一数
解决办法
使用 NumberUtilities.getUnique( ) 方法
讨论

唯一随机数经常在产生唯一的URL 时用到。就是在URL后加上个唯一的数字,以区别于使用过的URL,因此浏览器总是会去调用远程服务器而不是访问缓存
NumberUtilities.getUnique( ) 返回基于毫秒的数字
trace(NumberUtilities.getUnique( ));
下面的代码产生一组唯一的随机数:
for(var i:Number = 0; i < 100; i++) {
trace(NumberUtilities.getUnique( ));


4.9.模拟骰子


4.9. 模拟骰子


问题
我要模仿掷骰子
解决办法
用NumberUtilities.random( ) 方法产生指定范围的随机数
讨论

用 random( ) 方法产生整数来模拟掷骰子,这在很多游戏中经常用到,这次我们在ActionScript中实现


一般我们产生随机数然后保存它在使用,如果要重新使用存在的随机数,应保存它而不是再产生新的随机数。注意下面两种情况,第一种,dice总是die1和die2的之和:
var die1:uint = NumberUtilities.random(1, 6);
var die2:uint = NumberUtilities.random(1, 6);
var dice:uint = die1 + die2;
下面的情况,dice和die1和die2没有关系,换句话说,即使die1和die2加起来等于7,dice也不会等于它:
var die1:uint = NumberUtilities.random(1, 6);
var die2:uint = NumberUtilities.random(1, 6);
var dice:uint = NumberUtilities.random(1, 6) + NumberUtilities.random(1, 6);
NumberUtilities.random( ) 还可以模拟多边的骰子:
var die1:uint = NumberUtilities.random(1, 15);
下面的例子模拟了一个骰子



package {

import flash.display.Sprite;
import flash.text.TextField;
import flash.events.MouseEvent;
import ascb.util.NumberUtilities;

public class NumbersAndMath extends Sprite {

var _die:Sprite;
var _value:uint;

public function NumbersAndMath( ) {
_die
= new Sprite( );
addChild(_die);
_die.addEventListener(MouseEvent.CLICK, rollDie);
rollDie(
null);
}


private function rollDie(event:MouseEvent):void {
_value
= NumberUtilities.random(1, 6);
_die.graphics.clear( );
_die.graphics.lineStyle( );
_die.graphics.beginFill(
0xFFFFFF);
_die.graphics.drawRect(
0, 0, 50, 50);
_die.graphics.endFill( );
_die.graphics.beginFill(
0x000000);
if(_value == 1 || _value == 3 || _value == 5) {
_die.graphics.drawCircle(
25, 25, 4);
}

if(_value == 2 || _value == 3 || _value == 4 ||
_value
== 5 || _value == 6)
{
_die.graphics.drawCircle(
11, 11, 4);
_die.graphics.drawCircle(
39, 39, 4);
}

if(_value == 4 || _value == 5 || _value == 6) {
_die.graphics.drawCircle(
11, 39, 4);
_die.graphics.drawCircle(
39, 11, 4);
}

if(_value == 6) {
_die.graphics.drawCircle(
11, 25, 4);
_die.graphics.drawCircle(
39, 25, 4);
}

}


}

}



4.8.模拟硬币投掷


4.8. 模拟硬币投掷

问题
我要模拟硬币投掷或布尔事件来达到50%几率成功。
解决办法
用NumberUtilities.random( ) 方法产生 0 到 1的整数,根据每种可能得出结果。
讨论

用 random( ) 方法产生指定范围的随机整数,能够产生两个结果对应硬币的正面和反面状态,在程序里我们用0代表一个状态,1代表另一状态,当然你用1和2也是可以的,总之是2个状态,这样就能模拟硬币投掷了:



package {

import flash.display.Sprite;
import flash.text.TextField;
import flash.events.MouseEvent;
import ascb.util.NumberUtilities;

public class CoinExample extends Sprite {

private var _field:TextField;

public function CoinExample( ) {
_field
= new TextField( );
_field.autoSize
= "left";
addChild(_field);
var circle:Sprite
= new Sprite( );
circle.graphics.beginFill(
0, 100);
circle.graphics.drawCircle(
100, 100, 100);
circle.graphics.endFill( );
circle.addEventListener(MouseEvent.CLICK, onClick);
addChild(circle);
}


private function onClick(event:MouseEvent):void {
var randomNumber:Number
= NumberUtilities.random(0, 1);
_field.text
= (randomNumber == 0) ? "heads" : "tails";
}


}

}



记录正反面出现次数:



package {

import flash.display.Sprite;
import flash.text.TextField;
import ascb.util.NumberUtilities;

public class CoinTest extends Sprite {

private var _field:TextField;

public function CoinTest( ) {
_field
= new TextField( );
_field.autoSize
= "left";
addChild(_field);
var heads:Number
= 0;
var tails:Number
= 0;
var randomNumber:Number;
for(var i:Number = 0; i < 10000; i++) {
randomNumber
= NumberUtilities.random(0, 1);
if(randomNumber == 0) {
heads
++;
}

else {
tails
++;
}

}

_field.text
= "heads: " + heads + ", tails: " + tails;
}


}

}



如果要测试随机数,应该保存在变量中,像上面的代码,下面代码的统计是错误的,因此else if每次都产生新的随机数:



package {

import flash.display.Sprite;
import ascb.util.NumberUtilities;

public class RandomLetter extends Sprite {

public function RandomLetter( ) {
for(var i:Number = 0; i < 10000; i++) {
trace(getRandomLetter( ));
}

}


private function getRandomLetter( ):String {
if(NumberUtilities.random(0, 2) == 0) {
return "A";
}

else if(NumberUtilities.random(0, 2) == 1) {
return "B";
}

else if(NumberUtilities.random(0, 2) == 2) {
return "C";
}

// It's possible that none of the preceding will evaluate to true,
// and the method will reach this point without returning a valid
// string.
return "";
}

}

}



下面才正确:



package {

import flash.display.Sprite;
import ascb.util.NumberUtilities;

public class RandomLetter extends Sprite {

public function RandomLetter( ) {
for(var i:uint = 0; i < 10000; i++) {
trace(getRandomLetter( ));
}

}


private function getRandomLetter( ):String {
// random( ) 返回结果到变量上
var randomInteger:uint = NumberUtilities.random(0, 2);
if(randomInteger == 0) {
return "A";
}

else if(randomInteger == 1) {
return "B";
}

else if(randomInteger == 2) {
return "C";
}

return "";
}

}

}


4.7.生成随机数


4.7. 生成随机数

问题
我要生成随机数
解决办法
使用Math.random( ) 方法生成 0 到 .999999的随机数。还有,是用NumberUtilities.random( ) 方法可以生成指定范围的随机数
讨论

Math.random( ) 方法产生 0 到 0.999999999的浮点随机数。大多数情况我们希望产生整数而不是浮点数,还好随机值可以指定精度。
NumberUtilities.random( ) 方法产生指定的范围和精度,它接受三个参数:
minimum
可接受的最小值
maximum
可接受的最大值
roundToInterval
间隔值,可选
NumberUtilities 类在 ascb.util 包中,使用前导入。
例子如下:
// 产生 0 到 100的整数.
trace(NumberUtilities.random(0, 100));

// 产生 0 到 100的整数,间隔为5
trace(NumberUtilities.random(0, 100, 5));

trace(NumberUtilities.random(-10, 10, .1));

trace(NumberUtilities.random(-1, 1, .05));
package {

import flash.display.Sprite;
import ascb.util.NumberUtilities;
import flash.utils.Timer;
import flash.events.TimerEvent;

public class RandomNumberTest extends Sprite {

private var _total:uint;
private var _numbers:Object

public function RandomNumberTest( ) {
var timer:Timer = new Timer(10);
timer.addEventListener(TimerEvent.TIMER, randomizer);
timer.start( );
_total = 0;
_numbers = new Object( );
}

private function randomizer(event:TimerEvent):void {
var randomNumber:Number = NumberUtilities.random(1, 10, 1);
_total++;
if(_numbers[randomNumber] == undefined) {
_numbers[randomNumber] = 0;
}
_numbers[randomNumber]++;
trace("random number: " + randomNumber);
var item:String;
for(item in _numbers) {
trace("\t" + item + ": " + Math.round(100 * _numbers[item]/_total));
}
}

}
}


4.6.格式化货币数字


4.6. 格式化货币数字

问题
我要格式化货币,比如美元
解决办法
使用 NumberFormat.currencyFormat( ) 方法
讨论

不像其他语言,比如ColdFusion,ActionScript 没有提供内建的函数格式化货币数字。自定义类NumberFormat 包括一个currencyFormat( )方法。
currencyFormat( ) 至少需要一个参数,看下面的简单代码:
var styler:NumberFormat = new NumberFormat( );
trace(styler.currencyFormat(123456));
在英文操作系统上运行结果如下:
$123,456.00
和 format( ) 方法类似,currencyFormat( ) 方法根据自动本地化设置进行格式化。因此,如果上面的代码在西班牙语操作系统上运行结果如下:
123.456,00
覆盖自动本地化方法和format()类似:
使用 Locale 对象作为currencyFormat( )的第二个参数.
赋全局变量给 Locale.slanguage 和 Locale.svariant 属性
使用字符对象作为currencyFormat( )的第二个参数.


currencyFormat( ) 的字符对象比format( ) 方法中稍微不一样,它包括4个属性: group, decimal, currency, 和 before。group 和 decimal 属性和 format( ) 方法一样。currency 属性为货币符号, before 为布尔值,表示货币符号的位置。
下面是currencyFormat( )的一些例子代码:
var styler:NumberFormat = new NumberFormat( );
trace(styler.currencyFormat(123456));
Locale.slanguage = "nl";
trace(styler.currencyFormat(123456));
trace(styler.currencyFormat(123456, new Locale("sv")));
trace(styler.currencyFormat(123456, {group: ",", decimal: ".", currency: "@", before: false}));

输出结果:
$123,456.00
123.456,00
123,456.00kr
123,456.00@


4.5.不使用掩码进行数字格式化


4.5. 不使用掩码进行数字格式化

问题
我不想使用掩码进行格式化
解决办法
用 NumberFormat 对象不设置掩码,调用 format( ) 方法
讨论

4.4 节讨论了各种复杂的数字格式化方法,但是能不能不用那么复杂呢,NumberFormat 类提供了一个简单的办法,只使用最简单的format( )方法:
var styler:NumberFormat = new NumberFormat( );
trace(styler.format(12.3));
trace(styler.format(123.4));
trace(styler.format(1234.5));
trace(styler.format(12345.6));
显示如下:
12.3
123.4
1,234.5
12,345.6
正像上一节所说的,仍然有本地化问题,本地化处理和上一节一样:
var styler:NumberFormat = new NumberFormat( );
Locale.slanguage = "fr";
trace(styler.format(1234, new Locale("en")));
trace(styler.format(12345, {group: ":", decimal: "|"}));
trace(styler.format(123456));

输出:
1,234
12:345
123.456


4.4.格式化输出


4.4. 格式化输出

问题
我要把数字进行格式化输出
解决办法
NumberFormat 类,设置掩码,然后调用 format( ) 方法。
讨论

经常会遇到需要在输出时在头部和尾部加0或空格来达到格式化输出的目的,比如显示时间或日期。比如要格式化输出6小时3分钟,显示为 6:03 或 06:03,而不是 6:3。而且还经常碰到输出时进行对齐等,这都需要进行格式化输出:
123456789
1234567
12345
虽然自己都可以做到这种效果,但是你会发现用 NumberFormat 对象更简单更灵活。 NumberFormat 类是个自定义类,可到http://www.rightactionscript.com/ascb下载。使用它须先导入:import ascb.util.NumberFormat:
接下来决定是什么掩码进行格式化,它可以是0,#,.,,,或者其他。
零 (0)
使用零作为占位符
井号 (#)
点号(.)
逗号 (,)
下面看一下例子:掩码如下:
##,###.0000
格式化如下数字:1.2345, 12.345, 123.45, 1234.5, 和 12345, 结果:
1.2345
12.3450
123.4500
1,234.5000
12,345.0000
通过构造函数直接传入参数作为掩码:
var styler:NumberFormat = new NumberFormat("##,###.0000");
另外,使用属性进行修改掩码:
styler.mask = "##.00";
设置好掩码,调用 format( ) 方法就可以格式化数字了:
trace(styler.format(12345);
var styler:NumberFormat = new NumberFormat("#,###,###,###");
trace(styler.format(1));
trace(styler.format(12));
trace(styler.format(123));
trace(styler.format(1234));
styler.mask = "#,###,###,###.0000";
trace(styler.format(12345));
trace(styler.format(123456));
trace(styler.format(1234567));
trace(styler.format(12345678));
trace(styler.format(123456789));

输出如下:
1
12
123
1,234
12,345.0000
123,456.0000
1,234,567.0000
12,345,678.0000
123,456,789.0000
NumberFormat 对象自动本地化返回值。如果Flash播放器在英文操作系统上运行, NumberFormat 类是用逗号和点号,如果在法语操作系统上运行,正好相反,使用点号和逗号,因此需要覆盖自动本地化:
有几种方法覆盖自动本地化设置:
把Locale 对象作为format( ) 方法的第二个参数,format( ) 方法根据Locale 对象的设置进行格式化。Locale 对象使用两个参数,第一个是语言代码,如en,第二个参数为国家代码,如EN.
通过全局设置Locale.slanguage 或 Locale.svariant 属性就不需要给format( ) 方法传递参数了
使用符号对象作为format( )的第二个参数,包括两个属性:group和decimal。该方法适合没有用Locale对象进行全局设置下使用。
Locale 类在 ascb.util 包中,使用前导入。
下面的代码展示了三种方法:
var styler:NumberFormat = new NumberFormat("#,###,###,###.00");
Locale.slanguage = "fr";
trace(styler.format(1234));
trace(styler.format(12345, {group: ",", decimal: "."}));
trace(styler.format(123456));
Locale.slanguage = "en";
trace(styler.format(1234567));
trace(styler.format(12345678, new Locale("es", "ES")));
trace(styler.format(123456789, {group: "|", decimal: ","}));
结果:
1.234,00
12,345.00
123.456,00
1,234,567.00
12.345.678,00
123|456|789,00


2007年1月19日 星期五

4.3.四舍五入


4.3. 四舍五入

问题
我要进行四舍五入或取近似值.
解决办法
Math.round( ) 进行四舍五入, Math.floor( )Math.ceil( ) 进行上下近似值。NumberUtilities.round( ) 方法可自定义取值。
讨论

很多情况我们需要得到整数部分而不是带有小数的浮点数。比如计算出结果为 3.9999999 ,期望的结果应该是4.0。
Math.round( ) 方法进行四舍五入计算:
trace(Math.round(204.499)); // 显示: 204
trace(Math.round(401.5)); // 显示: 402
Math.floor( ) 方法去掉小数部分,Math.ceil( ) 方法去掉小数部分后自动加1:
trace(Math.floor(204.99)); // 显示: 204
trace(Math.ceil(401.01)); // 显示: 402
如果我想要把90.337 四舍五入到 90.34,可以这么写:
trace (Math.round(90.337 / .01) * .01); //显示: 9.34
trace (Math.round(92.5 / 5) * 5); // 显示: 95
trace (Math.round(92.5 / 10) * 10); // 显示: 90
更好的办法是用自定义函数NumberUtilities.round( ) ,它需要两个参数:
number :要舍入的数字
roundToInterval :间隔值
NumberUtilities 类在 ascb.util 包中。
imported ascb.util.NumberUtilities导入
trace(NumberUtilities.round(Math.PI)); // Displays: 3
trace(NumberUtilities.round(Math.PI, .01)); // Displays: 3.14
trace(NumberUtilities.round(Math.PI, .0001)); // Displays: 3.1416
trace(NumberUtilities.round(123.456, 1)); // Displays: 123
trace(NumberUtilities.round(123.456, 6)); // Displays: 126
trace(NumberUtilities.round(123.456, .01)); // Displays: 123.46


4.2.不同数字类型之间的转换


4.2. 不同数字类型之间的转换

问题
我要把当前数字类型转换为别的数字类型
解决办法
parseInt( ) 函数把字符串转换为十进制数,用 Number, uint, 或 int 对象的 toString( ) 方法转换为字符串。
讨论

在ActionScript中不管你怎么设置数字,它的内部结果总是以十进制存贮:
// 创建颜色对象
var pink:ColorTransform = new ColorTransform( );

// 用十六进制设置 RGB
pink.rgb = 0xF612AB;

// 显示这个值时:16126635
trace(pink.rgb);
如果你要输出为其他表示法,用 toString( radix ) 方法
下面的例子用构造uint对象,输出不同的格式:
// radix 为 2, 输出二进制
trace(new uint(51).toString(2)); // 显示: 110011
// radix 为 16, 输出十六进制
trace(new uint(25).toString(16)); // 显示:19
var quantity:Number = 164;
trace(quantity.toString(16)); // 显示: a4


下面的例子设置ColorTransform 对象的RGB值,调用 toString( ) 以十六进制显示:
var pink:Color = new ColorTransform( );
pink.rgb = 0xF612AB;
trace(pink.rgb.toString(16)); // 显示:f612ab
toString( ) 方法的参数值的合法范围在2到36,如果没有指定参数值,默认为10。
和 toString( ) 相反的是 parseInt( ) 函数。它把指定的字符串转换为数字。
下面的代码把各种字符串,输出十进制数。
trace(parseInt("110011", 2)); // 显示: 51
trace(parseInt("19", 16)); // 显示: 25
trace(parseInt("17", 10)); // 显示: 17
如果不指定字符串进制,默认为十进制,除非在字符串前加上0x, 0X, 或0:
trace(parseInt("0x12")); // 显示: 18
trace(parseInt("017")); // 显示: 15
下面的例子给出的字符串格式和指定进制冲突,这时会默认为十进制
// 但是下面的字符串是不合法的数字,因此返回0
trace(parseInt("0x12", 10)); // 显示: 0
下面的字符串为八进制,但指定为十进制,因此系统默认字符串为十进制,而不是八进制。
trace(parseInt("017", 10)); // 显示 17
trace(parseInt("A9FC9C")); // NaN


2007年1月18日 星期四

4.1.数字的不同表现形式


4.1. 数字的不同表现形式

问题
我要指定数字为十进制,八进制或十六进制
解决办法
十六进制以0x开头,八进制以0开头,二进制不能直接表示,可以用等价的八进制或十六进制或用 parseInt( ) 函数转换字符为数字。
讨论

ActionScript 中各种格式使用是很方便的。比如,如果你要设置Sprite.rotation 属性,最好是用十进制数:
rectangleSprite.rotation = 180;
另一方面,十六进制经常表示 RGB 颜色。例如,给 ColorTransform 对象的rgb属性使用十六进制数:
var pink:ColorTransform = new ColorTransform( );
pink.rgb = 0xF612AB;
0X或0x开头的数字为十六进制数,十六进制有0到9和A到F字符组成,没有大小写之分。
0开头的数字为八进制,有0到7组成;例如,0777 是个八进制数。大多数开发者不怎么使用八进制,基本上都是使用十进制,除了颜色值用十六进制表示。
二进制只有0和1组成,虽然不能直接表示它,但你可以等效的十六进制来表示它,比如二进制1111等于十六进制F,11111111等于FF,二进制数在为操作(&, |, ^, >>, <<, >>>)上经常用。.


4.0.简介


4.0. 简介

数字的用法非常广泛,有多种表示形式,如十进制,十六进制,每种表示都有其特定用处,比如,十六进制经常来表示 RGB 颜色值。(看4.2关于如何在各种表示法之间进行转换) 。
数字和数学紧密相关,没有数学运算,Flash将非常迟钝。像加法和减法等简单运算在ActionScript 经常用到,还有些运算,如随机数生成也是很常用的。
ActionScript 3.0 有三种基本数字类型: number, int,uint。 number 对应浮点数, int 和 uint 对应整数。 int 和 uint 的区别就是uint 代表无符号整数。


第三章. 运行时环境


第三章. 运行时环境




3.12.处理系统安全


3.12. 处理系统安全

问题
我要在应用程序中载入其他域的swf文件,并且允许它访问程序中的 ActionScript
解决办法
使用flash.system.Security.allowDomain( ), flash.system.Security.allowInsecureDomain( ),或 一个政策文件。
讨论

很多情况下应用程序有多个分布在不同域里的swf组成。如果你要载入外部域的swf文件,需要通过 flash.system.Security.allowDomain( ), flash.system.Security.allowInsecureDomain( ), 或一个政策文件设定
假设accessing.swf 在mydomain.com,它要访问otherdomain.com中的accessed.swf中的一个变量,而默认accessed.swf是不允许外部域的swf访问它,为了解决这个问题,在accessed.swf中加入以下语句:
flash.system.Security.allowDomain("http://mydomain.com");
允许指定的域可以访问它。
也许你会注意到,被载入的swf如果要访问载入它的swf是不可以的,同样,载入它的swf也要加入上面的语句设置。
域名可以是字符串形式,也可以使IP地址。如果你想让所有域都能访问它,可以设置为 "*"。 However, 但这样做可能会导致安全问题,不推荐。
如果 accessed .swf 文件在基于https://的服务器里,默认它不能被基于http://的域访问,设置flash.system.Security.allowDomain( )也没用,这时应该使用flash.system.Security.allowInsecureDomain( ) 设置非安全的http域可以访问。
这个办法虽好,但是如果经常变动域名就要重新编译swf文件就麻烦了,最好的办法是创建一个策略文件.
该策略文件是一个 XML 文件,列出了被允许的域:
<?xml version="1.0"?>
<!-- http://www.mydomain.com/crossdomain.xml -->
<cross-domain-policy>
<allow-access-from domain="www.otherdomain.com" />
<allow-access-from domain="*.adobe.com" />
<allow-access-from domain="123.45.67.89" />
</cross-domain-policy>
该文件被命名为 crossdomain.xml。通过 flash.system.Security.loadPolicyFile( )读取文件,参数为指定 crossdomain.xml 文件的URL字符串。
指定任何域都可访问:
<allow-access-from domain="*" />
阻止任何域访问:
<cross-domain-policy>
</cross-domain-policy>


3.11.提示用户改变播放器设置


3.11. 提示用户改变播放器设置

问题
我要打开用户的Flash播放器设置对话框窗口
解决办法
使用 flash.system.Security.showSettings( ) 方法.
讨论

flash.system.Security.showSettings( ) 方法打开播放器设置对话框,它包含多个标签,也可以调用该方法时传递参数给它直接打开相应标签,该参数字符串是 flash.system.SecurityPanel 类的静态属性:
SecurityPanel.CAMERA
摄像机面板


SecurityPanel.DEFAULT
默认面板


SecurityPanel.LOCAL_STORAGE
本地存储面板


SecurityPanel.MICROPHONE
话筒面板


SecurityPanel.PRIVACY
安全控制面板


SecurityPanel.SETTINGS_MANAGER
设置管理面板会打开浏览器进行更多设置
如果没有传递参数,默认使用 SecurityPanel.DEFAULT. 下面的例子打开本地存储面板:
flash.system.Security.showSettings(SecurityPanel.LOCAL_STORAGE);


3.10.检测设备视频


3.10. 检测设备视频

问题
我要确定目标设备是否可以播放视频
解决办法
使用 flash.system.Capabilities 类的hasEmbeddedVideo, hasStreamingVideo, 和 hasVideoEncoder 属性
讨论

检测用户端是否能播放视频也同样重要,使用flash.system.Capabilities.hasStreamingVideo 属性检测是否能播放视频流。如果返回 false,就可以让用户下载内嵌视频的.swf文件,在这之前也要用flash.system.Capabilities.hasEbeddedVideo 确定,看下面的代码:
if(flash.system.Capabilities.hasStreamingVideo) {
// 播放视频流
}
else if(flash.system.Capabilities.hasEmbeddedVideo) {
// 下载内嵌视频的swf文件
}
else {
//
}
如果应用程序需要视频流编码,比如传送摄像机视频流,还要确定系统是否具有编码能力,使用 flash.system.Capabilities.hasVideoEncoder 属性检测。

2007年1月17日 星期三

3.9.检测设备音频


3.9. 检测设备音频

问题
我要确定播放器正在使用的音频设备.
解决办法
使用 flash.system.Capabilities 类的 hasAudiohasMP3 属性
讨论

如果用户系统有播放音频的能力,则 flash.system.Capabilities.hasAudio 属性就返回True 。这实际上很重要,如果目标设备不支持音频,那就要避免强制用户下载音频内容(因此音频内容都比较大)。
// 只有当播放器可以播放声音才再如包含声音的.swf
if (flash.system.Capabilities.hasAudio) {
content = "sound.swf";
} else {
content = "silent.swf";
}

即时系统有播放音频能力也不意味着它有播放mp3的能力。因此当发布含有mp3内容时应用flash.system.Capabilities.hasMP3 属性检测下目标设备。
if (flash.system.Capabilities.hasMP3) {
var url:URLRequest = new URLRequest("sound.mp3");
sound = new Sound(url);
sound.play( );
} else {
// code to load an external .swf containing a ADCP sound
}


3.8.隐藏Flash播放器的菜单项


3.8. 隐藏Flash播放器的菜单项

问题
我要隐藏右键菜单
解决办法
不能够完全改变Flash播放器的右键弹出菜单,但是可以设置stage.showDefaultContextMenu 属性为false来最小化菜单项。
讨论

默认下Flash播放器右键弹出菜单的项目有:
Zoom In
Zoom Out
Show All
Quality (Low, Medium, or High)
Settings
Print
Show Redraw Regions (debug 版本)
Debugger (debug 版本)
About Adobe Flash Player 9


通过下面的语句可以移除许多项目,Settings和About是不能移除的:
stage.showDefaultContextMenu = false;
遗憾的是,Flash没有提供方法完全禁用右键菜单。


3.7.改变对齐方式


3.7. 改变对齐方式

问题
我要改变影片的对齐方式
解决办法
使用 stage.align 属性
讨论

默认下Flash电影会居中显示。可以利用任何可视化对象的stage.align属性来重新设置电影的对齐方式。flash.display.StageAlign 类的属性:




















































ValueVertical alignmentHorizontal


StageAlign.TOP


TopCenter


StageAlign.BOTTOM


BottomCenter


StageAlign.LEFT


CenterLeft


StageAlign.RIGHT


CenterRight


StageAlign.TOP_LEFT


TopLeft


StageAlign.TOP_RIGHT


TopRight


StageAlign.BOTTOM_LEFT


BottomLeft


StageAlign.BOTTOM_RIGHT


BottomRight

这里没有水平和垂直都居中的模式,其实,默认模式就是它了,但如果你改变了对其方式又想回到默认模式这时后只能传递空字符串""。


下面的类展示了缩放和对齐效果:
package {
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.display.StageAlign;

public class ExampleApplication extends Sprite {
public function ExampleApplication( ) {

stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_RIGHT;
graphics.beginFill(0xff0000);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
graphics.endFill( );
}
}
}


3.6.缩放影片


3.6. 缩放影片

问题
我想让影片适应屏幕大小
解决办法
使用 stage.scaleMode 属性
讨论

这里有几种缩放模式:exactFit, noBorder, noScale, 和 showAll。为了避免编写上错误,这些字符串都成为了flash.display.StageScaleMode 类的静态属性:EXACT_FIT, NO_BORDER, NO_SCALE, 和 SHOW_ALL.
Flash 播放器默认的缩放模式是showAll。这种模式会按照影片原始比例进行缩放以适应播放器大小。这样如果播放器的比例和影片的比例不一致就会导致电影边框的出现。设置应用程序的缩放模式:
stage.scaleMode = StageScaleMode.SHOW_ALL;
注意到 stage 并不是个全局对象,但是它是任何可视化对象的一个属性,因此这个语句在sprite类或继承自 DisplayObject 类里都可以。
noBorder 模式在保持原始比例下进行缩放以适应播放器,但是,如果播放器和影片比例不匹配,影片显示不下的会被剪切掉,使用下面的语句设置:
stage.scaleMode = StageScaleMode.NO_BORDER;
exactFit 模式缩放影片适应播放器,它改变了电影原始比例,如果必要,它会匹配播放器,这样电影总是填充整个播放器,但是这样电影中的元素可能会扭曲,代码如下:
stage.scaleMode = StageScaleMode.EXACT_FIT;
noScale 模式即不进行缩放,保持原始比例。使用该模式不要忘了设置对齐方式(看 3.7 节):
stage.scaleMode = StageScaleMode.NO_SCALE;
scaleMode 属性值并不影响右键菜单里功能,不过你可以禁用菜单里的缩放功能,看3.8 节。


3.5.检测显示设置


3.5. 检测显示设置

问题
我要知道客户机的显示设置情况
解决办法
使用 system.capabilities 对象的 screenResolutionXscreenResolutionY 属性
讨论

screenResolutionX 和 screenResolutionY 属性返回桌面的显示分辨率:
trace(flash.system.Capabilities.screenResolutionX);
trace(flash.system.Capabilities.screenResolutionY);
// 1024
// 768
有了这些值,你可以决定怎样显示flash影片。这一点对于Flash播放器也是很重要的,例如,手机屏幕和电脑屏幕尺寸是不同的,因此你要根据情况载入不同的尺寸的内容。
var resX:int = flash.system.Capabilities.screenResolutionX;
var resY:int = flash.system.Capabilities.screenResolutionY;
if ( (resX <= 240) && (resY <= 320) ) {
var url:String = "main_pocketPC.swf";
}
else {
var url:String = "main_desktop.swf";
}

loader.load(new URLRequest(url));
利用分辨率还可以居中你的弹出窗口:
var resX:int = flash.system.Capabilities.screenResolutionX;
var resY:int = flash.system.Capabilities.screenResolutionY;

//设置窗口的宽和高
var winW:int = 200;
var winH:int = 200;

// 设置窗口起始坐标
var winX:int = (resX / 2) - (winW / 2);
var winY:int = (resY / 2) - (winH / 2);

// 创建代码,然后传递给 URLLoader.load( )
// 打开新浏览器窗口
var jsCode:String = "javascript:void(
newWin=window.open('http://www.person13.com/'," +
"'newWindow', 'width=" + winW +
", height=" + winH + "," +
"left=" + winX + ",top=" + winY + "'));";

// 使用 URLLoader 对象调用 JavaScript 函数
urlLoader.load(new URLRequest(jsCode));
另外,得到屏幕分辨率可以确定是否缩放Flash影片。例如,用户把屏幕分辨率调高了,这时字体就变得小了。


3.4.检测系统语言


3.4. 检测系统语言

问题
我想知道客户端系统使用什么语言和输入法
解决办法
使用 flash.system.Capabilities.language 属性和 flash.system.IME
讨论

flash.system.Capabilities.language 属性给出客户端系统的语言,返回两个 ISO-639-1 字符(如 "fr" 代表 French). 有些国家代码两个字符是不合适的,比如( "zh-CN" 代表 Simplified Chinese 和 "zh-TW" 代表 Traditional Chinese).


下面的代码展示如何使用语言属性:
// Example output: en-US
trace(flash.system.Capabilities.language);
var greetings:Array = new Array( );
greetings["en"] = "Hello";
greetings["es"] = "Hola";
greetings["fr"] = "Bonjour";
var lang:String = flash.system.Capabilities.language.substr(0, 2);
if (greetings[lang] == undefined) {
lang = "en";
}
trace(greetings[lang]);
如果要创建国际化的Flash,可以把文本保存在数组里,根据语言动态显示,或者直接做成多个Flash版本(每个语言一个),如 myMovie_en.swf, myMovie_es.swf, myMovie_fr.swf, 等.
//从 capabilities 对象上得到语言值
var lang:String = System.capabilities.language.substr(0, 2);
// 创建支持语言数组
var supportedLanguages:Array = ["en", "es", "fr"];
// 设置默认语言.
var useLang:String = "en";

//循环匹配,如果找到,设置 useLang
for (var i:int = 0; i < supportedLanguages.length; i++) {
if (supportedLanguages[i] == lang) {
useLang = lang;
break;
}
}

// 载入对应Flash
var movieURL:String = "myMovie_" + useLang + ".swf");
还有一点也很重要,比如用户使用的输入语言,比如中文,日文,韩文,输入这些字符需要输入法,这时特定操作系统的一部分。
flash.system.Capabilities.hasIME 返回用户是否使用输入法. flash.system.IME 类返回关于输入法的信息。flash.system.IME.enabled 属性设置用户是否可以使用输入法。在有些操作系统和版本上你可以发送字符串给IME来转换成正确的字符,接受IME的返回,但这不是所有操作系统都支持的,最好检测下先。


3.3.检测播放器类型


3.3. 检测播放器类型

问题
我想知道播放器类型You want to know what type of Flash Player the .swf is being run from.
解决办法
使用 flash.system.Capabilities.playerType 属性.
讨论

播放器的类型有:
浏览器插件形式存在于 Mozilla 或 Firefox
ActiveX 控件形式存在于Internet Explorer
独立播放器
外部播放器,它与Flash IDE进行交互。


这些都是.swf 运行的环境,如果你要使用脚本进行交互,这就需要知道应用程序到底在Internet Explorer 或其他的浏览器运行。如果在独立播放器里运行,那么JavaScript等脚本就不管用了。
检测播放器类型,察看flash.system.Capabilities.playerType 的值。它可能是PlugIn, ActiveX, StandAlone, External
if(flash.system.Capabilities.playerType == "Plugin") {
// do actions for Mozilla, etc. browsers
}
else if(flash.system.Capabilities.playerType == "ActiveX") {
// do actions for IE
}
else {
// do actions for no browser
}


3.2.检测操作系统


3.2. 检测操作系统

问题
我要知道客户端的操作系统。
解决办法
使用flash.system.Capabilities.os 属性
讨论

ActionScript 3.0中,flash.system.Capabilities.os 属性返回操作系统名称和版本字符串。值可能包括Windows XP, Windows 2000, Windows NT, Windows 98/Me, Windows 95, 和 Windows CE. 在苹果机上,字符串包括版本号,比如 Mac OS 9.2.1 或 Mac OS X 10.4.4.
你可能基于操作系统做一些特殊处理,比如,根据当前系统载入特定的图标,或只是记录下用户的操作系统来统计。
下面的代码展示检测操作系统:
var os:String = System.capabilities.os.substr(0, 3);
if (os == "Win") {
// Windows-specific code goes here
} else if (os == "Mac") {
// Mac-specific code goes here
} else {
// Must be Unix or Linux
}


3.1.检测播放器版本


3.1. 检测播放器版本

问题
我要确定客户机上的Flash播放器版本
解决办法
可以使用Flash Player 检测工具。(http://www.adobe.com/software/flashplayer/download/detection_kit).
讨论

检测客户机上的Flash版本是个多年以来的难题,有各种开发者提供的方法,一般有三种方法:
基于浏览器脚本检测
服务端检测
ActionScript 检测


第一种方法使用JavaScript 或 VBScript 检测Flash 播放器版本。但是很多脚本在不同的平台不同的浏览器上会有兼容性问题出现。
服务端检测也有局限性,如果你无权限创建服务端脚本,这就很困难了。
大多基于 ActionScript 的检测技术不能在ActionScript 3.0 上用了。 ActionScript 3.0 有一套自己的检测客户端版本的方法,那就是 flash.system.Capabilities.version 属性。但是它不能检测Flash Player 8.5 之前的版本。
还好Adobe 已经考虑到所有这些问题,推出了 Flash Player Detection Kit 来指导你用最好的办法检测播放器版本。
检测包里包含文档和各种解决办法,包括VBScript 和 JavaScript 例子;ActionScript 检测;还有服务端的 ColdFusion 和 PHP 脚本检测。
基于ActionScript 的检测是比较好的,它可以支持到Flash播放器4,它使用一个 Flash 4 .swf t来检测当前版本,你所要做的就是在脚本里设置最小的版本变量,如果当前版本高,它会调用指定的内容。


3.0.简介


3.0. 简介

Flash Player 9 关于控制运行时环境提供了更多的信。The flash.system.Capabilities 类有许多静态方法返回关于播放器和计算机的信息,比如操作系统,语言,音频和视频。还有其他的类如flash.display.Stage 和 flash.system.Security 控制其他一些元素如播放器右键菜单和设置对话框。 flash.display.Stage 类也控制影片剪辑缩放和对齐。
Flash Player 9 可能是自 Flash Player 7 之后最具重要意义的一次升级。它比以往提供了更多能力控制上下文菜单。在Flash Player 7里,用 ContextMenu 类,可以控制右键菜单,可以添加或删除菜单项。


2007年1月15日 星期一

第二章. 自定义类


第二章. 自定义类



2.8.发送事件


2.8. 发送事件

问题
我要发送事件
解决办法
继承 flash.events.EventDispatcher 然后调用 dispatchEvent( ) 方法
讨论

事件在对象之间的通讯起到至关重要的作用,有了它才能开发出功能强大的系统。Flash Player 9, 的flash.events.EventDispatcher类有一套事件发送机制。所有的事件都继承自EventDispatcher (比如 NetStream and Sprite). 如果你要定义个类要发送事件也要继承EventDispatcher,如:
package {
import flash.events.EventDispatcher;
public class Example extends EventDispatcher {

}
}

EventDispatcher 类有个公共方法addEventListener( )removeEventListener( ), 通过它的子类来注册事件监听。 EventDispatcher 还有个 protected 方法 dispatchEvent( ) 来发送事件。dispatchEvent( ) 方法至少需要flash.events.Event 对象或Event的子类作为参数。


2.7.创建常量


2.7. 创建常量

问题
我要怎么申明常量
解决办法
和申明属性差不多,只是在前面多了const 关键字
讨论

常量的值一旦定义就不可改变,这在有时候是很有用的。比如你有个复合的值需要经常用到,这时就可以把它当作简单的标示直接引用。Math.PI 就是个常量,MouseEvent.MOUSE_UP也是常量,包含mouseUp值,设定这些常量可以减少出错,比如下面的代码错误你难以觉察出来:
// 下面的代码没错,但是mouseUp 被写成了 mousUp,因此该代码无效。
addEventListener("mousUp", onMouseUp);
但是使用常量,编译器就会给出错误提示:
// 导致编译器错误
addEventListener(MouseEvent.MOUS_UP, onMouseUp);
常量也可以有static和public修饰符,这时常量一申明就要赋值:
static public const EXAMPLE:String = "example";
按照约定,常量都要大写,这样可以和其他变量或属性区别开来。


2007年1月14日 星期日

2.6.覆盖父类方法


2.6. 覆盖父类方法

问题
我要对从父类继承过来的方法进行重新实现。
解决办法
父类的方法必须申明为public 或 protected。当申明子类的实现时使用 override 修饰符
讨论

通常子类继承父类的所有方法而不做任何修改,但有些情况,继承过来的方法需要重新申明,实现与父类的方法不同。这时就要覆盖方法,该方法必须加上override 修饰符。如下面的例子,首先定义一个父类Superclass:



package {
public class Superclass {
public function Superclass( ) {}
public function toString( ):String {
return "Superclass.toString( )";
}

}

}



下一步,定义Subclass 继承自Superclass:



package {
public class Subclass extends Superclass {
public function Subclass( ) {}
}

}



默认情况下,Subclass 继承 Superclass 实现的toString( ) 方法:
var example:Subclass = new Subclass( );
trace(example.toString( )); // 显示: Superclass.toString( )
如果你要 toString( ) 方法返回不同的值,就要覆盖它,如下:



package {
public class Subclass extends Superclass {
public function Subclass( ) {}
override
public function toString( ):String {
return "Subclass.toString( )";
}

}

}



覆盖方法,则该方法必须与父类的方法完全相同,包括参数数量和类型及返回类型,如果不一致,编译器就会报错。
有时你要覆盖方法是为了实现完全不同的功能,但有时你只是要增加一些东西,这时你可以调用父类方法:
super.methodName( );


2.5.创建子类


2.5. 创建子类

问题
我要创建派生类
解决办法
使用extends关键字继承已有的类
讨论

如果新建的类和已有的类拥有公共特性但是又比已有类有更多细节,这时去重写所有的代码还不如从已有类中继承公共特性再添加专有代码。这时这个新类就是已有类的一个子类。
使用extends关键继承超类:
public class Subclass extends Superclass

子类可以引用任何超类中的 public 或 protected 的属性和方法,private 属性和方法是不能够访问的。
继承性是非常强大的功能,但是另一方面如何正确的使用继承也很重要。在写子类之前你要确定新类和超类是否已经有子类关系,这里有两种基本关系类型:继承和组合。你经常要确定类之间是"is a" 关系还是"has a" 关系:
"Is a" 关系是继承关系。如一个应用程序管理着一个图书馆的藏书。
"Has a" 关系是组合关系。大多数类使用组合,而且比继承更有伸缩性(然而需要更多代码)。例如一本书不是一个作者,但是它有一个作者。


图书馆有不同类型的藏品包括书和DVDs。很明显书和DVDs有不同的类型数据。书包括页数和作者,而DVDs则有播放时间数,演员,导演等等,但是也有一些公共数据,比如所有图书馆都有用一些唯一的数字编号或命名规则来管理这些藏品. 而且每个种类都有标题或名称,这时你可以定义个类来归纳这些公共信息:



package org.examplelibrary.collection {
public class LibraryItem {
protected var _ddc:String;
protected var _id:String;
protected var _name:String;

public function LibraryItem( ) {}

public function setDdc(value:String):void {
_ddc
= value;
}

public function getDdc( ):String {
return _ddc;
}


public function setId(value:String):void {
_id
= value;
}

public function getId( ):String {
return _id;
}


public function setName(value:String):void {
_name
= value;
}

public function getName( ):String {
return _name;
}

}

}



书和DVDs 都是LibraryItem的一个种类. 接下来很自然就是定义Book 类和 DVD 类,继承自 LibraryItem,Book 类类似于:



package org.examplelibrary.collection {
import org.examplelibrary.collection.LibraryItem;
public class Book extends LibraryItem {
private var _authors:Array;
private var _pageCount:uint;

public function Book( ) {}

public function setAuthors(value:Array):void {
_authors
= value;
}

public function getAuthors( ):Array {
return _authors;
}


public function setPageCount(value:uint):void {
_pageCount
= value;
}

public function getPageCount( ):uint {
return _pageCount;
}

}

}



默认下可以继承任何类,但是如果你不要这个类再被继承,可以添加final修饰符申明该类:
final public class Example




Technorati :

2.4.创建静态方法或属性


2.4. 创建静态方法或属性

问题
我要创建的方法和属性不需要类实例就能直接访问。
解决办法
使用static 修饰符申明属性或方法
讨论

默认下属性和方法是属于实例的,例如 Example 类定义了 _id 属性和 getId( ) 方法,那么每个Example 实例都有自己的_id 属性和getId( ) 方法。但是有种情况你希望属性或方法是和类相关联而不是类实例,也就说不管有多少个类实例,都只有一个公共属性或方法,这样的属性和方法称为静态属性和方法。
Flash 播放器的类中就有些这样的例子,比如 Math 类中定义了 round( ) 方法,round( ) 方法就是个静态方法,因此可以通过类直接访问:
trace(Math.round(1.2345));
Math 类包含全部是静态方法,但是类也可以同时含有静态方法和实例方法及属性。比如 String 类有多数实例属性和方法,然而fromCharCode( ) 方法是静态的,该方法返回字符码。
下面的代码申明了一个静态的私有的属性_example:
static private var _example:String;
修饰符的顺序没有关系,比如static private 和 private static 是一样的.
static最重要的用处就是在单态模式下,即类只能创建一个实例,单态类有一个private static 属性用来存储类实例,然后在一个 public static 方法中访问这个实例。


2.3.创建成员属性


2.3. 创建成员属性

问题
我要创建public 成员属性
解决办法
使用隐含 getters 和 setters.
讨论

正如2.1节中所说的那样属性应该被申明为 private 或 protected。 public 属性并不是什么好主意,因为他不能体现封装性。要尽量做好封装,这意味着类不能暴露他的内部细节,public 属性使开发者能轻易破坏类或类实例。下面的简单例子是用了Public 属性:



package {
public class Counter {
public var count:uint;
public function Counter( ) {
count
= 0;
}

}

}



构造一个Counter实例, 然后改变count 属性值,如:
var counter:Counter = new Counter( );
counter.count++;
但是,如果count属性被规定不能超过100,那么外部修改很可能无法保证,这样破坏了这个规定,一个办法就是设置 getters 和 setters,如下:



package {
public class Counter {
private var _count:uint;
public function Counter( ) {
_count
= 0;
}

public function getCount( ):uint {
return _count;
}

public function setCount(value:uint):void {
if(value < 100) {
_count
= value;
}

else {
throw Error( );
}

}

}

}


另一个办法就是用隐式getters 和 setters. 隐式 getters 和 setters 就像申明方法那样,但是看起来又像属性getter 语法如下:
public function get name( ):Datatype {

}

setter 语法:
public function set name(value:Datatype):void {

}
下面定义隐式 getter 和 setter 方法:



package {
public class Counter {
private var _count:uint;
public function Counter( ) {
_count
= 0;
}

public function get count( ):uint {
return _count;
}

public function set count(value:uint):void {
if(value < 100) {
_count
= value;
}

else {
throw Error( );
}

}

}

}


counter.count = 5;
trace(counter.count);


2.2.类的保存


2.2. 类的保存

问题
把类文件保存到哪里呢
解决办法
保存与包名称相符的目录中。
讨论

类文件保存在与包路径相符的目录中,比如:com.examplecorp.net.messaging.email.MessageManager必须保存在 com/examplecorp/net/messaging/email/目录下. 编译器就知道去哪里找类。还有编译器也必须知道根目录是什么。例如,编译器需要知道 com 目录在哪里,编译器是通过classpath来找到com目录。默认的classpath就是Flex或Flash项目的根目录。例如,如果com 目录和.fla文件或mxml文件的同一目录,编译器就能找到这些类。其实你也可以保存到其他目录,例如,如果你有个公共库被多个项目使用,难道要拷贝多份到每个项目中,其实你可以编辑项目中的classpath来加入该库,这样不需要拷贝就能找到你的自定义类了。
右键点击工程名,选择Properties,选择Build Path,在Source Path中添加和修改classpath就可以了。如果你只使用SDK,那么当编译项目时必须设置classpath。使用mxmlc (Flex SDK中包含的命令行编译器), 加上 -source-path 选项, 跟上类目录,例如:
mxmlc -source-path . C:\libraries ExampleApplication.as


2.1.创建自定义类


2.1. 创建自定义类

问题
我要编写自己的类
解决办法
保存一个以.as扩展名的新文件,类名和文件名相同,编写如下结构:
package package {
public class Class {

}
}
讨论

在ActionScript 3 中,类是最基本的编程结构,所以必须先掌握编写类的基础知识。对于初学者,所以得类都必须放在.as文件中,每个as文件里只能定义一个public 类,而且类名字要与文件名相同。比如:你的类名为 Example ,那么文件名必须为 Example.as.
在 ActionScript 3.0 中所有的类都必须放在包中。包是对类进行分类的单位,其意义相当于文件系统的目录。包路径相对于classpath(类路径),默认的类路径就是项目的根目录(就是包含mxml文件的所在目录),因此顶级的包目录就是项目根目录。包申明如下:
package name {

}
如果类定义在顶级包中,那么包名可以不指定,如:
package {

}
当类文件保存在子目录,那么包名就是它的保存目录,例如,文件保存在example目录,那么包这样申明:
package example {

}

如果类文件保存在 example 目录的子目录 subpackage, 应这样申明:
package example.subpackage {

}

包是很重要的,它可以避免类名称空间冲突。例如,有两个开发者写了两个类文件都叫MessageManager. 这两个类虽有相同名字,但是完成不同的任务,因此你不能把这两个类放在一起,如果这样做,编译器将不知道调用哪个,一个办法是取个唯一的类名字。
你可以取名字叫 EmailManager 和 BinarySocket- MessageManager, 这是可以的,但是如果你管理成千上万的类这时就很困难了。因此用包可以很好的解决这个问题,即使你有很多相同的类名,只要它们不在同一个包就不会冲突,如把 MessageManager 放在net.messaging.email 包另一个放在net.messaging.binarysocket 包中。
一般取包名都以自己的网站域名,这样可以最大限度避免和别人的包名相冲突。
当有多个项目公用一些类,那么这些类直接被放在主包 中的子目录中。例如,上面的MessageManager 类放在com.examplecorp.net.messaging.email 和 com.examplecorp.net.messaging.binary- socket 包中。
下一步就是申明类自身:
public class Name {

}

类申明必须在包内。下面的代码在顶级包中定义了叫 Example 的类:
package {
public class Example {

}
}

类主体在括号内定义,包括属性,方法。属性就是和类关联的变量,使用var关键字申明他们,属性也有修饰符指定其范围。修饰符有:


private
该属性只有类实例自身可访问.


public
该属性可以被任何类实例访问(若直接被类访问可设置成static)


protected
该属性只被自身类实例或派生类实例访问。


internal
该属性可被包内的类实例访问。
默认情况下是属性被指定为 internal ,除非自己指定修饰符。大多数情况,属性被指定为 private 或protected。按照习惯约定,private 和 protected 申明的属性名称都在前面加上下划线。看下面的例子:
package {
public class Example {
private var _id:String;
}
}

与类关联的还有方法,你可以使用function关键字像申明函数那样申明方法。和属性一样,方法也有修饰符(public, private, protected, internal)。如果方法被类实例访问可设置为public(直接被类访问则加上static).如果方法只在类内方法则被设置为 private 或 protected。下面的代码申明一个方法叫getId( ):
package {
public class Example {
private var _id:String;
public function getId( ):String {
return _id;
}
}
}

按照约定,方法名称的起始字符必须为小写。每个类都有个和自己类名相同的方法,该方法称为构造函数,用它为创建新的实例时进行初始化工作。在 ActionScript 3.0 中,所有的构造函数都是public ,不像标准的方法,构造函数不能有返回值,也不能申明有返回类型。下面的代码申明了构造函数:
package {
public class Example {
private var _id:String;
public function Example( ) {
_id = "Example Class";
}
public function getId( ):String {
return _id;
}
}
}

下面展示如何构造一个新的 Example 类实例:
var example:Example = new Example( );
trace(example.getId( ));
// 显示: Example Class


2.0.简介


2.0. 简介

ActionScript 3.0 最本质的东西就是类,也就说它是面向对象的。 ActionScript 3.0 在面向对象基础上重新构建了ActionScript 核心。如果在Flex上编写ActionScript 3.0,代码都被放在<mx:Script> 标签内,所有ActionScript 都必须以类的形式出现。这一章讨论在ActionScript 3.0 上编写自定义类。


第一章. ActionScript 3 语言基础


第一章. ActionScript 语言基础







Technorati :

2007年1月11日 星期四

常青图书管理系统 BETA V0.1


简短描述:

以前为学习Swing而写的,该图书管理系统是基于java开发,可运行在各种操作系统平台之上,如Windows/Linux/MacOS,采用Mysql数据库,安装细节请查看"说明.txt"文件



安装步骤:
安装细节请查看"说明.txt"文件


测试环境:
Windows XP/Linux FC4











下载地址



Technorati :

Eclipse Plugins Manager Beta 2 (Link's Only) 发布


eclipse 插件管理器 BETA 2,
EPM b2 改进如下:
1.修正beta 1的一些已知bug;2.可以自行添加link文件;3.根据插件目录自动生成link文件;4.去掉原来的link文件保存目录,很多人都用错这个,哎,现在更简单方便了;5.增加 内存堆分配参数;
呵呵,免去新手安装插件的麻烦,让你的eclipse真正高效率运行 :) for Win32和for Linux的都在压缩包里了,如发现BUG,请到blog上跟贴,谢谢。
安装步骤:
确定已经安装了jre,win32的双击就可以了,Linux的双击运行或运行run.sh测试环境:
Windows XP, MagicLinux 2.0 Final


EPM b2 For Windows 效果:



For Linux 的效果:



下载地址



Technorati :