2008年7月29日
最近由於種種緣故又要開始做點教育訓練的事,先趁現在有空把手邊的資料整理一下。
*Flex 的基礎架構
關於 flex 基本上常被問到的不外乎就是「如何可以學好它?」,要瞭解這個問題的答案基本上只要看懂下面這圖就ok了。
*Actionscript 該學的重點
從最底層看起,最下面的 actionscript 3是一切的基礎,它是 flash/flex 編程使用的唯一程式語言,因此任何人想學好 flex 第一件事絕對是先摸熟 actionscript 這個語言,包含:
1. 它的基本語法與結構(array, hash, loop, if else…)
2. DisplayList (DisplayObject, DisplayObjectContainer)與 Event system(bubbling, propagating…)
3. Sound, Video, NetConnection 與 Graphics class
掌握 as3 的精華後,接下來就可以進入 flex framework。
*Flex framework 的重點
基本上 flex framework 就是用 actionscript 寫成的框架,因此也可以把它看成是 as3的最好示範,看著 framework source 學 actionscript 也是挺不錯的,只是路會變很長。
Flex Framework 整個體系非常博大精深,通常一般人不太可能完整把它學完,只需要針對最常用到的部份熟悉就好,圖中列出的那三塊(component, managers, style/skin)就是我個人認為所有初學者最優先該學會的。
*Component 該學些什麼
Component 是整個 flex framework 的基礎,幾乎80% 的元素都是由 UIComponent 繼承而來,例如最根本的的 它本身就是一個 UIComponent,因此,熟悉 component 就成為學好 flex framework 最根本也最重要的基本功
Flex 內建了 二十幾個 UI controls, 例如 Button, DataGrid, HBox等,以種類來分,這些 components 可以概分為三大類:
-Controls: Button, DateChooser, Slider…
-Containers: Box, DividedBox, Panel…
-List: DataGrid, Tree, TileList…
初學者第一步至少該學會怎麼用這些元件,瞭解每個元件的 properties, events, styles, effects…,知道怎麼在手冊裏查它的 API 文件,以及何時該用何種元件。
進階一點,則是學會怎麼修改這些元件,例如繼承一個 Button 下來加上不同的功能,或是寫不同的 skin border 來改變它的外觀。
再更進階,則是開始研究元件的生命週期,瞭解每個元件是何時初始化,元件內部有那些關鍵指令與它們個別的功用,然後可以試著建立自已的 custom component。
這一關看起來容易但實際上最困難,因為 flex 的 component framework 寫的非常龐大,雖然亂中有序但要在混沌中看出隱藏的架構然後抓住重點整串提起,就非得有人帶著指引正確的途徑才比較可能達成。
*manager 是什麼
圖中最上方的第二塊就是 manager。
flex 裏有很多的 managers,負責做各種不同的工作(廢話…),幾個比較重要的包含:
-SystemManager:
它是每支 flex app 的根源,最先被下載,也最早啟動,由它進行一連串的 app boot流程
-StyleManager:
它負責整支app 的 css style 套用與 skin 生成,如果想玩動態 css 載換也靠它
-DragManager:
Flex最大的賣點就是 drag and drop,這個 manager 就是背後的英雄,初學者至少要學會怎麼處理 drag 行為的五個事件,以及如何在不同元件間做拖放;進階的玩家則應該深入研究這支 manager 是怎麼寫成的,詳細閱讀它的 source 會得到意想不到的無窮樂趣(如果你讀完卻沒有這種感覺,呃,那代表你該再多讀幾次,如果還是沒有,那請私下聯絡我 :D)
-ModuleManager:
使用 Flex 開發大型應用程式時,往往會將程式切割成許多小的 module, 這個 manager 就是負責載入並管理所有的 module (包含它的 class partition),初心者或許用不到,但有志深入的玩家一定要很熟。
-CursorManager:
這個用到的時機不是很多,但偶爾要換一下 cursor 時還是會用到,初學者至少要知道怎麼用指定的圖案去換掉系統cursor。
*Style/Skin 的重點
CSS style 與 skinning 是 Flex 最大的賣點之一,也是開發過程中較為麻煩也最耗時的部份。
初學者應該要徹底瞭解如何使用 CSS style 來打點一支 flex app 的外觀,換顏色、素材,使用外部 assets 修飾介面。
中階玩家則應該瞭解 skinning 的系統,包含 programmatic skinning 與 graphical skin,它們兩的差別?使用時機?如何客製化?
更高階的玩家則應該熟悉整個 Styling system 的運作模式,外加如何動態載入 css 在 runtime 換掉整個介面。
簡而言之,flex app 寫的好不好,外行人其實看不太出來,但一支 app UI 美不美則是一翻兩瞪眼,比較漂亮的那就先加十分
(當然,有一種情況是刻意用心去美化了介面結果弄巧成拙搞的怨聲載道人人喊打,但那種比較不多見,也不是每家公司都會搞到這步田地,就先不討論)
*學完基本功後下一步
在我的標準裏,當一個 developer 對上圖內每一塊都有中等程度的瞭解後,就算是完成 flex 養成教育,可以邁向下一個階段。
也就是開始熟悉 application 的製作手法,這包含
-瞭解至少一種以上的開發框架,例如 Cairngorm,老實說我對這個框架沒什麼好感(因為手法太複雜,只適合超複雜登月計畫或火星探勘時使用),但它結構設計良好,又是業界公認的聖盃,等於是專家們共通的語言,因此至少要先瞭解它在做什麼,將來在專業場合才好溝通(俗話說 know the rules so you know what you are breaking, 就是指這情況)
-接著可以看看比較簡單的手法,像 Riawave, Model-Glue:Flex, PureMVC…等,基本上這些框架設計方式都大同小異,每個都有不同的應用場合,可以挑一個喜歡的再自行修改。
-瞭解基本的概念,例如 Value Object, DAO, MVC 等,它們在大部份的程式框架裏都會出現,早點學會日子比較輕鬆。
接著就是開始實際 coding,寫一個中小型規模的app,不論是單純的 CRUD app,或是留言版、電話簿、進銷存管理都可以,籍由多寫來強化編程的概念,然後透過大量的 peer code review 來找出可改進的地方。
*結論
結論還是老話一句:要入門 flex 超級簡單,只要不是白癡應該一小時就行,但要成為可獨當一面的專業開發者,路就很長,如果沒有走對方向很容易就迷失甚至最後放棄。
換句話說,要能成為職場上真正需要的 professional developer,並不如表面上想像的容易(其實我想每種技術領域跟產業都一樣吧),這也是我過去半年來協助很多公司做 recruiting 後的感想。
posted @
2008-07-29 14:46 姜大叔 阅读(1294) |
评论 (0) |
编辑 收藏
摘要: 做Flex做久了做大了,就会觉得之前写的的Flex代码开始有点乱,哪怕你写的规范了,但总觉得结构松散,维护不方便,相信很多人刚开始做Flex的时候,都是想到什么功能,就写什么功能,或者有些好点的,就先画了个大体的流程图之类的,因为现在Flex普及得还不够,很多人做Flex也是试探阶段,不敢用作商业项目或其它大项目,只会用来试水技术层面的...
阅读全文
posted @
2008-07-29 14:44 姜大叔 阅读(4969) |
评论 (32) |
编辑 收藏
在上一篇文章中,为大家介绍了Flex中皮肤的简单且基本也是最常用的用法,就是先在外部程序中做好皮肤样式,之后再在Flex中加载这些皮肤资源,比如是PNG图片,或者是SWF文件等,这些方法都是比较直接且方便简单的。不过灵活性还是不太好,而现在这篇文章中要讲的,就是从程序里AS编写的皮肤,你可以更灵活自由地控制皮肤的样式与表达方式等,而不用依赖于外部的图片或SWF资源,不过这样还是有些缺点的,就是扩展或是改变时就比较不灵活,像图片资源的皮肤的话,可以很轻易的改变不同的图片的路径资源等就可以修改了不同的皮肤模式,而用AS Graphics写的皮肤的话,要修改一下那些代码,当然,你也可以在AS代码里写上动态加载其它图片资源又或者作一些更高级的效果等等。
先在这里解析一下,要以代码方式自定义一个皮肤的话,需要自已编写一个皮肤子类,继承ProgrammaticSkin这个类,这个是所有编写自定义皮肤的基类,该类也派生了另外两个类:RectangularBorder 与 Border 类,都是差不多的,如果你是写Icon之类的小皮肤的话,比如CheckBox或者RadioButton这类皮肤不需要太复杂的画图逻辑,而且大小固定,就像是一个小Icon吧,只是有几个状态而已,这类小皮肤的话,继承ProgrammaticSkin就可以了,而写一些复合的控件,背景大小可以调节之类的皮肤(其实就是大部分皮肤)就用Border或者RectangularBorder类。但都有一个相同点,就是继承了那些类之后,都必需覆盖 updateDisplayList 这个方法,这个方法是由程序自动调用,当需要用到控件时,需要控件的皮肤进行表现时,就会调用那个方法,所以你必需覆盖它,并将你的画图逻辑代码写在那个方法里面。还有要注意的是,这个皮肤类会与你应用这皮肤的控件的Style设置共享,也就是说你可以在编写这个皮肤类代码里面,使用getStyle()等等方法获得设置在目标控件中的风格属性,比如说是<mx:Panel backgroundColor="0xffffff" borderSkin="MySkin"> 那么你可以在MySkin代码里获取这个颜色值来进行画出该颜色的图片或其它操作,而直接将颜色值写死在代码里是不规范的,就如我下面贴出来的的代码,不过出于自已懒,快速代个示例代码,所以犯这个错了。说多了,下面看看代码先。
好了,我们先看看看代码,这份代码是写了一个Panel的皮肤:
1 package com.jiangzone
2 {
3 import mx.skins.Border
4 import mx.core.EdgeMetrics;
5 import mx.core.Container;
6 import mx.graphics.RectangularDropShadow;
7
8 public class MyPanelBorderSkin extends Border {
9
10 public function MyPanelBorderSkin():void {
11 }
12
13 /**
14 * 该方法必需要覆盖,如果你要自定义自已的皮肤的话,
15 * 该方法当在控件更新外观时将会被自动调用
16 * 会传入两个参数数,第一个是Width,第二个是Height,即是该控件的宽与高
17 * */
18 override protected function updateDisplayList(w:Number,h:Number):void {
19 super.updateDisplayList(w,h);
20
21 var ba:uint = 1; //backgroundAlpha 背景透明度
22 var bg:uint = 0xffffff; //backgroundColor 背景颜色
23 graphics.clear(); //graphics这个属性是父类里已经提供了的
24 var p:Container = parent as Container; //获取该皮肤所应用在的父容器,这里为Panel
25
26 //这里需要注意,一定要判断父容器是否已被设置,在文章里作解释
27 if(p){
28 //获取容器定义的区域边界信息对象
29 var vm:EdgeMetrics = p.viewMetrics;
30 //设置四个角的圆度
31 var radiusContent:Object = {tl:vm.top,tr:0,bl:0,br:vm.top};
32 //标题栏圆度
33 var radiusTitle:Object = {tl:vm.top,tr:0,bl:0,br:0};
34 //画一个圆角矩形,整个背景
35 this.drawRoundRect(0,0,w,h,radiusContent,bg,ba);
36 //画一个圆角矩形,标题栏
37 this.drawRoundRect(0,0,w,vm.top,radiusTitle,0xff0000,.7);
38 //画一个圆角矩形,标题栏的那个高光水晶条
39 this.drawRoundRect(0,0,w,vm.top / 2,radiusTitle,0xffffff,.3);
40
41 //下面是画阴影的。
42 var dropShadow:RectangularDropShadow = new RectangularDropShadow();
43 dropShadow.distance = 8;
44 dropShadow.angle = 60;
45 dropShadow.color = 0x000000;
46 dropShadow.alpha = 0.4;
47
48 dropShadow.tlRadius = radiusContent.tl;
49 dropShadow.trRadius = radiusContent.tr;
50 dropShadow.blRadius = radiusContent.bl;
51 dropShadow.brRadius = radiusContent.br;
52
53 dropShadow.drawShadow(graphics, 0, 0, w, h);
54 }
55 }
56 }
57 }
上面的代码就是皮肤的代码,之后你还要做的,就是将该皮肤应用到Panel这个容器里:
1 <mx:Application
2 xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()"
3 layout="absolute">
4
5 <mx:Style>
6 .myPanelSkin {
7 borderSkin: ClassReference( "com.jiangzone.MyPanelBorderSkin" );
8 }
9 </mx:Style>
10
11 <mx:Panel borderSkin="com.jiangzone.MyPanelBorderSkin"
12 width="200" height="150" x="24" y="23"/>
13
14 </mx:Application>
代码很简单,这里要说一下,viewMetrics 这个属性是Container控件所独有的属性,是一个只读属性,编写Container子类的时候都要覆盖它,是用于定义这个容器正文区与边界值的,比如Canvas的四周都是0,所以没有标题栏与边条,而Panel就有四周的边界,而Top边界比较大,用作显示title的,所以如果你要做容器的皮肤的话,注意一下这个值。还有就是,为什么获取了皮肤应用的控件引用(parent)后还要判断它是否为空?因为当程序加载到这个控件时,是先加载那个皮肤的,所以parent的值未被设置,是空的,如果你不作判断的话,将会出现空引用的错误(parent.viewMetrics),当加载完皮肤后,再加载控件并设置控件的属性和设置皮肤,这时将会再次调用updateDisplayList的方法,这时parent才有值,就是那个控件的引用。当改变了style或一些属性后,又会自动触发调用updateDisplayList方法。
我们来看看最终运行效果:
补充一下:
在第一篇文章里,说了将皮肤做在SWF文件里再加载,想一下,可以将该皮肤做成动画MC的,而不单单只是一个画面,可以做成一些动画作为皮肤,之后在Flex引用该SWF的Symbol,这样皮肤就有了动画效果了,不只只是单纯的不动的平面图!
Flex中的皮肤教程就说到这里,皮肤还有很多可探索的,只要大家有求知欲,多点看看英文文档,看看别人的例子程序代码,现在Flex也开源了,也可以多看看Flex的源码,会得到很多知识!
posted @
2008-07-29 14:40 姜大叔 阅读(2423) |
评论 (2) |
编辑 收藏
好久没有写文章了,一直用“忙”来为自已找籍口,其实是懒,不过这个月发生了这么大的事情 5.12 让我们每个人都永记心中,看到中国人的团结,看见解放军们志愿者们这么努力地为灾区奉献,我也不能再为自已找借口了,虽然我远在广东,不能到现场去参与救缓,钱也捐过了,但想想,我还有事情可以做的,就是用我自已的知识,写点技术文章,虽则不能直接的帮助到灾区,但也为国民提高Flex技术知识出分力,做好本职工作。在这里哆嗦点也得说句:为灾区遇难者祈祷,为灾区救缓不懈努力的军民给与崇高的敬意!
.....................................................(三分钟后)
好了,我们转入正题,之前我也写过Flex的动画与变换的文章,不知道大家有没有看过。现在我要写的就是Flex中的皮肤样式方面的,我技术不太好,算是自已学习Flex过程中的理解体会吧。
这里是第一篇,将讲述一下Flex中如何应用UI的皮肤,其实应用UI皮肤不难,你们在使用Flex的过程中是否觉得Flex中自带的皮肤样式不太好看?或者是想自已做个比较有特色的?下面就我们来说说皮肤吧,先来个简单的,你们在做网页时,做导航按钮什么的很多人都是用一个图片来作为一个按钮吧?之后做几个不同的颜色,之后就在CSS或者JS里设置一下当鼠标Over和Down和Out等等动作时,就切换不同颜色的图片,这样实现动态效果。在Flex里也可以如此简单的做皮肤。你可以先画好一个UI的皮肤,之就就将该图片应用到Flex里面。
先来看看效果:
之后我们来看看代码:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()" width="257" height="182" backgroundGradientColors="[#ffffff, #ffffff]">
3
4 <mx:Script>
5 <![CDATA[
6
7 //Embed标签是用于将一些外部资源加入到Flex中,随Flex的编译成SWF文件,
8 //这里是加入一张PNG图片,即是做好的Skin图片
9 [Embed(source="images/buttonskin.png",
10 scaleGridTop="26",
11 scaleGridBottom="64",
12 scaleGridLeft="30",
13 scaleGridRight="106")]
14 //上面的Embed标签下面要紧跟着这个Class,意思就是将上面的资源加入到Flex
15 //后变为这个Class的内容,即调用这个Class时,就是调用那些资源
16 private var MyBtnSkin:Class;
17
18 //在程序创建完成时会调用该函数,在函数里面设置Button的样式(Style)
19 //这里就是设置按钮的up,over,down三个鼠标状态时的皮肤,就是上面加入的图片资源
20 private function init():void{
21 btn.setStyle("upSkin",MyBtnSkin);
22 btn.setStyle("overSkin",MyBtnSkin);
23 btn.setStyle("downSkin",MyBtnSkin);
24 }
25 ]]>
26 </mx:Script>
27 <mx:Button id="btn" label="Hello World" width="190" height="90"/>
28 </mx:Application>
怎么样?很简单吧?大家需要注意一下的是在Embed标签里,我定义了一些scaleGridTop之类的属性,这是跟皮肤的缩放有关的,如果不定义那些属性的话,那么图片是多大的,就按多大来进行缩放,当你的按钮很大时,那些皮肤图片就会被拉大,出现马赛克与变形等,这都是不好看的。加入了9格缩放模式后,当你缩放按钮时,九个格中的四个角的区域不会被缩放,保持原样,中间格会宽高同时缩放,中间上下格会仅是宽度缩放,中间左右格只会高度缩放,这样,那个皮肤的边框无论你如何缩放,都是原来的大小比例,而不会将整个图片一起拉申。
现在的按钮太单调了,只有一个外观,现在大家可以再加多两个不同颜色或者其它图案的图片作为不同状态的皮肤就可以了,比如将overSkin改成红色边框的图片等。
其实大家有没有发现,上面代码的写法感觉比较麻烦的。我们可以用CSS来实现,我们可以直接点,将皮肤直接写在Button上,如下:
1 <mx:Button label="Hello World"
2 upSkin="@Embed('images/buttonskin.png')"
3 overSkin="@Embed('images/buttonskin.png')"
4 downSkin="@Embed('images/buttonskin.png')"
5 />
这样也是同样的效果。省事好多了吧。或者我们用CSS来写:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="257" height="182" backgroundGradientColors="[#ffffff, #ffffff]">
3 <mx:Style>
4 Button{
5 up-skin:Embed(source="images/buttonskin.png",
6 scaleGridTop="26",
7 scaleGridBottom="64",
8 scaleGridLeft="30",
9 scaleGridRight="106");
10 over-skin:Embed(source="images/buttonskin.png",
11 scaleGridTop="26",
12 scaleGridBottom="64",
13 scaleGridLeft="30",
14 scaleGridRight="106");
15 }
16 </mx:Style>
17 <mx:Button id="btn" label="Hello World" width="190" height="90"/>
18 </mx:Application>
如果觉得加上CSS代码会令程序代码混乱的话,就将CSS代码写在CSS文件里去,在程序里导入CSS文件就可以了。(至于CSS的用法,我就不说了,反正Flex里的CSS方式与Html里的用法用样。只是要注意一下CSS里面设置的属性的名字就可以了)
<mx:Style source="styles/styles.css" />
但有人可能会问,这样做的话,如果一个程序有很多不同的UI,并有不同的皮肤,那不就是要生成很多的图片?这个问得好,确实,如果以这种方式的话,就像一个网站里的images文件夹一样,有很多的小图片,这样太麻烦了,而且也不好维护。既然有这样的问题,我们就将皮肤干脆做成在一个文件里面算了,方便快捷,维护又方便,而这个文件,就是SWF文件。我们如果有Flash基础的话,基本对MC都不会陌生,对,这次我们的主角就是SWF里面的MC,我们可以将一个皮肤做成一个MC,在Flash里将所有用到的皮肤都做在一个SWF里,一个图片就像是一个MC,之后发布该SWF文件,在Flex里加载这个SWF文件,再在需要的皮肤里调用SWF里面相应皮肤的MC的名字就可以了。如下:
这个是在Flash里做好的皮肤SWF文件,里面有三个不同颜色的皮肤模式
[swf]attachments/month_0805/p2008518233635.swf[/swf]
这里要注意一下,在Flash里做这些皮肤时,要将MC加上链接,链接的名称,就是你在Flex里调用该皮肤的名称,图片如下:
在做好皮肤的SWF后,让我们回到Flex 里面,在Flex里写如下代码:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="257" height="182" backgroundGradientColors="[#ffffff, #ffffff]">
3 <mx:Style>
4 Button{
5 up-skin:Embed(source="images/btnSkin.swf",symbol="btnUP");
6 over-skin:Embed(source="images/btnSkin.swf",symbol="btnOVER");
7 down-skin:Embed(source="images/btnSkin.swf",symbol="btnDOWN");
8 }
9 </mx:Style>
10 <mx:Button id="btn" label="Hello World" width="100" height="60"/>
11 </mx:Application>
注意一下的就是,在Embed标签里,要导入的资源文件不是PNG了,而是一个SWF,就是我们刚才在Flash里做好的皮肤文件,注意看,后面还跟着一个symbol属性,该属性就是指明你要调用哪一个MC,就是SWF里面的MC,记得,都要为每个MC做链接,并链接名字要与symbol里的名字致。至此,我们的皮肤就完成了。一个SWF文件就搞掂。这里是最终效果:
好了,这篇教程就到些结束,上面介绍的都是Skin的比较简单快捷的用法,不过灵活性就不是很大,但也是皮肤技术的最基础的,大家也可以再扩展一下其它用法等等的,在下篇文章,也就是Flex皮肤系列文章的(二)中,我会介绍一下用程序代码来编写皮肤,这就是不依赖于外部的资源文件,直接用AS3代码用Graphics来自已画皮肤。下篇将会用到AS3的Draw API方面的知识,请大家做好准备。
在此再次向我们的灾区战士们给与崇高的敬意!
posted @
2008-07-29 14:37 姜大叔 阅读(7262) |
评论 (5) |
编辑 收藏
摘要: 好久没有发表文章了,这天写了一个Flash的一个动画菜单的效果,在之前看到一个站的一个菜单效果挺不错,就试着自已也用纯AS3写一个出来试试。看了一下研究了一个算法,自已也写了个出来,写得比较粗糙,所以运行时可能还会有点小问题,有空要再优化一下算法才行。
有点像 Apple 的菜单效果。原本是计划用图片的,不过后...
阅读全文
posted @
2008-07-29 14:33 姜大叔 阅读(1375) |
评论 (0) |
编辑 收藏
用PV3D做的一个Flex效果,有时间会整理一下代码与文档贴出来!
以下是效果:
posted @
2008-07-29 14:31 姜大叔 阅读(12036) |
评论 (133) |
编辑 收藏
这篇文章是Flex动画效果变换的最后一编了,这篇将会讲述Flex中的“变面”(我自已的理解)技术,即是Transitions!
如果看过Flex SDK里面的自带的例子程序,有一个叫“Flex Store”的应用,在里面的手机列表中看某一个手机的详细时,就是这种效果,不多说,这篇会比较简单,先看看效果:
看到了效果了吧,这种的变换不难实现,再来看看代码再解析:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="695" height="555">
3 <mx:states>
4 <mx:State name="A">
5 <mx:SetProperty target="{windowA}" name="width" value="500"/>
6 <mx:SetProperty target="{windowA}" name="height" value="300"/>
7 <mx:SetProperty target="{windowC}" name="width" value="150"/>
8 <mx:SetProperty target="{windowC}" name="height" value="150"/>
9 <mx:SetProperty target="{windowC}" name="y" value="333"/>
10 <mx:SetProperty target="{windowD}" name="x" value="373"/>
11 <mx:SetProperty target="{windowD}" name="width" value="150"/>
12 <mx:SetProperty target="{windowD}" name="height" value="150"/>
13 <mx:SetProperty target="{windowD}" name="y" value="333"/>
14 <mx:SetProperty target="{windowB}" name="x" value="23"/>
15 <mx:SetProperty target="{windowB}" name="y" value="333"/>
16 <mx:SetProperty target="{windowB}" name="width" value="150"/>
17 <mx:SetProperty target="{windowB}" name="height" value="150"/>
18 <mx:SetProperty target="{windowC}" name="x" value="200"/>
19 </mx:State>
20 <mx:State name="B">
21 <mx:SetProperty target="{windowD}" name="width" value="150"/>
22 <mx:SetProperty target="{windowD}" name="height" value="150"/>
23 <mx:SetProperty target="{windowC}" name="width" value="150"/>
24 <mx:SetProperty target="{windowC}" name="height" value="150"/>
25 <mx:SetProperty target="{windowA}" name="width" value="150"/>
26 <mx:SetProperty target="{windowA}" name="height" value="150"/>
27 <mx:SetProperty target="{windowB}" name="width" value="500"/>
28 <mx:SetProperty target="{windowB}" name="height" value="300"/>
29 <mx:SetProperty target="{windowA}" name="y" value="333"/>
30 <mx:SetProperty target="{windowC}" name="x" value="200"/>
31 <mx:SetProperty target="{windowC}" name="y" value="333"/>
32 <mx:SetProperty target="{windowB}" name="x" value="23"/>
33 <mx:SetProperty target="{windowD}" name="x" value="373"/>
34 <mx:SetProperty target="{windowD}" name="y" value="333"/>
35 </mx:State>
36 <mx:State name="C">
37 <mx:SetProperty target="{windowD}" name="width" value="150"/>
38 <mx:SetProperty target="{windowD}" name="height" value="150"/>
39 <mx:SetProperty target="{windowB}" name="width" value="150"/>
40 <mx:SetProperty target="{windowB}" name="height" value="150"/>
41 <mx:SetProperty target="{windowA}" name="width" value="150"/>
42 <mx:SetProperty target="{windowA}" name="height" value="150"/>
43 <mx:SetProperty target="{windowC}" name="width" value="500"/>
44 <mx:SetProperty target="{windowC}" name="height" value="300"/>
45 <mx:SetProperty target="{windowA}" name="y" value="333"/>
46 <mx:SetProperty target="{windowB}" name="x" value="200"/>
47 <mx:SetProperty target="{windowB}" name="y" value="333"/>
48 <mx:SetProperty target="{windowC}" name="x" value="23"/>
49 <mx:SetProperty target="{windowC}" name="y" value="19"/>
50 <mx:SetProperty target="{windowD}" name="x" value="373"/>
51 <mx:SetProperty target="{windowD}" name="y" value="333"/>
52 </mx:State>
53 <mx:State name="D">
54 <mx:SetProperty target="{windowC}" name="width" value="150"/>
55 <mx:SetProperty target="{windowC}" name="height" value="150"/>
56 <mx:SetProperty target="{windowB}" name="width" value="150"/>
57 <mx:SetProperty target="{windowB}" name="height" value="150"/>
58 <mx:SetProperty target="{windowA}" name="width" value="150"/>
59 <mx:SetProperty target="{windowA}" name="height" value="150"/>
60 <mx:SetProperty target="{windowD}" name="width" value="500"/>
61 <mx:SetProperty target="{windowD}" name="height" value="300"/>
62 <mx:SetProperty target="{windowA}" name="y" value="333"/>
63 <mx:SetProperty target="{windowB}" name="x" value="200"/>
64 <mx:SetProperty target="{windowB}" name="y" value="333"/>
65 <mx:SetProperty target="{windowD}" name="x" value="23"/>
66 <mx:SetProperty target="{windowD}" name="y" value="19"/>
67 <mx:SetProperty target="{windowC}" name="x" value="373"/>
68 <mx:SetProperty target="{windowC}" name="y" value="333"/>
69 </mx:State>
70 </mx:states>
71 <mx:transitions>
72 <mx:Transition fromState="*" toState="*">
73 <mx:Parallel targets="{[windowA, windowB, windowC, windowD]}">
74 <mx:Move />
75 <mx:Resize />
76 </mx:Parallel>
77 </mx:Transition>
78 </mx:transitions>
79 <mx:TitleWindow x="23" y="19" width="250" height="200" layout="absolute" title="A" id="windowA" click="currentState='A'" />
80 <mx:TitleWindow x="309" y="19" width="250" height="200" layout="absolute" title="B" id="windowB" click="currentState='B'" />
81 <mx:TitleWindow x="23" y="260" width="250" height="200" layout="absolute" title="C" id="windowC" click="currentState='C'" />
82 <mx:TitleWindow x="309" y="260" width="250" height="200" layout="absolute" title="D" id="windowD" click="currentState='D'" />
83 </mx:Application>
代码会比较多,我们先看看<mx:states>标签,它是一个集合,就是你的程序有多少个状态,什么是状态呢?我自已理解就即是有多少个“面谱”,即是现在程序里面有四个显示界面状态,所以里面有四个<mx:State>标签,每个<mx:State>状态都需要有一个名字name属性,以区分是哪个界面状态,在每个状态里面都有很多<mx:SetProperty>的标签,看英文都知道了,该标签用于设置这个状态下的所有界面元素的属性(组件的属性),因为每个界面状态中各个组件的大小布局都不同,所以在状态标签里就要预先设置好该状态下的组件的位置与大小,那个target="{windowC}"属性就是设置你要设置的哪个组件的名字拉,name="height"就是你要设置的属性value="333"就是你要设置该属性的值,我们细心看看的话,可能会发现,每个状态里面设置的属性,都是width,height,x,y这四个属性,我们看看上面的最终效果就知道无论切换哪个状态,组件间的变换来来去去都是移动位置与大小改变,就是说当你变换状态时,需要改动哪些属性的,就将它的目标值设置在<mx:SetProperty>标签里。再看看下面的<mx:transitions>标签,这个也是个集合,里面放着不同的变换方法<mx:Transition>,我们来看看变换标签的代码:
1 <mx:Transition fromState="*" toState="*">
2 <mx:Parallel targets="{[windowA, windowB, windowC, windowD]}">
3 <mx:Move />
4 <mx:Resize />
5 </mx:Parallel>
6 </mx:Transition>
formState与toState属性是要设置该状态变换是怎样触发的,里面要填上状态的名字,<mx:State name="C"> C就是状态的名字,即是如果你formState="A",toState="C"的话,只有从A状态切换到C状态时,才会产生以上的变换动画效果,如果不附合该规则如A切换到B状态的话,则只会按状态的属性设置值来直接生成视图,而没有动画渐变效果了。如果填上“*”的话,就是无论是哪个状态切换到哪个,都会运行动画效果,至于变换期间用到哪种动画效果来进行渐变,就在它的下级标签里定义了,这里它用到了<mx:Parallel>并列播放移动与重整大小的动画效果,之前文章讲过,这里不多说了。基本上,一个变换就做好了,但我们还需要触发它,也就是去改变程序当前的显示状态:click="currentState='A'" 在每个组件的click事件里,改变程序的currentState值,就是改变程序的当前显示状态!之后动画效果就会触发了!
迟点有时间,再做一个3D的动画效果,将会用到PV3D的框架,再整理一个代码与教程也贴出来跟大家分享吧,不过不知道大家对PV3D这个东东熟悉不,不熟悉的话,可能看得痛苦点,至于PV3D方面的教程,我也看看抽点时间写写吧!先谢过大家的支持!
posted @
2008-07-29 14:28 姜大叔 阅读(6888) |
评论 (6) |
编辑 收藏
在上篇文章《Flex的动画效果与变换(一)》中讲到了使用Flex系统里面自带的一些动来效果的使用,但很多开发者都并不满足Flex里提供的简单的渐变大小,透明,移动,遮罩等的效果,如果是Flash的开发者的话,更不用说了,在Flash,多数人都是随意的制作一些动画效果等,而且形态多变。但是不是Flex里就不能实现呢?肯定不是,在Flex里也可以自定义动画效果,只不过就是没有Flash里面那么简单随意了。不过熟悉了之后,也会觉得在Flex里制作动画也不是什么难事,不多说了,转入正题!
在这里我先介绍一下Flex里面的动画效果机制,在Flex里面要使用动画效果的话,先要创建一个效果标签,之后在组件里(如TextInput)写上效果触发器,但可能会有人问,如果程序里我就只定义一个移动效果
<mx:Move>,之后我程序里面有5个组件,每个组件的动画效果都指向这个Move效果,那么它是不是组件一运行了效果后,组件二再触发效果,是不是组件一的效果会消失才会到组件二里播放?其它不是,虽然我们只定义了一个Move,但我们定义的只是Move效果的工厂,这里就用到了设计模式中的“工厂方法”模式,其实5个组件都可以同时运行效果,而5个效果都是不同的一个实例,彼此独立。所谓工厂方法模式,就好比是一家衣服制造工厂,A走进这家工厂说要一件衣服,工厂就制作一件合适A的Size的衣服,B进去,就会生产合适B的衣服,但A与B的衣服都是一样的。就好等于面向对象中的类与对象的关系一样。(我可能说多了-_-)
效果运行的时候,其实运行的不是Move这个对象,而是MoveInstance这个对象,Move只是工厂,既然一个动画效果就主要分这两大部份,我们就先建造一个工厂吧!
在Flex里面所有的效果的工厂都是继承自 mx.effects.Effect 这个类,我们也不能搞特殊,我们自定义的效果也要继承那个类,先看以下整个工厂类的代码:
1 package com.jiangzone.flex.effects {
2 import mx.effects.Effect;
3 import mx.effects.EffectInstance;
4
5 public class MyEffect extends Effect {
6 private var _color:Number = 0xFF0000;
7
8 public function set color(value:Number):void {
9 _color = value;
10 }
11
12 public function MyEffect(newTarget:Object = null) {
13 super(newTarget);
14 instanceClass = MyEffectInstance;
15 }
16
17 override public function getAffectedProperties( ):Array {
18 return [];
19 }
20
21 override protected function initInstance(instance:EffectInstance):void {
22 super.initInstance(instance);
23 MyEffectInstance(instance).color = _color;
24 }
25 }
26 }
大家看看上面的代码,其中先看构造函数,构造函数要接收一个默认为空的Object对象
public function MyEffect(newTarget:Object = null)
之后在该构造函数里面调用父类的构造函数,并且将instanceClass这个属性设置为你的该效果的实例类,因为这个类是工厂类,所以要知道你这个工厂生产什么产品,即上面说的“衣服”,所以这里我们将其命名为MyEffectInstance,注意:在Flex中的所有效果实例类都是在工厂类后面加Instance,也不是一定,只是规范而已。还有注意,下面一会定义的实例类的类名一定要跟这里的一致。
大家还会看到,上面的代码中,复写(override)了二个方法:getAffectedProperties( )与initInstance(instance:EffectInstance)
这两个方法都是要复写的,先说说getAffectedProperties( )这个方法,这个方法是获取被改变的属性值,怎么说呢,比如说,你做的动画效果如果要用到组件对象的一些属性的话,就要返回这些属性的名字,如:你的效果是对组件做旋转的话,则:
1 override public function getAffectedProperties( ):Array {
2 return ["rotation"];
3 }
反正你做的效果需要对组件修改什么属性的话,都在这个方法里返回名字,修改多个属性的话就往数组里加就是了。
后面就是这个方法了initInstance,该方法接收一个instance:EffectInstance参数,也就是效果实例类啦,因为每个效果实例类都要继承EffectInstance类,所以这个方法里的参数写的是父类,在里面要做其它的话,需要将 instance 转换为你相应的效果类。在这个方法里面,也是要调用父类的同名方法:super.initInstance(instance);
基本上,一个工厂类就写好了,但这样只是最简单的写法,试想想,每个人穿衣服的Size不同,喜欢的颜色也不同,所以,是不是可以由用户来定义他们想要的效果的颜色等属性呢?当然,你对衣服有什么要求,都是向工厂提出的,没有人会对衣服说吧?所以,这些可设置的属性也是定义在工厂类里面,所以下面,我们为该衣服可定制颜色为例,在工厂类里面加入如下代码:
1 private var _color:Number = 0xFF0000;
2 public function set color(value:Number):void {
3 _color = value;
4 }
你想运行时的效果可以设置不同的颜色的话,就可以直接设置MyEffect的color属性,之后将这个属性传给效果实例类:
1 override protected function initInstance(instance:EffectInstance):void {
2 super.initInstance(instance);
3 MyEffectInstance(instance).color = _color;
4 }
这些对效果实例类的设置,都是要定在initInstance方法里了,你想对运行时的效果设置什么属性的话,都要先告诉工厂类,之后工厂类在这个方法里面转嫁给实例类,这样,同一个效果,可以运行不同的颜色。但前提是你后面要写的实例类要有color这个属性。
现在已做好了工厂类了,下面要做效果实例类了,先贴出完整代码:
1 package com.jiangzone.flex.effects {
2 import mx.effects.EffectInstance;
3 import flash.display.Shape;
4 import flash.events.Event;
5
6 public class MyEffectInstance extends EffectInstance {
7
8 private var _color:Number;
9 private var shape:Shape;
10
11 public function set color(value:Number):void {
12 _color = value;
13 }
14
15 public function MyEffectInstance(newTarget:Object) {
16 super(newTarget);
17 }
18
19 override public function play( ):void {
20 super.play( );
21 drawShape();
22 }
23
24 private function drawShape():void{
25 shape = new Shape();
26 shape.graphics.beginFill(_color);
27 shape.graphics.drawRect(target.width * -0.5,target.height * -0.5,target.width,target.height);
28 shape.graphics.endFill();
29 shape.x = target.x + target.width * 0.5;
30 shape.y = target.y + target.height * 0.5;
31 target.parent.rawChildren.addChild(shape);
32 target.addEventListener(Event.ENTER_FRAME,onEnterFrame);
33 }
34
35 private function onEnterFrame(e:Event):void{
36 shape.scaleX += 0.1;
37 shape.scaleY += 0.1;
38 shape.alpha -= 0.05;
39 if(shape.alpha <= 0){
40 target.parent.rawChildren.removeChild(shape);
41 target.removeEventListener(Event.ENTER_FRAME,onEnterFrame);
42 }
43 }
44 }
45 }
我们看到,每一个动画效果实例类,都要继承自EffectInstance这个类,构造函数也是需要接收一个Object,这个Object其实就是你要应用到的组件对象,这个会是系统自动传递的,接收了Object后还要用该Object 调用父类的构造函数:super(newTarget);
之后还有一件必做的事,就是重写play()这个方法:override public function play( ):void
是不是对play()很熟悉?因为第一篇文章中,就用到这个方法来发动效果的播放的,所以,你需要做的动画编程都是在这个方法里。但还是要先调用父类的同名方法,super.play();之后的,就是你想怎么画就怎么画啦。我将画一个与要应用效果的组件一样大小的矩型,之后该矩形会放大并透明,效果都写在drawShape()方法里了。看到这个方法里面的代码,是不是跟Flash里的一样了?
这里再贴上MXML代码:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application layout="absolute" xmlns:mx="http://www.adobe.com/2006/mxml"
3 xmlns:pf="com.jiangzone.flex.effects.*">
4 <pf:MyEffect id="myEffect" color="0xFFFFFF" />
5 <mx:VBox x="100" y="43">
6 <mx:TextInput focusInEffect="{myEffect}" />
7 <mx:TextInput focusInEffect="{myEffect}" />
8 <mx:TextInput focusInEffect="{myEffect}" />
9 <mx:TextInput focusInEffect="{myEffect}" />
10 </mx:VBox>
11 </mx:Application>
这里先看看最终效果:
在这里,我用了ENTER_FRAME的写法,但是如果不用ENTER_FRAME方式制作动画的话,还有另外一种方法的,那就是Tween了,Tween是以“时间”为准,而ENTER_FRAME是以“帧”为准,其实到这里,一个基本的Flex自定义动画效果就完成了,但扩展一下的,还可以用Tween来实现,而且建议用Tween来写动画效果,易控制,清淅一点。用Tween实现的话,效果与写法都是差不多的,要用Tween就要将效果实例类继承自TweenEffectInstance这个类,并重写它的onTweenUpdate( )方法与onTweenEnd( )方法,这种Tween效果的写法,将会比ENTER_FRAME的写法方便,因为它根据的是时间,所以,你可以指定效果播放的时间,并且当播放完毕会自动调用onTweenEnd()方法,你可以在该方法里写一些处理操作,如释放资源等等
由于编幅关系,就不在这里详细介绍TweenEffectInstence了,就简单贴出该类的写法与注释吧:
1 package com.jiangzone.flex.effects {
2 import mx.effects.effectClasses.TweenEffectInstance;
3 import flash.display.Shape;
4 import flash.events.Event;
5 import mx.effects.Tween;
6
7 public class MyEffectInstance extends TweenEffectInstance {
8
9 private var _color:Number;
10 private var shape:Shape;
11
12 public function set color(value:Number):void {
13 _color = value;
14 }
15
16 //构造函数
17 public function MyEffectInstance(newTarget:Object) {
18 super(newTarget);
19 }
20
21 //同样的要重写play()方法与调用父类同名方法
22 override public function play( ):void {
23 super.play();
24 drawShape(); //先创建一个矩形
25 /*注意:用Tween效果写法的话,就一定要创建一个Tween对象
26 第一个参数是侦听器,即侦听Update与End的,这两个方法都在这个类里,
27 所以这里就写this,第二和第三个参数都是一个数组
28 第二个参数是初始值数组,第三个是结果值数组,都要一一对应,第四个是变化时间
29 这里的是[1,1]分别是初始时的scale比例与alpha,[3,0]就是最终结果数值
30 系统会自动在1000毫秒里平分这些值来得到渐变效果
31 并将每一次数值的改变时调用Update方法,结束后调用End方法
32 你也可以将时间的参数发布到工厂类属性里,可以方便设置播放时间,像Flex自带效果一样
33 */
34 new Tween(this,[1,1],[3,0],1000);
35 }
36
37 override public function onTweenUpdate(value:Object):void{
38 //这里将改变的数值应用到组件对象中。注意:也要与上面的数值数组相对应。
39 shape.scaleX = Number(value[0]);
40 shape.scaleY = Number(value[0]);
41 shape.alpha = Number(value[1]);
42 }
43
44 override public function onTweenEnd(value:Object):void {
45 //当播放完时会自动调用该方法,这里就做删除该矩形的操作吧
46 target.parent.rawChildren.removeChild(shape);
47 }
48
49 private function drawShape():void{
50 shape = new Shape();
51 shape.graphics.beginFill(_color);
52 shape.graphics.drawRect(target.width * -0.5,target.height * -0.5,target.width,target.height);
53 shape.graphics.endFill();
54 shape.x = target.x + target.width * 0.5;
55 shape.y = target.y + target.height * 0.5;
56 target.parent.rawChildren.addChild(shape);
57 }
58 }
59 }
就写到这里吧,关于Tween其它的,就留作为作业,让大家思考与探索吧!之后如果有时间的话,将会写完下篇文章介绍Flex的“变面”动画,即状态变换!这里先谢谢大家支持!
posted @
2008-07-29 14:24 姜大叔 阅读(5660) |
评论 (7) |
编辑 收藏
在Flex里面不像在Flash里面随意制作动画了,Flex更趋向于应用程序,而不是动画制作了,所以没有了时间轴的概念。在Flex中使用动画效果,可以用Flex自带的Effect,或者自已定制Effect,因为很多人都想借Flash里面的一样操作Flex,比如在Flash里面做一个动态按钮很容易,当鼠标移动到上面时,会有很多发光的点跑出来(荧火虫效果),这种效果在Flash十分容易实现,但在Flex里面要实现这种效果就不是那么简单的了,下面说说在Flex里的的动务效果的使用与自定义制作。
首先介绍一下Flex里面的自带的效果有以下几种:
Blur 模糊效果
Move 移动效果
Fade 淡入淡出效果
Glow 发光效果
Resize 调整大小效果
Rotate 旋转效果
Zoom 缩放效果
WipeLeft 用遮罩实现画面收放效果,下同,分别为不同方向
WipeRight
WipeUp
WipeDown
不同的效果所需要设置的属性也不一样,比如Blur效果需要设置它的X与Y轴的模糊像素
<mx:Blur id="blur" blurXFrom="0" blurXTo="10" />
而Move效果需要设置移动的位置信息
<mx:Move id="moveEffect" xFrom="-100" />
其它设置可以参考Flex语言参考
下面说说如何使用这些效果。要运行这些效果有两种方法:一种是调用该效果的play()方法,另外一种是使用触发器来触发效果。
(1)使用play()方法:
先看效果:
以下代码:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
3 <mx:Script>
4 <![CDATA[
5 private function onClick(event:Event):void {
6 be.target = event.currentTarget;
7 be.play( );
8 }
9 ]]>
10 </mx:Script>
11
12 <mx:Blur id="be" blurXTo="50" blurYTo="50" duration="1000" />
13
14 <mx:Panel id="p" width="200" height="180" click="onClick(event)" />
15 </mx:Application>
在代码中看到,要使用效果,先定好一个效果,如上面的<mx:Blur ...>该标签就是模糊效果的MXML标签,定好效果后在Panel的点击事件里再对该效果进行一些设置,如be.target = event.currentTarget 设置该效果将要应用到的目标组件(Component),之后再调用play()方法后,该效果就会应用在Panel上面播放!
(2)使用触发器播放效果:
使用触发器播放效果的话,可以不用写ActionScript代码,直接在组件的效果触发器上指明使用哪个效果就可以了,比较简单明了,但就不能进行更多的属性定制,而用AS控制播放的话,可以对效果进行很多的设置再相应根据情况播放,先看看触发器播放的代码:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
3
4 <mx:Blur id="be" blurXTo="50" blurYTo="50" duration="2000" />
5
6 <mx:Panel id="p" width="200" height="180" creationCompleteEffect="{be}" />
7 </mx:Application>
看以上代码,先写好了Blur的效果并设置好属性后,duration="2000"这个是播放的时间以毫秒为单位。
在Panel标签里有这样一句:creationCompleteEffect="{be}" 这个就是触发器,是该Panel组件的效果触发器,当Panel组件加载完成时,会由系统自动调用该效果触发器,触发器里面指了触发 be 这个Blur效果
在Flex里面还有很多触发器如:
addedEffect 被添加进容器时触发效果
removedEffect 被从容器中移除时触发效果
creationCompleteEffect 被创建成功时触发效果
focusInEffect 获得焦点时触发
focusOutEffect 失去焦点时触发
hideEffect 被隐藏时(visible=false)触发
showEffect 被显示时(visible=true)触发
rollOverEffect 鼠标经过时触发
rollOutEffect 鼠标离开时触发
mouseDownEffect 鼠标按下时触发
mouseUpEffect 鼠标松开时触发
moveEffect 被移动时触发
resizeEffect 被重整大小时触发
注意:这些都是效果触发器,不要与事件触发器混乱了。事件触发器是rollOver,事件触发器与效果触发器差不多,事件触发器是当用户执行相就操作时触发事件,将会调用自定的事件触发处理函数,而效果触发器是执行相应操作时被触发并由系统自动调用所定的效果的play()方法。
现在说说效果的一些其它属性:
每个效果都有reverse( );方法,该方法是反向播放,原本由小到大的变化,而调用reverse( );后再运行play()的话,效果将会从大到小进行播放。
但要注意的一点是,reverse( );不会自动播放,即是单单调用reverse( );的话,效果并不会播放,他只会记录该效果为倒转,而要再调用play()后倒转效果才会开始播放。而调用pause( )与resume( )就是暂停与继续播放效果
startDelay这个属性是设置效果的播放延时,以毫秒为单位,即要等待多少毫秒后效果才开始播放,如:
<mx:Blur id="be" blurXTo="50" startDelay="3000" />
该模糊效果将会在调用play()之后3秒才开始播放
repeatCount这个属性是设置效果的重复次数,默认为1,设置为0的话就是不停循环播放
<mx:Blur id="be" blurXTo="50" startDelay="3000" repeatCount="5" />
每个效果都有两个事件:effectStart 与 effectEnd
你可以在该效果事件的处理函数里面对效果作相应的操作,如:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
3 <mx:Script>
4 <![CDATA[
5 import mx.events.EffectEvent;
6 public function onEffEnd(e:EffectEvent):void{
7 e.effectInstance.reverse();
8 e.effectInstance.play();
9 }
10 ]]>
11 </mx:Script>
12 <mx:Blur id="be" blurXTo="50" blurYTo="50" duration="2000" />
13
14 <mx:Panel id="p" width="200" height="180" creationCompleteEffect="{be}" effectEnd="onEffEnd(event)" />
15 </mx:Application>
当效果播放完时,系统将会自动触发effectEnd事件,在处理函数里面,将该效果Instance即现时播放的效果实例进行倒转并播放,当播放完,又会触发effectEnd事伯,这样一直循环!
现在再来说说效果的组合:
通常如果你觉得只应用一个效果很单调的话,可以进行效果组合应用,即多个效果同时播放或者顺序播放,
如,当加载页面时,你想Panel先模糊到一定程度,再将Panel移动到某个位置,再把Panel还原成原来的清淅度(即消退模糊)。这样分析一下,一共用了三个效果,一,先应用Blur(由清至模)效果,当Blur完成时,再应用Move效果,当Move完成时,再应用另外一个Blur(由模至清)效果。这样三个效果组合就是按顺序组合,先后运行。先来看看效果:
再来看看代码:
1 <?xml version="1.0" encoding="utf-8"?>
2 <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
3
4 <mx:Sequence id="sequenceEffect">
5 <mx:Blur id="beOut" blurXTo="50" blurYTo="50" duration="500" />
6 <mx:Move id="mv" xTo="200" yTo="150" duration="500" />
7 <mx:Blur id="beIn" blurXFrom="50" blurYFrom="50" blurXTo="0" blurYTo="0" duration="500" />
8 </mx:Sequence>
9
10 <mx:Panel id="p" width="200" height="180" mouseDownEffect="sequenceEffect" />
11 </mx:Application>
看以上代码,<mx:Sequence id="sequenceEffect">标签就是顺序组合效果标签,当应用sequenceEffect效果的时候,它会按顺序播放该标签里面的三个子效果。
另外的就是同时播放了,
1 <mx:Parallel id="parallelEffect">
2 <mx:Blur id="beOut" blurXTo="50" blurYTo="50" duration="500" />
3 <mx:Move id="mv" xTo="200" yTo="150" duration="500" />
4 </mx:Parallel>
这个标签就是同时播放的效果标签,放在它里面的子效果都会同时播放,即一边模糊一边移动。这个都可以自由组合,<mx:Parallel>与<mx:Sequence>标签都可以自由组合,例如先按顺序先播放模糊,再同时播放移动与隐藏等。这里就不多说了。
使用Flex自带的效果基本用法就这些,下篇文章将讨论自定义效果的制作,下篇将会详细说说这篇文章上面说到的 effectInstance 即实例与工厂的概念。
posted @
2008-07-29 14:20 姜大叔 阅读(20775) |
评论 (6) |
编辑 收藏
近来在学习Flash的3D技术,现在我看过的就PV3D 与Away3D这两个开源框架,感觉还不错,好像Away3D的效果好点,但效率不如PV3D,PV3D算是亻为(不会用五笔打这个字-_-,我机子里又没拼音输入,将就下吧!)3D,不管这么多了,做个东西练练手,先看效果:
[url=http://www.jiangzone.com.cn/jiang/3dmusic/soundtest.swf]这里看看效果![/url]
这个程序很简单到没无简单了,就只是用了PV3D框架中的Cube的3D对象,一口气生成64个放在数组里待用,之后用Flash里的声音类加载并播放声音,之后在ENTER_FRAME事件里检测当前声音的频谱,之后根据频谱的高低来调整Cube格子的高低,在Flash里获取的声音频谱有512个Float数据,代表512级频谱,而前256级为LeftChangle,后256级为RightChangle,由于如果生成256个3D对象的话,对CPU来说是个考验,所以就折中间隔抽取64个频段来显示,由于懒于写代码了,就只写了LeftChangle的频谱。
以下是代码:
1 //************导入需要用到的类
2 import flash.display.Sprite;
3 import org.papervision3d.scenes.Scene3D;
4 import org.papervision3d.cameras.FreeCamera3D;
5 import org.papervision3d.objects.Cube;
6 import org.papervision3d.materials.MaterialsList;
7 import org.papervision3d.materials.WireframeMaterial;
8 import org.papervision3d.materials.ColorMaterial;
9 import org.papervision3d.cameras.Camera3D;
10 import org.papervision3d.objects.Plane;
11
12 //************定义需要用到的对象
13 var cont:Sprite;
14 var scene:Scene3D;
15 var camera:FreeCamera3D;
16 var ml:MaterialsList;
17 var material:ColorMaterial;
18 var cubeArr:Array;
19
20 //***********设置放置3D东东的容器
21 cont = new Sprite();
22 cont.x = 150;
23 cont.y = 120;
24 this.addChild(cont);
25
26 //******设置一个3D场景,并设置好Camera的位置与角度
27 scene = new Scene3D(cont);
28 camera = new FreeCamera3D();
29 camera.z = 1200;
30 camera.x = 100;
31 camera.y = 700;
32 camera.rotationX = -30;
33 camera.rotationY = 180;
34 camera.focus = 100;
35 camera.zoom = 10;
36
37 //***********定义一个存放所有格子的数组
38 cubeArr = new Array();
39
40 //定义一个颜色材质与材质列表,用于应用于格子的6个面的贴图
41 material = new ColorMaterial(0x096288);
42 ml = new MaterialsList();
43 ml.addMaterial(material,"front");
44 ml.addMaterial(material,"back");
45 ml.addMaterial(material,"top");
46 ml.addMaterial(material,"bottom");
47 ml.addMaterial(material,"left");
48 ml.addMaterial(material,"right");
49
50 var i:int = 0;
51 var j:int = 0;
52
53 //生成64个小格子并排列好位置
54 for(;i<64;i++){
55 ml.addMaterial(new ColorMaterial(0xB9DFCF),"top");
56 var c:Cube = new Cube(ml,20,20,20);
57 c.x = (i % 8) * (20 + 5);
58 c.z = (j % 8) * (20 + 5);
59 (i % 8 == 0) ? j++:j=j;
60 cubeArr[i] = c;
61 scene.addChild(c);
62 }
63
64 //创建一个面板,设置好位置放在小格子队列的下面(地板)
65 var plane:Plane = new Plane(new WireframeMaterial(0xaaaaaa),300,300,7,7);
66 plane.rotationX = -90;
67 plane.y = -50;
68 plane.x = 70;
69 plane.z = 60;
70 scene.addChild(plane);
71
72 //以上是3D部分,以下是声音部分
73 //建立一个字节数组用于存放获取到的频谱信息
74 var bArr:ByteArray = new ByteArray();
75 //读取声音文件并播放
76 var req:URLRequest = new URLRequest("a.mp3");
77 var sound:Sound = new Sound(req);
78 sound.play();
79
80 //添加一个事件侦听器
81 this.addEventListener(Event.ENTER_FRAME,onEnter);
82
83 function onEnter(e:Event):void{
84 //获取当前声音频谱,将数据存放在bArr里面。
85 //true为频谱模式,0为采样率代号
86 SoundMixer.computeSpectrum(bArr,true,0);
87 i=0;
88 //读取前256个数据,即Left声道的频谱
89 for(;i<256;i++){
90 var temp:Number = bArr.readFloat();
91 //间隔4个数据中获取一个显示
92 if(i % 4 == 0){
93 //从数组中获取相应的格子
94 var c:Cube = cubeArr[i/4] as Cube;
95 //设置格子在三维坐标中的高。
96 //由于频谱数据是0-1,所以将其放大100倍
97 c.y = temp * 100;
98 }
99 }
100 //镜头旋转移动
101 camera.moveLeft(12);
102 camera.rotationY += 0.6;
103 scene.renderCamera(camera);
104 }
posted @
2008-07-29 14:16 姜大叔 阅读(1441) |
评论 (1) |
编辑 收藏
发现一篇好文,介绍AS3中常见的位运算技巧的。小弟勉强翻译一下,有错误还请指正。原文在这里
在AS3中位操作是非常快的,这里列出一些可以加快某些计算速度的代码片段集合。我不会解释什么是位运算符,也不会解释怎么使用他们,只能告诉大家如果想清楚其中的原理这里有一篇极好的文章在gamedev.net上叫做 ‘Bitwise Operation in C' .
如果你知道任何下边没有列出来的不错的技巧,请留下个评论或者给我发个邮件。所有这些都是基于AS3的
[b]左位移几就相当于乘以2的几次方[/b]( Left bit shifting to multiply by any power of two )
大约快了300%
[code]
x = x * 2;
x = x * 64;
//相当于:
x = x << 1;
x = x << 6;
[/code]
[b]右位移几就相当于除以2的几次方[/b](Right bit shifting to divide by any power of two)
大约快了350%
[code]
x = x / 2;
x = x / 64;
//相当于:
x = x >> 1;
x = x >> 6;
[/code]
[b]Number 到 integer(整数)转换[/b]
在AS3中使用int(x)快了10% 。尽管如此位操作版本在AS2中工作的更好
[code]
x = int(1.232)
//相当于:
x = 1.232 >> 0;
[/code]
[b]提取颜色组成成分[/b]
不完全是个技巧,是正常的方法 (Not really a trick, but the regular way of extracting values using bit masking and shifting.)
[code]
//24bit
var color:uint = 0x336699;
var r:uint = color >> 16;
var g:uint = color >> 8 & 0xFF;
var b:uint = color & 0xFF;
//32bit
var color:uint = 0xff336699;
var a:uint = color >>> 24;
var r:uint = color >>> 16 & 0xFF;
var g:uint = color >>> 8 & 0xFF;
var b:uint = color & 0xFF;
[/code]
[b]合并颜色组成成分[/b]
替换值到正确位置并组合他们 (‘Shift up’ the values into the correct position and combine them.)
[code]
//24bit
var r:uint = 0x33;
var g:uint = 0x66;
var b:uint = 0x99;
var color:uint = r << 16 | g << 8 | b;
//32bit
var a:uint = 0xff;
var r:uint = 0x33;
var g:uint = 0x66;
var b:uint = 0x99;
var color:uint = a << 24 | r << 16 | g << 8 | b;
[/code]
[b]使用异或运算交换整数而不需要用临时变量[/b]
很可爱的技巧, 在本页顶端的链接里有详细的解释 ,这里快了 20%
[code]
var t:int = a;
a = b;
b = t;
//相当于:
a ^= b;
b ^= a;
a ^= b;
[/code]
[b]自增/自减(Increment/decrement)[b]
这个比以前的慢不少,但却是个模糊你代码的好方法;-)
[code]
i = -~i; // i++
i = ~-i; // i--
[/code]
[b]取反[/b](Sign flipping using NOT or XOR)
另人奇怪的是这个居然快了300%!
[code]
i = -i;
//相当于:
i = ~i + 1;
//或者
i = (i ^ -1) + 1;
[/code]
[b]使用bitwise AND快速取模[/b] (Fast modulo operation using bitwise AND)
如果除数是2的次方,取模操作可以这样做:
模数= 分子 & (除数 - 1);
这里大约快了600%
[code]
x = 131 % 4;
//相当于:
x = 131 & (4 - 1);
[/code]
[b]检查是否为偶数[/b](Check if an integer is even/uneven using bitwise AND)
这里快了 600%
[code]
isEven = (i % 2) == 0;
//相当于:
isEven = (i & 1) == 0;
[/code]
[b]绝对值[/b]
忘记 Math.abs()吧 (Forget Math.abs() for time critical code.)
version 1 比 Math.abs() 快了2500% ,version 2 居然比 version 1 又快了20% !
[code]
//version 1
i = x < 0 ? -x : x;
//version 2
i = (x ^ (x >> 31)) - (x >> 31);
[/code]
posted @
2008-07-29 14:14 姜大叔 阅读(306) |
评论 (0) |
编辑 收藏
好久没写文章了,上一个多月,很多很多烦人的事情,我也不想多说了,导致了没时间写文章了,不过今后的时间将会多点,所以在这段时间里打算多写点技术文章来填充一下Blog,主要是Flash与Java方面吧。
今天做了一个小东西,一个会转动的3D盒子的效果,用于切换不同的内容面板,是一个不错的Flex效果,该东西是用了DistortionEffects做的,首先感谢Dreamer介绍了DistortionEffects这个东东,我也是在他Blog里看到,之后用来做点东西试试的。以下是Dreamer里的一编文章:[url=http://www.zhuoqun.net/html/y2007/492.html]DistortionEffects[/url]
其实这个很简单,只是使用了DistortionEffects里面的CubeRotate效果,很炫的效果,致至该效果的实现,还有待看看它的源码进行研究研究!
以下放出我的该小Flex的源码:
[mDown=attachments/month_0712/j20071225181513.rar]点击下载此文件[/mDown]
posted @
2008-07-29 14:13 姜大叔 阅读(6527) |
评论 (48) |
编辑 收藏
本篇接着上一篇文章,上篇文章中,大家都看到最终效果了,这个例子很简单,我就粗略的讲解一下代码吧
整个Flex程序,就两个Panel面板,将控件都排放好到Panel里面,之后定好相应的属性,我主要解析一下下面的代码:
1 <mx:Script>
2 <![CDATA[
3 //导入需要用到的类
4 import mx.utils.Base64Encoder;
5 import mx.collections.ArrayCollection;
6 import mx.controls.Alert;
7 import com.adobe.images.PNGEncoder;
8
9 //这里定义一个ArrayCollection 这就是PieChart控件所需要的dataProvider,并赋好初值
10 [Bindable]
11 private var mydata:ArrayCollection = new ArrayCollection([
12 {name: "非常满意",num: 1},
13 {name: "满意",num: 1},
14 {name: "普通",num: 1},
15 {name: "不满意",num: 1},
16 {name: "非常不满意",num: 1}
17 ]);
18
19 //方法为PieChart控件自动调用的,是用于当图表要显示Label时,将会调用该方法,并传上相应的参数,这样,你就可以在该方法里自已处理好要显示的数据,再返回给Chart显示
20 private function showLabel(data:Object, field:String, index:Number, percentValue:Number):String{
21 return data.name + ":" + data.num + "\n" + Math.round(percentValue) + "%";
22 }
23
24 //该方法是当点击“确定”按钮时调用的,用于改变图表数据的ArrayCollection里的数据,再更新图表显示
25 private function changeData(ent:Event):void{
26 //定一个临时的集合用于放置数据
27 var temp:ArrayCollection = new ArrayCollection();
28 //判断用户所填的数据是否为空,如果为空的就不要加进集合里了,免得图表出现一些无谓的Label,下同
29 if(resu0.text != ""){
30 temp.addItem({name: "非常满意",num: resu0.text});
31 }
32 if(resu1.text != ""){
33 temp.addItem({name: "满意",num: resu1.text});
34 }
35 if(resu2.text != ""){
36 temp.addItem({name: "普通",num: resu2.text});
37 }
38 if(resu3.text != ""){
39 temp.addItem({name: "不满意",num: resu3.text});
40 }
41 if(resu4.text != ""){
42 temp.addItem({name: "非常不满意",num: resu4.text});
43 }
44 panel1.title = subjectTitle.text;
45 /*
46 mydata[0].num = resu0.text;
47 mydata[1].num = resu1.text;
48 mydata[2].num = resu2.text;
49 mydata[3].num = resu3.text;
50 mydata[4].num = resu4.text;
51 */
52 pc.dataProvider = temp;
53 }
54
55 //这里是截图的方法函数,参照于国外某高手地Flex截图方面的代码
56 private function catchPic(ent:Event):void{
57 //根据要截图的Panel的大小建立一个放置位图数据的BitmapData对象
58 var bitmapData:BitmapData = new BitmapData(panel1.width,panel1.height,true,0xffffff);
59 //用现时panel1对象的位图信息填充到bitmapData对象中
60 //由于每一个DisplayObject对象都有自已的用于显示的位图色彩信息,
61 //所以很多继承了DisplayObject的类都可以填充到bitmapData对象中处理,如模糊处理等
62 bitmapData.draw(panel1);
63 //用PNGEncoder类对位图信息进行压缩转换处理才得以输出PNG图片格式数据
64 var bytes:ByteArray = PNGEncoder.encode(bitmapData);
65 //再将数据进行编码,用于在JavaScript中向浏览器传播
66 //最后调用JavaScript来打开新窗口来显示图借数据
67 var b64encoder:Base64Encoder = new Base64Encoder();
68 b64encoder.encodeBytes(bytes);
69 ExternalInterface.call("showPic",b64encoder.flush(),panel1.width+25,panel1.height+25);
70 }
71 ]]>
72 </mx:Script>
JavaScript方面要写上以下函数:
1 function showPic(img,width,height){
2 window.open("data:image/png;base64," + img,"","width="+width+",height="+height+",resizable=1");
3 }
以上有几点应该要注意:
注意PieChart的dataProvider的写法,也就是ArrayCollection的写法,这与很多控件的dataProvider的都一样,比如DataGrid控件。
注意,PNGEncoder类不是Flex原本自带的,是要另外自已下载的扩展包,在com.adobe.images里面,是Adobe发布的核心扩展类库,更多的类库,大家可以在网上找找。
以下放出完整的源代码:[down=attachments/month_0710/92007102323188.rar]点击下载此文件[/down]
posted @
2008-07-29 14:10 姜大叔 阅读(2763) |
评论 (4) |
编辑 收藏
应朋友要求,做一个数据图表的东西,主要是用于将数据以图表的方式表示,以不至于用Excel看一大表格,里面密密麻麻的数字,所以,第一时间想到用Flex Chart玩玩,由于原本已有点Flex基础,So,Flex Chart也不是太难,而且效果满意漂亮,也就当作开拓该方面技术的练手之作吧,程序很简单,就在一边输入数据,另一边则显示该数据所表示的饼图图形,后来并加上了“生成图片”按钮,该功能是参照外国某位牛人所做的,不过就是该功能只支持FireFox。
[url=jiang/catch_chart/chart.html]可以去这里看看效果[/url]
待会我整理好源代码,再放出源代码,并解析一下该程序。
posted @
2008-07-29 14:08 姜大叔 阅读(2292) |
评论 (0) |
编辑 收藏
这几天无聊着,打开163邮箱,看到右上角的天气信息,突然心血来潮,自已也做个天气信息的小模块放在Blog上,就是左边侧栏的那个,其实之前都做过,只不过是用Java来解析,Flex来显示,而已之前的那个是解析www.weathercn.com的信息的,但现在该站的天气页面改版了,所以不得不重新做,既然如此,就拿163的来做吧。原理都是一样,用PHP读取163的天气信息网页,再用正则表达式解析网页的内容,提取有用的天气信息,组织好成XML格式,之后Flex用WebService来读取该PHP,得到XML数据,再相应的显示。
其实这并不难,打开的我163邮箱,查看它的网页源码,经过一番的查找分析,找到它获取天气信息的网页:http://mimg.163.com/tianqi/city/59287.html 呵呵,还是Html的哦,网易的将动态的天气生成静态的页面,我们访问起来,也不太占服务器资源吧,因为天气也是经常访问的。看看源代码,就几行Html代码,正合我的需求,免得我的PHP分析大量的代码占点资源。不过有点要注意,网易把每个城市的代号生成一个页面,即是一个城市,一个天气页面,上面那个URL是广州的,就是说 59287 就是广州的城市代码,在PHP里访问时,要跟据用户选的是哪个城市,相对应的修改那个号码就行了,如:
$url = "http://mimg.163.com/tianqi/city/".$_GET['city'].".html";
这样,你访问PHP时,要传一个城市代码参数,至于网易定义的城市代码,我这里就不列出来了,太多太长了,也找了我不少时间。我放在Flex的源码里,可以下载我的源码看看。
以下是PHP页面的代码:
1 <?php
2 include("class.Chinese.php"); #导入字符集转换的类
3 $codeTableDir = "./config/"; #指定字符码表的路径
4
5 $url = "http://mimg.163.com/tianqi/city/".$_GET['city'].".html"; #获取城市代码,组成完整的URL
6
7 $fh = fopen($url,'r'); #读取该URL获取内容
8 while(!feof($fh)){
9 $content .= fread($fh,128); #读取全部内容
10 }
11 $content = new Chinese("GB2312","UTF8",$content,$codeTableDir); #转换字符码,GB转UTF8
12 $content = $content->ConvertIT(); #转换
13 eregi('(<span class="wetCityName">)([^/]*)(</span>)',$content,$arr); #找出表示城市的内容
14 $city = $arr[2];
15
16 eregi('(<span class="wetDate">)(.*)(</span></div>)',$content,$arr); #找出表示日期的内容
17 $date = $arr[2];
18
19 $wea = split('txtAlgCen fRig handle',$content); #分开今天与明天
20
21 eregi('(<div class="wetSts">)([^<]*)(</div>)',$wea[0],$arr); #找出表示今日天气的内容
22 $info1 = $arr[2];
23 eregi('(<div class="wetSts">)([^<]*)(</div>)',$wea[1],$arr); #找出表示明日天气的内容 下同
24 $info2 = $arr[2];
25
26 eregi('(</b>)([^<]*)(</div>)',$wea[0],$arr);
27 $temp1 = $arr[2];
28 eregi('(</b>)([^<]*)(</div>)',$wea[1],$arr);
29 $temp2 = $arr[2];
30
31 eregi('(class="wetDico )(.*)("></div>)',$wea[0],$arr);
32 $pic1 = $arr[2];
33 eregi('(class="wetDico )(.*)("></div>)',$wea[1],$arr);
34 $pic2 = $arr[2];
35
36 echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?>";
37 echo "\n";
38 ?>
39 <weather>
40 <city><?= $city ?></city>
41 <date><?= $date ?></date>
42 <today>
43 <wea><?= $info1 ?></wea>
44 <temp><?= $temp1 ?></temp>
45 <pic><?= $pic1 ?></pic>
46 </today>
47 <tomorrow>
48 <wea><?= $info2 ?></wea>
49 <temp><?= $temp2 ?></temp>
50 <pic><?= $pic2 ?></pic>
51 </tomorrow>
52 </weather>
以上有几点要注意:
1。由于网易里的网页是用GB2312编码的,而Flex里接收的XML要UTF-8编码才会正确读出,所以要将获取的网易里的数据进行转码,这问题刚开始时,老是乱码,搞到心烦,后来发现网易的Html的头信息里 发现是GB2312的编码,郁闷了半天,之后上网找了个中文转码的类,下载来直接套用。
2。获取回来的天气信息的Html是分今天与明天的天气信息,而里面的格式也是一样的,所以,用正则查找时,只找到最先的那个,所以要先找个今天与明天的分界点,将其分离开来再各自解析里面的内容,得出今天的天气与明天的天气。
3。写输出XML时,格式要正确,不然Flex里读取会有麻烦,还有,encoding一定要是UTF-8,还要将该PHP文件保存成UTF-8格式,我用DW写该PHP时,因为我的DW新建文件时,默认是ISO8859-1的,所以要将其文档编码格式改为UTF-8来保存。
PHP完工之后,来到Flex这边,画好界面,添加代码如下:
先加一个HttpService标签 :
<mx:HTTPService id="hs" showBusyCursor="true" method="GET" result="{hsResult(event);}" fault="{hsFault(event);}" />
(事件响应方法我就不在这里贴出来了,可下载源码看)
当下拉选框选中城市之后,判断用户选中的是哪个城市,再对照该城市的代号,组合成URL
hs.url = "http://www.jiangzone.com.cn/jiang/weather/weather.php?city=" + cityvalues[i];
hs.send();
结果返回时,再相应的赋值:
lblCity.text = event.result.weather.city;
lblDate.text = event.result.weather.date;
lblWea.text = event.result.weather.today.wea;
weather对应XML里的<weather>根标签,city对应XML里的<city>标签。
OK,修改一下,大功告成!
以下是源码:
[down=attachments/month_0709/t2007916122822.rar]点击下载此文件[/down]
posted @
2008-07-29 14:07 姜大叔 阅读(1212) |
评论 (1) |
编辑 收藏
adobe的一些as3类库提供给google code(http://actionscript3libraries.riaforge.org/)可以随意下载...
其中的as3corelib里面包含image的png和jpg类和json(序列化数据)类包.
同时amfphp1.9beta的发布可以让你用remoting来传递amf3格式的数据.
那么有了这两个条件我们就可以用他们来直接生成png和jpg格式的图片...
使用方法也是比较简单,使用前面我提供的方法可以很方便的实现...
as3corelib里面的png和jpg类
[down=attachments/month_0709/r2007917165754.rar]点击下载此文件[/down]
as代码:
//------------------------------------------------------------------------------------
//在舞台上放一个mc名字为a,mc包含一些随意的图形
import flash.net.*;
import flash.display.*;
var hostName:String = "http://localhost/amfphp/";
var gatewayUrl:String = hostName+"gateway.php";
////////////////////////////
var bpd:BitmapData = new BitmapData(a.width, a.height);
bpd.draw(a);
//var jpegEnc:JPEGEncoder = new JPEGEncoder(80);
//var dat:ByteArray = jpegEnc.encode(bpd);
var dat:ByteArray = PNGEncoder.encode(bpd);
//
var nc:NetConnection = new NetConnection();
//nc.objectEncoding = 0;
nc.connect(gatewayUrl);
//
nc.call("SavePic.save",new Responder(onResult,onFault),dat);
//nc.call("SavePic.test",new Responder(onResult,onFault),"h..roading");
/////////////////////////////
function onResult(re:Object)
{
trace(re);
}
function onFault(re:Object)
{
trace(re);
for(var i in re)
trace(i+"=>"+re[i]);
}
//----------------------------------------------------------------------------------------------
php代码:
1 //-----------------------------------------------------------------------------------------------
2 <?php
3
4 class SavePic
5 {
6
7 function Tuya()
8 {
9 $this->methodTable = array(
10 "save" => array(
11 "access" => "remote"
12 ),
13 "test" => array(
14 "access" => "remote"
15 )
16 );
17 }
18 function test($str)
19 {
20 return $str;
21
22
23 }
24
25
26 function save($data)
27 {
28 $name = time().'.jpg'; //png
29
30 $im = fopen($name,'a');
31
32 fwrite($im,$data);
33
34 fclose($im);
35
36 return $name;
37 }
38 }
39 ?>
40
41
posted @
2008-07-29 14:05 姜大叔 阅读(240) |
评论 (0) |
编辑 收藏
在关系数据库应用中,都离不开表与表的关联,表与表之间建立了关联关系后就可以提供很多方便的操作了,比如我要查询一个学生的信息,如果不建立学生与班级的关系的话,那么,只可以显示出学生的基本信息,而学生是属于某一个班级的,我现在想知道这个学生的信息与该学生所在的班级的信息,甚至是该学生的任课老师等信息,建立了学生表,与班级表,老师表之间的关系就方便好多了,用一个学生ID就可以查找出所有相关联的数据。
在FleaPHP中,支持以下四种关联关系:
* HAS_ONE
* HAS_MANY
* MANY_TO_MANY
* BELONGS_TO
分别对应FLEA_Db_TableDataGateway表中的 $hasOne,$hasMany,$manyToMany,$belongsTo 属性
我们下面就用 老师,学生,学生档案,班级的例子说说这几个关系。
以下是SQL代码:
1 //学生表
2 Create TABLE `testDB`.`student` (
3 `stu_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
4 `stu_name` VARCHAR(20) NOT NULL,
5 `stu_class_id` INTEGER UNSIGNED NOT NULL,
6 PRIMARY KEY (`stu_id`)
7 )
8
9 //学生档案表
10 Create TABLE `testDB`.`stu_record` (
11 `record_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
12 `stu_id` INTEGER UNSIGNED NOT NULL,
13 `family_add` VARCHAR(150) NULL,
14 `family_tel` VARCHAR(20) NULL,
15 PRIMARY KEY (`record_id`)
16 )
17
18 //教师表
19 Create TABLE `testDB`.`teacher` (
20 `teacher_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
21 `teacher_name` VARCHAR(20) NOT NULL,
22 PRIMARY KEY (`teacher_id`)
23 )
24
25 //班级表
26 Create TABLE `testDB`.`classes` (
27 `class_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
28 `class_name` VARCHAR(20) NOT NULL,
29 PRIMARY KEY (`class_id`)
30 )
31
32 //由于教师与班级是多对多关系,所有建立一个中间连接表以表示该关联
33 Create TABLE `testDB`.`link_teacher_classes` (
34 `link_id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
35 `teacher_id` INTEGER UNSIGNED NOT NULL,
36 `class_id` INTEGER UNSIGNED NOT NULL,
37 PRIMARY KEY (`link_id`)
38 )
现在,建好表之后,我们还要写几个继承自FLEA_Db_TableDataGateway父类的类,以对应数据库中的Student,Teacher,Classes表(连接表不用了)如下:
1 //学生表对应的类
2 class Model_Student extends FLEA_Db_TableDataGateway{
3 var $tableName = 'student';
4 var $primaryKey = 'stu_id';
5
6 /*
7 这是一对一关系,一个学生对应唯一的一个Record档案
8 tableClass 对应要与之关联一对一的表的对象类,即档案表的类
9 foreignKey是在档案表中,对应学生表的一个外键接连字段,
10 用该字段标识该档案属于哪个学生
11 mappingName 就是用于在你查询学生信息时,学生档案字段的别名
12 */
13 var $hasOne = array(
14 'tableClass' => 'Model_Record',
15 'foreignKey' => 'stu_id',
16 'mappingName' => 'record'
17 );
18 /*
19 这是从属关系,一个学生属于某一个班级,换过来说就是班级与学生一对多,
20 一个班级有多个学生,而学生就只属于一个班级
21 tableClass 指定该类要与之有从属关系的类,这里是班级类
22 foreignKey 这里指定的是该表里的连接外表的字段,这里是学生表的班级ID字段
23 注意:这里的foreignKey指的是本表的字段,而上面hasOne关系中的foreignKey
24 指的是外表中的字段,要注意区分清楚
25 mappingName这里就不多说了,跟上面的一样,也是用于显示区分的别名
26 */
27 var $belongsTo = array(
28 array(
29 'tableClass' => 'Model_Classes',
30 'foreignKey' => 'stu_class_id',
31 'mappingName' => 'class'
32 )
33 );
34 }
35
36 //档案表对应的类
37 class Model_Record extends FLEA_Db_TableDataGateway{
38 var $tableName = 'stu_record';
39 var $primaryKey = 'record_id';
40 }
41
42 //教师表对应的类
43 class Model_Teacher extends FLEA_Db_TableDataGateway{
44 var $tableName = 'teacher';
45 var $primaryKey = 'teacher_id';
46
47 /*
48 这里确立了教师与班级的多对多关系,一个教师可以教多个班级
49 而一个班级也有多个教师
50 tableClass 指定要建立多对多关系的表的类
51 joinTable 这个属性要注意一个,这个是指定教师表与班级表关系的连接表
52 由于二维表的数据结构的原因,所以多对多关系要用一个中间表(即连接表)来表示
53 连接表中就指明两个表各自的主键ID就可以了,以两个字段作复合主键,如不用复合
54 主键,就新增加一个字段作主键也可,这没关系
55 */
56 var $manyToMany = array(
57 array(
58 'tableClass' => 'Model_Classes',
59 'mappingName' => 'class',
60 'joinTable' => 'link_teacher_classes',
61 )
62 );
63 }
64
65 class Model_Classes extends FLEA_Db_TableDataGateway{
66 var $tableName = 'classes';
67 var $primaryKey = 'class_id';
68
69 /*
70 这里确立了班级与学生的一对多关系
71 tableClass 指定该表的类要与哪个表的类建立一对多关系
72 foreignKey 外键ID,这里即指定学生表中定义的班级ID,这个是外表的字段
73 */
74 var $hasMany = array(
75 array(
76 'tableClass' => 'Model_Student',
77 'foreignKey' => 'stu_class_id',
78 'mappingName' => 'students'
79 )
80 );
81
82 var $manyToMany = array(
83 // 由于多对多关系是双向的,所以班级表也可以定义上与教师表的多对多关系
84 // 也可以不写,看需求而定,比如你想查看某个班级信息时,一并显示该班
85 // 有多少个任课老师的话,那就在这个班级类里加上关系,我这里就不写了。。。
86 );
87 }
以上的代码就建立好了关系了,现在只管使用就是了。
不过还提醒大家一点,在每个关系里,都有一个 ‘enabled’ 的属性,如果设置为 false的话,则是将该关系禁掉,所以查询出来的结果就没有该关系的相应数据,这样可以节省效率,按需要时才打开相应的关联,该属性默认为true 可以如下设置:
1 array(
2 'tableClass' => 'Model_Student',
3 'foreignKey' => 'stu_class_id',
4 'mappingName' => 'students',
5 ‘enabled’ => false
6 )
现在我们来测试一下数据。(事先自行插入几行数据用以测试)
1 $classManager = FLEA::getSingleton('Model_Classes');
2 $class =& $classManager->find(array('class_id' => 1));
3 dump($class);
出现结果如下:
这个例子已含盖了这四个关联关系的基本用法了,其它的就由你们来探索了,不久后,我将会发表一下这此关联方面的高级应用,有时间的话会写写这方面的东西。敬请期待!
posted @
2008-07-29 14:02 姜大叔 阅读(317) |
评论 (0) |
编辑 收藏
这篇文章是接着上一篇,在上一篇的基础上加了用户注册功能,注册成功后显示用户信息,由于时间关系,没有详细研究实现什么功能例子用于教学,本来我也只是FleaPHP初学者,所以,虽然例子十分的傻瓜式简单,但就当是我在学FleaPHP时的快速练习吧。
这里只实现了个注册功能,有经验的应该闭上眼睛都能做出来了,所以我这里的只是演示一下FleaPHP的大概操作流程的,也就是它的结构都是差不多,我在之前第一篇的那个Model_User类里,加了几个方法:
1 //添加一个新用户
2 function addNewUser($user){
3 return $this->_tbUser->create($user);
4 }
5 //用主键加载一个用户
6 function loadUser($userid){
7 return $this->_tbUser->find($userid);
8 }
并在Default.php里,也就是在Default的Controller里加了一个action与一个方法函数:
1 function actionRegister(){
2 $mod_user = new Model_User;
3 $username = $_POST["username"];
4 $password = $_POST["password"];
5 $password2 = $_POST["password2"];
6
7 if(strlen($username) < 6){
8 $message = '用户名要求6-10位!';
9 include('app/View/register.php');
10 return;
11 }
12
13 if($password != $password2){
14 $message = '你输入的密码不一致,请重新输入!';
15 include('app/View/register.php');
16 return;
17 }
18
19 if(strlen($password) < 6){
20 $message = '密码要求6-10位!';
21 include('app/View/register.php');
22 return;
23 }
24
25 $user = array(
26 'name' => $username,
27 'password' => $password
28 );
29 $newUserId = $mod_user->addNewUser($user);
30
31 if($newUserId){
32 $message = '注册成功!';
33 $this->showinfo($newUserId);
34 }else{
35 $message = '出现错误,注册失败!';
36 include('app/View/error.php');
37 }
38 }
39
40 function showinfo($userid){
41 $mod_user = new Model_User;
42 $user = $mod_user->loadUser($userid);
43 include('app/View/userinfo.php');
44 }
这都很直观了,大家注意的是流程的控制与转移,如果是开发小项目的话,这可能还会觉得麻烦,但当项目稍大时,程序结构将会很直观,控制流程很清淅。
之后在View文件夹里加了register.php error.php等View文件,也主要是Html的,应尽量把逻辑代码与UI代码分开,这里只是初入门篇,迟点将会介绍与Smarty模板技术结合使用的文章。
最终源码下载:[down=attachments/month_0710/k20071016235619.rar]点击下载此文件[/down]
posted @
2008-07-29 13:58 姜大叔 阅读(218) |
评论 (0) |
编辑 收藏
由于忙,很少时间学学 FleaPHP 框架,不过之前也看了看学了点东西,感觉FleaPHP框架还是很不错的,有良好的MVC模式结构,比较方便的数据库操作,好东西不敢独享,所以献上我在学习FleaPHP中的一点小心得,适合初接触 FleaPHP 框架的同学,PHP基础不太好的话,建议先补习一下PHP再看看这系列的文章。
本例子是我刚学FleaPHP框架时的小练习,一步一步循序渐进,刚开始的例子显得比较简单,都只是熟习他的环境而已,不过我将采用增量开发的模式,慢慢完善深入,同学们记得专心上堂哦,做好功课哦。(文章的末尾将提供完整源代码打包,由于篇幅有限,所以文章中贴出来的代码有的只是片段)
这篇是我写 FleaPHP 学习笔记的开篇,做的是一个简单的登录功能,牛刀小试吧,认大家认识一下 FleaPHP 的MVC结构与基本数据库操作,如果是对 FleaPHP 还不知道是什么东西的话,可以到以下网站了解了解。
fleaphp.org
数据库准备:数据库里就只有一个表 tb_user 里面有三个字段,id ,name ,password 够简单吧[smile]
我的这个FleaPHP应用的目录结构如下:
图中可以看到就一个index.php主页文件,一个app与一个FLEA文件夹,因为FleaPHP是采用单一入口框架,所以就只有一个index.php文件,所以的请求都经由这个文件再进行分类分发,这样可以提高代码的安全性,可以将程序其它文件放置到URL访问不到的地方,在index.php里引用该路径就可以了。不过有点要注意的是文件的路径的正确性。
app文件夹就是我的程序代码放置的地方,里面有Controller,Model,View,Table文件夹,Controller文件夹都是FleaPHP的命名约定的,最好使用这样的命名,因为会跟据请求到该文件夹中找相对应的类文件的,好,我们先来看看index.php的代码:
1 <?php
2 require("./FLEA/FLEA.php");
3 FLEA::import(dirname(__FILE__)."/app");
4
5 // 准备数据库连接信息
6 $dsn = array(
7 'driver' => 'mysql',
8 'host' => 'localhost',
9 'login' => 'root',
10 'password' => 'root',
11 'database' => 'test',
12 );
13
14 set_app_inf('dbDSN',$dsn);
15
16 FLEA::runMVC();
17 ?>
就这么个三四行代码,程序就启动了,呵呵,不过后面还有呢。。。
先来解析一下,第一句是 require("./FLEA/FLEA.php"); 就是要先导入FleaPHP的核心引擎,所以之前大家看到了有个FLEA文件夹,就是FLEA的核心引擎啦,这个大家先不用管它,等到要研究他的源代码时再看吧,平时的使用就看看提供的API文档就够了。
同学们注意了,FLEA::import(dirname(__FILE__)."/app"); 这句的作用是什么?这个import函数是FLEA的静态函数,用于将你的程序文件夹导入进来,如果有玩过Java的同学应该知道多少吧,当FleaPHP接收到一个请求时,会分析URL再调用相对的Controller和Action,
如:http://localhost/fleatest/index.php?controller=Shop&action=checkout 这里即调用Shop控制器里面的checkout动作,而FleaPHP怎么知道我的控制器代码放在哪里呢?就是靠import这个语句了,将程序文件夹导入进来之后,FleaPHP会自去到这个文件夹里找Controller文件夹再找Shop.php这个类文件,所以,如果你为了安全起见,将程序文件夹放在了其它访问不到的地方,就得靠这个import来导入,以FleaPHP能正确找到所需要的Controller。(可能说得啰嗦了[redface],打字累了,烧根烟先,同学们小息片刻!)
接下来$dsn这个数组就是存放你连接数据库的信息啦,聪明的同学一眼看出来了。之后当然要设置入FleaPHP配置里面啦,用这个set_app_inf('dbDSN',$dsn);,之后,FLEA::runMVC(); 这个就是启动了。我们输入的URL是 http://localhost/fleatest/index.php,由于FleaPHP默认的Controller是Default,默认的Action是Index,所以,这时,FleaPHP就会跳到app/Controller/Default.php这个页面来,代码如下:
1 <?php
2 FLEA::loadClass("Model_User");
3 class Controller_Default
4 {
5 function actionIndex(){
6 include('app/View/login.php');
7 }
8
9 function actionLogin(){
10 //新建一个对应user数据表操作的Model对象
11 $mod_user = new Model_User;
12 //根据用户输入的用户名查找数据库有没有存在
13 $result = $mod_user->findByName($_POST['username']);
14 if(empty($result)){
15 $message = '该用户不存在!';
16 include('app/View/login.php');
17 }else{
18 //判断密码是否相同
19 if($result['password'] == $_POST['password']){
20 $username = $result['name'];
21 include('app/View/welcome.php');
22 }else{
23 $message = '密码不正确,请重新输入!';
24 include('app/View/login.php');
25 }
26 }
27 }
28 }
29 ?>
为什么类名是 Controller_Default ?这都是FleaPHP的命名规则,大家可以去官网看看它的命名规则,这里就不多啰嗦了。现在,它将会调用actionIndex的默认action函数,里面只引入了一个php文件,那就是登录界面的页面。
在登录界面的Form里,设置好action提交路径,使它提交到Default控制器的Login动作(action),里面实例化了一个Model对象,该对象是封装了对表tb_user的操作,之后程序逻辑就是跟据登录成功与否来进行相应的信息表示啦,简单的实现了从Controller -> Model -> View 的MVC访问操作流程。
大概的结构就这么简单了。
最后符上源代码:[down=attachments/month_0710/520071010232245.rar]点击下载此文件[/down]
大家如果有什么疑问,可以留言或发到我邮箱里!
posted @
2008-07-29 13:57 姜大叔 阅读(345) |
评论 (1) |
编辑 收藏
缓存
功能强大的应用程序一般都有很大的开销,通常是数据获取和处理操作带来的。对于Web应用程序,这个问题是由于HTTP协议的无状态性造成的。由于HTTP协议是无状态的,对于每个页面请求,都要重复地执行相同的操作,而不论数据是否修改。要让应用程序在世界范围最大的网络中可用,会使这个问题进一步恶化。所以,毫不奇怪,人们总在想方设法地让Web应用程序运行得更高效。对此有一种特别有效的解决方案,这也是最合理的方案之一:将动态页面转换成静态页面,只有在页面内容有修改之后才重新构建,或者定期地重新构建。Smarty提供了这样一个特性,一般称为页面缓存。
如果要使用缓存,需要首先通过设置Smarty 的缓存属性来启用缓存,如下:
1 <?php
2 require("Smarty.class.php");
3 $smarty = new Smarty;
4 $smarty->caching = 1;
5 $smarty->display("news.tpl");
6 ?>
启用缓存后,调用 display() 和 fetch() 方法在指定模板 (由$cache_dir 属性指定) 中保存目标模板的内容。
处理缓存生命期
缓存的页面在由 $cache_lifetime 属性指定的生命期(以秒为单位)内有效,默认为3600秒,即1小时。因此,如果希望修改此设置,就可以设置这个属性,如下:
1 <?php
2 require("Smarty.class.php");
3 $smarty = new Smarty;
4 $smarty->caching = 1;
5 //设置生命周期
6 $smarty->cache_lifetime = 1800;
7 $smarty->display("news.tpl");
8 ?>
在此对象的生命期内,后续调用和缓存的模板都使用此生命期。
有可能需要覆盖以前设置的缓存生命期,从而能分别控制每个模板的缓存生命期。通过将$caching 属性设置为2就可以做到这一点,如下:
1 <?php
2 require("Smarty.class.php");
3 $smarty = new Smarty;
4 $smarty->caching = 2;
5 $smarty->cache_lifetime = 1200;
6 $smarty->display("news.tpl");
7 ?>
在这里,news.tpl 模板的生命期设置为20分钟,它覆盖了前面设置的全局生命期值。
通过 is_cached( ) 消除处理开销
如前面所述,缓存模板还能消除处理开销,如果禁用缓存(只启用编译),这些处理开销总是会发生。但是,默认情况下并没有启用缓存。要启用缓存,需要把处理指令放在 if 条件中,并执行 is_cached( )方法,如下:
1 <?php
2 require("Smarty.class.php");
3 $smarty = new Smarty;
4 $smarty->caching = 1;
5
6 if (! $smarty->is_cached("news.tpl")){
7 $conn = mysql_connect("localhost","name","pwd");
8 $db = mysql_select_db("news");
9 $query = "select * from news";
10
11 }
12 $smarty->display("news.tpl");
13 ?>
在这个例子中,将首先验证模板news.tpl是否有效。如果有效,则跳过数据库访问,否则才访问数据库。
为每个模板创建多个缓存
任何指定的Smarty模板都可以用于为整个新闻项,博客项等提供一个通用界面。由于同一个模板用来生成不同数量的不同项,那么如何缓存一个模板的多个实例呢?答案比你想像的要简单。Smarty的开发人员实际不已经解决了这个问题,可以通过display()方法为缓存模板的每个实例指派一个唯一标识符。例如,假设有一个用生成用户信息的模板,并希望缓存这个模板的各个实例:
1 <?php
2 require("Smarty.class.php");
3 require("User.class.php");
4
5 $smarty = new Smarty;
6 $smarty->caching = 1;
7
8 //根据不同的用户ID来区分不同的用户实例来判断有没有被缓存
9 if(! is_cached("userinfo.tpl",$_GET['userid'])){
10 $user = new User();
11
12 $smarty->assign("name",$user->getName());
13 $smarty->assign("address",$user->getAddress());
14 }
15
16 /*
17 当显示时也根据该用户的ID来区分将哪个实例进行缓存,而不影响其它用户的缓存
18 即是用userid 值来区分同一个缓存模板的不同实例,所有用户都共用一个模板,
19 但信息都不尽相同,所以不能统一缓存,要独立分开缓存
20 */
21 $smarty->display("userinfo.tpl",$_GEG['userid']);
22 ?>
特别注意下面一行:
$smarty->display("userinfo.tpl",$_GEG['userid']);
这一行对于此脚本有两个功能,一方面获取名为$_GET['userinfo'] 的 userinfo.tpl 缓存版本,另一方面,如果还不存在这个缓存,则用这个名字来缓存该模板实例。采用这种方式,可以轻松地为指定模板缓存任意数量的实例。
关于缓存的结语
模板缓存大大提升了应用程序的性能,如果决定将Smarty集成到工程中来,就应当认真地考虑缓存。但是,因为大多数强大的Web应用程序功能都体现在其动态性上,所以一方面要考虑到性能提升,另一方面也要考虑到缓存页面随时间是否仍有效,要在这二者之间进行权衡。
posted @
2008-07-29 13:55 姜大叔 阅读(214) |
评论 (0) |
编辑 收藏
Smarty 的配置文件
开发人员一直使用配置文件来存储确定应用程序行为和操作的数据。例如,php.ini 文件负责确定PHP的大量行为。对于Smarty ,模板设计人员也可以利用配置文件的强大作用。例如,设计人员可以使用配置文件存储页面标题、用户消息以及有必要集中存储的任何信息。
以下是一个示例配置文件 (名为 app.config):
#Global Variables
appName = "PMNP News Service"
copyright = "Copyright 2005 PMNP News Service, Inc."
[Aggregation]
title = "Recent News"
warning = "Copyright warning.Use of this information is for personal use only."
[Detail]
title = "A Closer Look..."
中括号中包围的项称为节(section)。节之外的项都认为是全局的。这此项应当在定义任何节之前定义。
下面将展示如何使用config_load 函数来加载配置文件,还会解释如何在模板中引用配置变量。
配置文件存储在 configs 目录中,并使用Smarty函数 config_load 加载。下面是加载配置文件 app.config 的示例:
{config_load file="app.config"}
但是要记住,此调用只能加载配置文件的全局变量。如果要加载特定的节,需要使用 section 属性指定。所以,可以使用以下语法加载 app.config 的节 Aggregation:
{config_load file="app.config" section="Aggregation"}
另外两个可选参数介绍如下:
scope:确定所加载的配置变量的作用域。默认情况下设置为local,表示变量只能用于本地模板。其它可能的设置包括 parent 和 global 。作用域设置为 parent 时,变量可用于本地模板和调用模板。作用域设为global 时,变量则可以用于所有模板。
section:指定加载配置文件的特定节。因此,如果只对某个特定节感兴趣,可以只加载该节,而非整个文件。
引用配置变量
配置文件中变量的引用方式与其它变量的引用方式有所不同。实际上,这些配置变量使用几种不同的语法来引用,下面将介绍这个内容。
1、#
在 Smarty 模板中,可以在变量前面加上#号来引用配置变量。例如:
{#title}
2、Smarty的$smarty.config变量
引用配置变量时,如果喜欢更为正式的语法,可以使用 Smarty 的 $smarty.config 变量。例如:
{$smarty.config.title}
3、get_config_vars() 方法
array get_config_vars([string variablename])
get_config_vars() 方法返回一个数组,包含加载的所有配置变量值。如果只对某个变量值感兴趣,可以通过 variablename 传入该变量。例如,如果只对以上 app.config 配置文件中 Aggregation 节的 title 感兴趣,可以首先使用 config_load 函数加载该节:
{config_load file="app.config" section="Aggregation"}
然后,在模板中启用PHP的节中调用 get_config_vars( ),如下:
$title = smarty->get_config_vars("title");
当然,无论选择哪一种获取配置参数的语法,都不要忘记首先使用 config_load 函数加载配置文件。
posted @
2008-07-29 13:51 姜大叔 阅读(328) |
评论 (0) |
编辑 收藏
Smarty 的表现逻辑
第一编文章中,只简单的讲述了Smarty里的基本原理,也就传几个变量的值而已,而本节中,将会讲述Smarty的逻辑表示结构,比如它的分支,修饰符,迭代等结构的表达。
(1) 注释:
中Smarty中,也可以使用注释,设计人员可以用注释在模板页面中传递一些说明信息等。在Smarty中的注释为:{* Hello Jiang! *},大家可以看到,Smarty中的注释是用{**}来包围的,可以单行,也可以多行,比如可以这样写:
{* Hello
Jiang! *}
(2)变量修饰符:
在Smarty中,可以为变量添加修饰符,用于对变量进行一些Smarty已定义好的操作,变量修饰符的写法是:
{$var|modifier}
其中,$var 是变量,modifier 是修饰符的单词,意为对指定变量进行某种修饰操作。
1、capitalize 修饰符
capitalize 修饰符用于对变量内的值中所有单词的首字母变为大写,可看示例:
$smarty = new Smarty;
$smarty->assign("$title","hello jiang zone");
$smarty->display("index.tpl");
index.tpl 内容为:
{$title|capitalize}
2、count_words
count_words 函数统计变量中的单词总数
3、date_format
date_format 函数是PHP strftime() 函数的包装器,它能将可以被strftime()解析的任何日期/时间格式字符串转换为某种特殊格式。
4、default
default 函数当应用层没有返回值时,default为指示特定变量的默认值提供了一种简单的方式。
5、strip_tags
strip_tags 函数删除变量字符串中的标签符号。如:
$smarty->assign("name","<b>Jiang</b>");
模板里这样写:{$name|strip_tags}
会输入如下name的值:"Jiang",它将<b></b>删除了。所以,没有输出粗体
6、truncate
truncate 函数将变量字符串截取为指定数量的字符。]
(3)控制结构
1、if-elseif-else
Smarty 的if语句与PHP语言中的if语句相同,与PHP一样,可以使用一些条件限定符如下:
eq gt gte ge
lt lte le ne
neq is even is not even is odd
is not odd div by event by not
mod odd by == !=
> < <= >=
示例:
{if $var > 5}
<p>Hello JiangZone</p>
{/if}
2、foreach
foreach 标记的作用与PHP语句中的命令相同。但如下所示,其语法大不相同。它有4个参数,其中两个是必要的:
form : 这个必要参数指定目标数组的名。
item : 这个必要参数指定当前元素的名。
key : 这个可选参数指定当前键的名。
name : 这个可选参数指定节的名。这个名是任意的,应当设置为一个描述性的名字。
看看如下例子:
require("Smarty.class.php");
$smarty = new Smarty;
$daysofweek = array("Mon","Tues","Weds","Thu","Fri","Sat","Sun");
$smarty->assign("daysofweek",$daysofweek);
$smarty->display("daysofweek.tpl");
以下是daysofweek.tpl模板文件:
{foreach from=$daysofweek item=$day}
{$day}<br />
{/foreach}
3、foreachelse
foreachelse 标记与 foreach 一起使用,与用于字符串的 default 标记作用类似,数组为空时 foreachelse 标记可以生成某个候选结果。以下是一个使用 foreachelse 的模板示例:
{foreach key=key item=item from=$titles}
{$key}: {$item}<br />
{foreachelse}
<p>No states matching your query were found.</p>
{/foreach}
注意,foreachelse 不使用结束括号:它嵌入到foreach中,这与elseif嵌入到if语句中很类似。
(4)语句
Smarty 提供了几个用于完成特殊任务的语句。
1、include
include语句与PHP包中的同名语句相同,只是它只用于将其它模板导入到当前模板。例如,假设希望在Smarty模板中导入两个文件,header.tpl 和 footer.tpl ,可以如下完成:
{include file="header.tpl"}
{include file="footer.tpl"}
2、insert
insert 标记与 include 标记的功能相同,只是它要导入不会被缓存的数据。例如,可以使用这个函数插入经常更新的数据,如股票价格,天气预报或其它在很短时间内就要改变的内容。它也接受几个参数,一个是必要的,另外三个是可选的:
name : 这个必要参数确定insert函数的名。
assign : 这个可选参数可用于将输出给变量,而不是直接发送到输出。
script : 这个可选参数可以指向在导入文件前直接执行的一个PHP脚本。当输出文件的内容依赖于脚本所完成的某个特定动作时,可以使用此参数。例如,可以执行一个PHP脚本,返回某个默认的股票价格放在不可缓存的输出中。
var : 这个可选参数用于传入所有插入模板使用的其它参数。可以通过这种方式传递很多参数。
3、literal
literal 标记告诉Smarty :标记中嵌入的任何数据都应当原样输出,不需要转换。这个标记量常用于在模板中嵌入JavaScript 和CSS ,从而不需要担心与 Smarty 的定界符冲突。
4、php
可以使用php函数在模板中嵌入PHP代码。{php}{/php}标记中的任何代码都由PHP引擎处理。
posted @
2008-07-29 13:49 姜大叔 阅读(384) |
评论 (0) |
编辑 收藏
其实本人也正在学PHP,所以就把学习PHP时的心得体会与所学到的知识分享一下吧,所以这一系列教程(或者是心得吧),将围绕在初学PHP过程中遇到的问题和需要注意的地方。
这是关于PHP中Smarty模板技术的第一编,为什么要说模板技术呢?原因很简单,如果你想做一个合格的PHP程序员的话,如果你想开发一个结构合理、移植方便的系统的话,那就要留意一下PHP的模板技术,而在PHP的模板技术中,Smarty是一个不错的选择。
使用模板化主要有两个原因:1、可以使用同样的代码基为不同的目标生成数据。2、应用程序设计人员(负责创建和维护界面的人)可以与应用程序开发人员独立工作,因为用表现和逻辑并非密不可分地纠缠在一起。但模板化引擎如何完成这种分离?有趣的是,大多数实现的做法与编程语言非常相似,为完成各种与界面有关的任务提供了良好的定义的语法和命令集。
Smarty提供了很多强大的功能。
1、强大的表现逻辑
2、模板编译
3、缓存
4、高度可配置和可扩展
5、安全
如何使用Smarty?
使用Smarty与使用其它任何类库一样。对于初学者,只需要在执行脚本中使Smarty类库可用。实现这一点非常容易,使用 require() 语句即可:
require("Smarty.class.php");
之后,就可以实例化Smarty类:
$smarty = new Smarty;
现在,就可以用Smarty来做东西了。下面看看一个小例子:
以下是模板文件 index.tpl :
1 <html>
2 <head>
3 <title>{$title}</title>
4 </head>
5 <body bgcolor="#ffffff" text="#000000" link="#0000ff" vlink="#800080" alink="#ff0000">
6 <p>
7 Hi,{$name}.Welcome to the wonderful world of Smarty.
8 </p>
9 </body>
10 </html>
以下是PHP页面方面代码 index.php:
<?php
require('Smarty.class.php');
$smarty = new Smarty;
$smarty->assign("name","JiangZone");
$smarty->assign("title","Jiang's Blog");
$smarty->display("index.tpl");
?>
从以上两段代码可以看得到,index.tpl文件是页面文件,没有业务逻辑代码,只有{$name},{$title},初学者可能觉得怪,不过如果有Java基础的话,应该看得出,它跟Jsp的EL表达式有点类似,或者跟标签库功能有点像。这样看上去,页面跟程序逻辑就分开来了,设计师专注于他的页面设计,需要放入处理后的值的话,就加上个{$name}等标记,而程序员则专注于程序逻辑的实现,将处理结果放到模板页面那里相应的变量位置。明眼人可能很快看得出,上面PHP代码里,$smarty->assign("name","JiangZone");就是一个对模板赋值的过程,意为为模板中的变量$name 的值设为字符串“JiangZone”,下面的也是一样,而最后那句,$smarty->display("index.tpl");则是将上面设置好的值应用到index.tpl模板上,并将应用后的结果输出到客户端。
posted @
2008-07-29 13:46 姜大叔 阅读(514) |
评论 (0) |
编辑 收藏
我在初学PHP期间,学了点入门文章,学了学语法,就想做点小东西来练练手,做什么好呢?就做个购物车吧,页面都做好了,现在正写代码进去,测试运行,但有个小问题,我在产品列表里明明是选了几个产品的,即是选中了几个 Checkbox ,checkbox 的名字都是"prodno" 但怎么在PHP里用 $_POST['prodno'] 来获取它的值,总是只获取到最后的那个checkbox 的值,为什么?检查了form几遍,没有错误,再检查了PHP几遍,也没错啊[loo](其实是checkbox里的name错了,当时不知道PHP要这样),郁闷了,我以前做Java都是这样写的呀,有什么问题?做ASP时,也是这样写也没错呀,问题出在哪呢?
后来用 is_array($_POST['prodno']) 得到的是false,不会吧,PHP不会获取重复名字的表单?唉,后来上网Google了一翻,原来PHP里获取重复名字的表单值跟JSP/ASP等的都不一样,JSP的直接用 request.getParameterValues("prodno"); 返回的是一个String[] 数组,但PHP的差别在于,要将form里的 checkbox 的 name 写成: prodno[] 就是要多了后面那对方括号,之后PHP里 $_POST['prodno'] 获得的就是一个 array ,唉,这样都行,不过后来想想,这个可能跟PHP的一个语法有关吧,在PHP里,数组是可以这样赋值的:
$arr[] = 'aaa';
$arr[] = 'bbb';
$arr[] = 'ccc';
这样写的话,$arr 里就有三个值,key分别是0,1,2,而value分别是'aaa','bbb','ccc',
就是说在PHP里,你给数组赋值的话,不写key的话,他会默认用递增的数字来自动当key,所以,当表单提交到PHP里时,实际上就是
$prodno[] = 'a';
$prodno[] = 'b';
所以,当 $_POST['prodno'] 的值就是$prodno 的值,所以它是一个数组,
而如果表单里name的值写成 prodno 的话,就会是
$prodno = 'a';
$prodno = 'b';
所以,大家可以看得出问题所在了,这样$prodno的值始终会给最后的那个值覆盖,所以就出现我之前的现像我用$_POST['prodno']获取到的值总是我最后的那个checkbox的值,所以更不用说is_array()了,当然是得到false了。
所以,对于一些有其它编程语言基础的人来说,学PHP的话,要注意这点,跟别的语言都不同。
posted @
2008-07-29 13:45 姜大叔 阅读(1376) |
评论 (2) |
编辑 收藏
本文讨论的是如何彻底杜绝warning: Cannot add header information - headers already sent in...... 这种令人莫明其妙的的错误。
只要你写过PHP代码,相信都遇上过这个大多时候都令人莫明其妙的warning吧..今天我们就来搞定它...............
看了PHP手册,回答如下:
消息“Warning: Cannot send session cookie - headers already sent。。。”或者“Cannot add header information - headers already sent。。。”。
函数 header(),setcookie() 和 session 函数需要在输出流中增加头信息。但是头信息只能在其它任何输出内容之前发送。在使用这些函数前不能有任何(如 HTML)的输出。函数 headers_sent() 能够检查您的脚本是否已经发送了头信息。请参阅“输出控制函数”。
意思是:不要在使用上面的函数前有任何文字,空行,回车,空格等。但。。。问题是,这答案并不令人满意。因为往往程序在其他PHP环境下运行却正常。
首先:这错误是怎么产生的呢?让我们来看看PHP是如何处理HTTP header输出和主体输出的。
PHP 脚本开始执行时,它可以同时发送header(标题)信息和主体信息。 Header信息(来自 header() 或 SetCookie() 函数)并不会立即发送,相反,它被保存到一个列表中。 这样就可以允许你修改标题信息,包括缺省的标题(例如 Content-Type 标题)。但是,一旦脚本发送了任何非标题的输出(例如,使用 HTML 或 print() 调用),那么PHP就必须先发送完所有的Header,然后终止 HTTP header。而后继续发送主体数据。从这时开始,任何添加或修改Header信息的试图都是不允许的,并会发送上述的错误消息之一。
好!那我们来解决它:
笨方法:把错误警告全不显示!
掩耳盗铃之计,具体方法就不说了 ^_^#
解决方案:
1)适用于有权限编辑PHP。INI的人
打开php。ini文件(你应试比我清楚你的php。ini在哪里),找到
output_buffering =改为on或者任何数字。如果是IIS6,请一定改为ON,不然你的PHP效率会奇慢。
2)使用虚拟主机,不能编辑PHP。INI,怎么办?
简单:
在你的空间根目录下建立一个。htaccess文件,内容如下:
AllowOverride All
PHP_FLAG output_buffering On
不幸的情况是:还是不行?全部网页都不能显示啦?
那么,你可以打电话骂一通空间商,然后让他给你把apache的。htaccess AllowOverride打开
3)在PHP文件里解决
ob_start()
启用output buffering机制。 Output buffering支持多层次 -- 例如,可以多次调用 ob_start() 函数。
ob_end_flush()
发送output buffer(输出缓冲)并禁用output buffering机制。
ob_end_clean()
清除output buffer但不发送,并禁用output buffering。
ob_get_contents()
将当前的output buffer返回成一个字符串。允许你处理脚本发出的任何输出。
原理:
output_buffering 被启用时,在脚本发送输出时,PHP并不发送HTTP header。相反,它将此输出通过管道(pipe)输入到动态增加的缓存中(只能在PHP 4。0中使用,它具有中央化的输出机制)。你仍然可以修改/添加header,或者设置cookie,因为header实际上并没有发送。当全部脚本终止时,PHP将自动发送HTTP header到浏览器,然后再发送输出缓冲中的内容。
posted @
2008-07-29 13:44 姜大叔 阅读(175) |
评论 (0) |
编辑 收藏
真的很久很久没有写过文章了,唉,惭愧啊!寒暄的说话就不多说了,直入主题。有人问过我,如何制作一些论坛的自动发言机器人?我说,这很简单啊,(如果没有图片验证码的话!)在Java里,有些URL,URLConnection的类啊,这些类可以访问一个URL获取数据,可以发送Request,你就可以结合一些类做一个自动发言的了,但用Post发送表单的话,就没有直接的,比较麻烦,如果还要处理Cookie的话,之后我想了想,想到了可以用Jakarta Commons 下面的开源项目啊,有一个项目名叫“HttpClient”的,这个就是用Java写的Http客户端,可以说是一个简单功能的浏览器吧,只是不能解析HTML标签,写这个解析可不是易事。既然我们是写个自动访问网站的程序,就不用解析显示HTML啦。朋友说能写个招聘网站的自动刷新简历的程序就好了,那就用51job为例吧。
首先,你得去下载HttpClient的包。
http://jakarta.apache.org/commons/httpclient
代码不多,我们就先来看看代码吧:
1 import java.io.*;
2 import org.apache.commons.httpclient.*;
3 import org.apache.commons.httpclient.methods.*;
4
5 public class HttpTest {
6
7 /**
8 * @param args
9 * @throws Exception
10 */
11 public static void main(String[] args) {
12 //先建立一个客户端实例,将模拟一个浏览器
13 HttpClient client = new HttpClient();
14
15 //这个是URL地址,我经过分析51job网站登录后的跳转到的地址,并分析得它在JavaScript里提交的URL的参数,不同网站就自已分析了,这个就是登录后刷新简历的URL地址
16 String url = "http://my.51job.com/my/Pop_RefreshResume.php?en=0&ReSumeID=88888888&Read=0&ID=88888888";
17
18 //之后再建立一个Post方法请求,提交刷新简历的表单,因为提交的参数较多,所以用Post请求好了
19 PostMethod method = new PostMethod(url);
20
21 //下面的就是将要提交的表单的数据填入PostMethod对象里面,以name , value 对加入!
22 method.addParameter("HPNation", "086");
23 method.addParameter("HPCity","020");
24 method.addParameter("HPNumber","88888888");
25 method.addParameter("FPNation","086");
26 method.addParameter("FPCity","020");
27 method.addParameter("FPNumber","88888888");
28 method.addParameter("FPExtension","000");
29 method.addParameter("MPNation","086");
30 method.addParameter("Mobile","13888888888");
31 method.addParameter("EmailAdd","888@888.com");
32 method.addParameter("ReSumeID","88888888");
33
34 //这里是建立请求时服务器需要用到的Cookie。
35 Cookie cookie = new Cookie(".51job.com","51job","ccry%3D.0%252FZKBaMTmj82%26%7C%26cconfirmkey%3DcpwWgp7FC9FZM%26%7C%26cresumeid%3D88888888%26%7C%26cresumeids88888888d888826%7C8408ilstatus%3D2%26%7C%26cnickname826cenglis8cautologin%3D","/",null,false);
36
37 //将设置好的Cookie加入模拟的客户端里。当请求发生时,就会将Cookie写进请求头里了
38 client.getState().addCookie(cookie);
39 int i=0;
40
41 //开始死循环
42 while(true){
43 try{
44 //这里是要求客户端发送一个请求。直接将PostMethod请求出去。
45 client.executeMethod(method);
46
47 //下面是获取返回的结果
48 InputStream in = method.getResponseBodyAsStream();
49 ByteArrayOutputStream baos = new ByteArrayOutputStream();
50 byte[] buff = new byte[1024];
51 int len = -1;
52 while((len=in.read(buff))!=-1){
53 baos.write(buff, 0, len);
54 }
55 String result = new String(baos.toByteArray());
56
57 //释放资源
58 in.close();
59 baos.close();
60
61 //在51job里,刷新简历成功的话,会返回一些JavaScript代码,里面有个alert()输出“简历已刷新”的信息的,你分析结果,如果有这句话,则成功刷新了。之后就让线程睡眠1分钟后循环刷新!
62 if(result.indexOf("简历已刷新")!=-1){
63 System.out.println("简历已刷新! " + ++i);
64 }else{
65 System.out.println("刷新失败!");
66 }
67 Thread.sleep(60000);
68 }catch(Exception ex){
69 System.out.println("******** Error! ********");
70 try{
71 //出现错误时,再等待20秒后再重新进行刷新。
72 Thread.sleep(20000);
73 }catch(Exception e){
74 System.out.println("******** Thread Error! ********");
75 }
76 }
77 }
78 }
79
80 }
我们再来看看,其实很简单,我们来说明一下。HttpClient将很多Http协议底层的东西都封装了,这样很方便使用,如果自已用Socket写的话,还有处理很多信息,Http协议的三次握手等等的操作,很是麻烦。现在用HttpClient就一步到位了。但要注意一下,51job里面的简历刷新是要先登录后才可以进行的,而熟悉Web开发的人员都知道,登录后服务器就会记录下你的Session,而Session也是基于Cookie的,所以Session ID是以Cookie的方式记录在客户机的,这样每次请求都要将该Cookie发送到服务器验证,这样才可以保证Web的状态。所以,你可以在浏览器里先登录一次。之后找出该网站的Cookie文件,将里面的内容复制出来放上上面代码的Cookie类里面。你也可以用这个HttpClient写一个登录的请求,之后再从ResponseHeader里获取Cookie。至于表单里的信息,就按自已需要填写就行了。就是模拟一个浏览器,将你的简历刷新的表单模拟出来再发送。
在下面,我给出一本电子书给大家下载参考:《Jakarta Commons Cookbook.chm》
[down=attachments/month_0803/020083401424.chm]点击下载此文件[/down]
posted @
2008-07-29 13:09 姜大叔 阅读(1723) |
评论 (2) |
编辑 收藏
现在双核的CPU已经卖到白菜价了,四核也将在08年迎来普及风暴,这样看来,硬件处理性能是得到很大提高了,但是不是说明程序的效率就可以忽视了呢?肯定不是,程序也将配合硬件的提高,开发出更有效率,更强大,更好的人工智能的程序。多核的趋势,这不得不提到程序的并发性能,不然的话,100核的CPU也只会英雄无用武之地。如何更大地发挥多核CPU的性能,就要用到软件的并发处理能力了。但要设计得合理的并发程序也不是这么简单的事情,线程的控制也是很头痛的问题,还有安全问题等等,我越来越感觉到并发的重要性,所以这段时间也在找Java并发,线程方面的资料,打算钻研一番,并在几天后整理一下,将一些心得放上来。将会是Java并发优化方面的文章!另外,好像听说Flash Player 10将会支持线程,不知道是不是真的,看现时的AS3里没有线程的概念,将会出AS4与之配合?不管怎么都好,期待是美好的事情!
posted @
2008-07-29 13:07 姜大叔 阅读(252) |
评论 (0) |
编辑 收藏
SVN的好处,不用多说了,当你的项目做到一定规模时,多人开发甚至异地多人协作开发时,可以为你的团队省去很多麻烦。可能很多人都用过SVN的客户端工具,如TortoiseSVN或者是Eclipse中的插件,网上有很多开源项目,或者很多源代码,都会用这种方式发布,稍有关注一下技术的很多时候都会下载别人的源程序结合自已的项目开发或者是学习,比如去Google Code有很多。我们只用客户端去连接SVN的服务器,之后更新代码副本到本地。但我们也应该要认识一下SVN的服务器端,比如你是个项目的负责人建立一个项目让大家协同开发等,就要在你的机子上打开SVN服务器程序放上你的项目,让其它人用客户端去连接获取或更新代码。
配置SVN服务器所需要的准备工具:
SubVersion 可从
http://subversion.tigris.org 下载。这里就是SVN服务器的程序
SVNService.exe (非必要)这个是将你的SVN服务端程序注册成Windows系统服务的,就好比是Tomcat,当你系统启动时,该SVN服务器会自动启动并后台运行,不注册成服务的话,就要自已手动打开了。
现在工具都准备好了,动工。
下载好SubVersion后,就进行安装,如果你下载的是ZIP包,那就解压到自定的目录就可以使用了。
1. 打开CMD命令行模式,进入到SubVersion目录下的bin目录下,输入如下命令:
svnadmin create d:\svn_projects\project1 (路径可自定)
如图:
注:要先在D盘中建立 svn_projects目录 目录名称可以自已定,
之后打开该目录,你可以看到,SVN帮你建立了一个project1的目录,里面生成了几个方件夹与文件,先不理它
现在,存放项目的路径建好了,下面我们要来配置一下该项目的用户,密码等信息
来打开project1/conf/文件夹下的svnserve.conf文件,以记事本打开就行了,你将会看到很多注释(都是采用Unix风格的 # 注释)将
# anon-access = read
# auth-access = write
# password-db = passwd
# realm = My First Repository
这几句的的注释符号去掉找去掉前面的空格如下:
anon-access = read
auth-access = write
password-db = passwd
realm = My First Repository
之后打开passwd文件,以记事本打开,在 [users] 下面添加你的用户名与密码
比如我要添加一个 jiang 用户,密码是 123456 那么这样写
jiang = 123456
保存后就OK了,那么现在,可以运行SVN服务器程序了。
在Cmd命令行下进入到SubVersion目录下的bin目录下,输入如下命令:
svnserve -d -r d:\svn_projects
如图所示:
这样服务器就启动了,它会一直侦听的,所以不要关闭窗口。像Tomcat一样一直挂着就行,如果你觉得这样麻烦,就可以用SVNService.exe这个工具来注册成系统后台服务运行。
注意:你现在的项目的SVN地址就是:svn://localhost/project1 内网就更换你的内网IP就行了,公网也换为你的公网IP就行了
现在服务器就启动了,之后你可以用Eclipse的svn工具等将项目文件放到该SVN服务里共享,
如下图所示:
就这样,SVN的服务器端就搭建好了,现在大家可以用SVN客户端更新下载下来开发了。
关于其它SVN客户工具的登录,可以参考以下文章
如何结合使用Subversion和Eclipse
posted @
2008-07-29 13:06 姜大叔 阅读(305) |
评论 (0) |
编辑 收藏
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息,以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制。Java反射机制主要提供以下功能:
[*]在运行时判断任意一个对象所属的类;
[*]在运行时构造任意一个类的对象;
[*]在运行时判断任意一个类所具有的成员变量和方法;
[*]在运行时调用任意一个类的方法;
[*]生成动态代理;
在JDK中,主要由以下类来实现Java反射机制,这些类都位于 java.lang.reflect 包中。
[*] Class 类:代表一个类。
[*] Field 类:代表类的成员变量
[*] Method 类:代表类的方法
[*] Constructor 类:代表类的构造方法
[*] Array 类:提供了动态创建数组,以及访问数组元素的静态方法
看看下面例子:它读取命令行参数指定的类名,然后打印这个类的所具有的方法信息:
1 import java.lang.reflect.*;
2 public class DumpMethods{
3 public static void main(String[] args)thorws Exception{
4 //加载并初始化命令行参数指定的类
5 Class classType = Class.forName(args[0]);
6 //获得类的所有方法
7 Method[] methods = classType.getDeclaredMethods();
8 for(int i=0;i<methods.length;i++){
9 System.out.println(methods[i].toString());
10 }
11 }
12 }
运行命令 “java DumpMethods java.util.Stack”,就会显示java.util.Stack 类所具有的方法。
这个只是十分之基本,很简单的示例,Java的反射还可以做很多东西,可以跟据用户的输入就可以得知哪些类,需要做什么操作,就是说动态跟据需要来调用相应的类与相应的方法,我之前也做过,用Struts结合Java反射来实现一些功能,有些时候,别人的接口代码里只返回一个Object给你,而你就要用反射来得知该Object是属于哪一个类型,之后根据设置做相应的操作,不知道大家有用过Hibernate没有,这个框架就是大量用到了Java反射,用户只需配置一下XML文件,Hibernate运行时,就会读取解析XML配置文件,之后动态找相应的类,实例化相应的POJO等等,这些都是全动态化的,也就是说,我请求这个表的数据时,就自动实例化该表的POJO实例,这些都是在XML配置文件里定义好映射关系。ORM框架就如此,很多中间件也是大量使用反射,将反射灵活的运用起来,将会得到意想不到的效果,这也是Java的半动态语言吧。使编程工作变得十分灵活。
posted @
2008-07-29 13:00 姜大叔 阅读(280) |
评论 (1) |
编辑 收藏
摘要: 不知道大家有没有用过农业银行的网上银行服务,该网上银行登录时,输入密码时,不可以直接打键盘输入,要用鼠标点击图形小键盘来输入,而小键盘里面的数字是随机排列的。
这样一来,可以防止木马记录用户键盘输入的密码被盗,确实是挻实用的功能,研究了一翻后,也大根掌握了它的工作原理,现在用Java来实现,大概是这样的:用Strut...
阅读全文
posted @
2008-07-29 12:47 姜大叔 阅读(1208) |
评论 (1) |
编辑 收藏
摘要: 上几天,要做Jsp文件上传,上网Google了几下,发现多是用JspSmartUpload组件来实现上传,于是想将其应用到我的Struts应用里头,刚开始不太会用,随便写了个表单页面,就一个文件表单域,上传,OK,正确接收,但当应用到自已的项目里时,发现其它表单的信息出现乱码了,中文问题又出来了。
但是确定是加上了这句:enctype="multipart/form-data",还是不行,文件是可以正确获取了,只是其它的Text Input表单传来的中文怎么转换编码都不行,不能正确还原中文,我用的是UTF-8编码。我想,这应该是JspSmartUpload的问题吧,上网找了找,很多都没说过这问题,有很多文章都是说教你怎么用JspSmartUpload上传文件等等的,但那些例子都是只上传一个文件表单,没有混合上传与普通表单的信息。
郁闷了半天,既然是组件的问题,我想到想看看修改它的源码来实现中文混合表单上传,我下载下来的都是Jar包的,都是已编译好的,又没时间上网找源码了,只好反编译一下,修改,再重新编译。唉,编译下来的类里面的变量名等等都很混乱,
阅读全文
posted @
2008-07-29 12:37 姜大叔 阅读(875) |
评论 (5) |
编辑 收藏