Posted on 1 Comment

AS3的事件傳遞機制(Event、dispatchEvent及addEventListener)

當事件被發送出來之後。Event一般來説都會有一個Flow。 Flow分三個部分:

  1. Capture(捕獲階段)
  2. Targeting(目標階段)
  3. Bubbling(冒泡階段)

其關係圖如下:(來源為http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html)

efAzj

值得一提的是,在FLASH裡,只有和UI相關的物件,會有上圖所示的目標及補獲階段的事件流

一般像是Timer、Loader事件,是直接進入目標階段,並不會有上圖所示的事件流的流程。
在宣告Event時,也可以先指定事件是否要完整的跑完全部的流程。

宣告事件的語法及及參數意義如下:

var event:Event = new Event(type, bubbles, cancelable);

參數說明(官方文件)

  1. type:String是要發送的事件的識別名稱EX:Event.COMPLETE
  2. bubbles:Boolean預設值是false,這個值是用來設定是否要有冒泡階段,如果傳false,代表他不會跑完全部的流程,只會從Capture到target階段就停止
  3. cancelable:Boolean預設值是false,這個值是用來設定是否這個事件可以被event.preventDefault();取消,如果設定為true的話,代表此事件是可以被取消的(稍候在dispatchEvent會更詳述這部份)。
    常用的可以取消的事件有(cancelable為true):   FocusEvent.MOUSE_FOCUS_CHANGEFocusEvent.KEY_FOCUS_CHANGETextEvent.TEXT_INPUT

而發送事件則是用這一段程式碼:

var result:Boolean = box.dispatchEvent(event);

在發送事件時,要注意,假使今天我們是在STAGE裡面有一個box物件,那當我們用box.dispatchEvent(event);
即使事件是用box發送的,事件還是會從stage > root > box這樣跑。(請見上圖)
假如在建立Event時,bubbles設為true,那在上圖跑的流程為stage > root > box > root > stage
而若bubbles設為false,跑的流程則是stage > root > box

當我們用root.dispatchEvent(event);
bubbles設為true,上圖跑的流程為stage > root > stage
bubbles設為false,流程為stage > root

由此可知,事件流只會到目標(Target),就會停止往下傳
假如今天box裡面有一個物件button,若是用box.dispatchEvent(event)
button物件是不會接到事件流的。(因為到目標階段便會開始bubbles或停止)
並且假如今天root裡同時有box和box2兩個物件,
假使我們用box.dispatchEvent(event),則box2也不會接到事件。

至於第三個在建立Event的參數cancelable,不論值是true或false,都不會影響事件流的流程。
那他的作用是做什麼的呢?

我們可以發現,dispatchEvent有回傳一個布林值,cancelable就是在影響這個布林值的傳回參數。
今天我們假如要創建一個物件,但是要讓使用者有權力去阻止這個事件發生後的後續發展時,可以這樣寫

if(this.dispathEvent(event)){
     trace("success!");
     //在這寫後續發展
}

假使使用者在監聽的途中呼叫 event.preventDefault();,並且EVENT的cancelable為true時,dispatchEvent回傳的值就會是false,便不會執行trace("success!");那塊區塊。但假使EVENT的cancelable為false時,不論你在事件執行中有沒有呼叫event.preventDefault(),dispatchEvent回傳的值就會是true

一般我們會在傳遞事件的途中,去將事件攔截下來。增加監聽事件的程式碼如下

box.addEventListener(MouseEvent.MOUSE_DOWN,parent1Event);

官網關於addEventListener的說明如下

addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

傳入的參數意義如下:

  1. type(String):事件名稱
  2. listener(Function):要執行的Function
  3. useCapture(Boolean):這個和本篇要說的事件流有關,當今天傳入的值為true,則只能在補獲階段去抓取事件,在冒泡階段是聽不到的。如果將 useCapture 設置為 true,則偵聽器只在捕獲階段處理事件,而不在目標或冒泡階段處理事件。
    如果 useCapture 為 false,則偵聽器只在目標冒泡階段處理事件
    要在所有三個階段都偵聽事件,需註冊 addEventListener 兩次:一次將 useCapture 設置為 true,一次將 useCapture 設置為 false。當今天若useCapture為true時,並不會聽到target階段時的事件但若為false時,也可以聽到target階段時的事件

    假使今天我們用box.dispatchEvent(event);來發送事件,
    然後box的監聽事件的useCapture設為true。ex : box.addEventListener(MouseEvent.CLICK, rootClick, true);
    這樣rootClick事件並不會被呼叫到。

    若box的監聽事件的useCapture設為false 。ex : box.addEventListener(MouseEvent.CLICK, rootClick, false);
    這樣rootClick事件便被呼叫到。

  4. priority(int):若今天同時有很多個監聽器同時監聽同一事件,可用這個值來設定那一個監聽器應該被優先執行。數字愈高代表會愈快執行該事件偵聽程式。
  5. useWeakReference(Boolean):是否使用弱關連。若是用弱關連,當被監聽的物件所指到的變數被指向記憶體的其他地方,這個關係也會一併被取消(物件會被GC回收)。但如果傳進的值是false,當今天被監聽的物件的其他關連被指向別處時,該物件不會被GC回收,需要手動removeEventListener時,該物件才會被GC回收。

除了上述的幾個參數外,還有幾個函數可以中斷事件流的流程。
那就是stopPropagation()stopImmediatePropagation()

那這兩個函數有什麼差別呢?

下圖可以很清楚的解釋差異:
stopPropagation():會把同一階層的其他事件跑完才停止。
5MYe3

stopImmediatePropagation():立刻停止之後所有的事件流。
jleAX

假如今天box同時有兩個監聽函數如下

box.addEventListener(MouseEvent.MOUSE_DOWN,eventMouseDownHandler1,false,1);
box.addEventListener(MouseEvent.MOUSE_DOWN,eventMouseDownHandler2,false,2);

則由於priority值的關係,eventMouseDownHandler2會先被執行,eventMouseDownHandler1在之後才被呼叫
那若是在eventMouseDownHandler2裡面呼叫event.stopPropagation(),eventMouseDownHandler1還是會被執行,才將事件流中斷。
但是若在eventMouseDownHandler2裡面呼叫event.stopImmediatePropagation(),則eventMouseDownHandler1就不會被呼叫到

相關資源

  1. AS3.0的事件機制(詳細)
  2. AS3筆記-事件流(event flow)
  3. Event propagation
  4. Introduction to event handling in ActionScript 3.0
Posted on

UIView及繪圖練習範例APP

作業目標:Youtube操作影片
練習原始碼:homework0803

這個作業主要是在練習對view以及簡單的繪圖的操作,還有timer的使用
因此我截錄一些我覺得是練習關鍵的程式碼

下面的是viewController的相關程式碼

一個個出現的程式碼,按下show按鈕時觸發

- (IBAction)showButtonClick {
    [self clearButtonClick];
    [self.circleNumber resignFirstResponder];
    total = [self.circleNumber.text integerValue];
    index = 0;
    //設定間隔一個個產生circle
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(addItems:) userInfo:NULL repeats:YES];
}

取得圓圈排列坐標的程式碼,傳入值為圓半徑及角度

//取得坐標
- (CGPoint)getPoint:(int) radius withAngel:(CGFloat) degrees{

    int y = sin(degrees * M_PI / 180)*radius;
    int x = cos(degrees * M_PI / 180)*radius;
    CGPoint btCorner = CGPointMake(x, y) ;
    return btCorner;
}

下面的程式碼則是在被新增的UIView物件裡面

顯示動畫,這邊要注意的是因為定位點的關係,由於我們希望元件的縮放是以view的中心來做縮放
所以改變的值是self.bounds,一般我們在做元件內部繪圖事件,都會使用self.bounds
而self.frame則是在外部設定物件資訊時使用。

-(void) playAnimation{
    [UIView animateWithDuration:0.5f
                     animations:^{
                         self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 50, 50);
                     }completion:^(BOOL finished) {
                         [UIView animateWithDuration:0.5f
                                          animations:^{
                                               self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 30, 30);
                                          }completion:^(BOOL finished) {
                                          }];

                     }];
}

另外,因為- (id)initWithFrame:(CGRect)frame被呼叫的時間是在circle被新建的時後,而不是在被加到畫面時,
為了落實動畫的被執行時間,所以我又實作了layoutSubviews這個方法,
並加上BOOL init當今天是第一次被呼叫時,才會執行playAnimation

-(void)layoutSubviews{
    if(init){
        [self playAnimation];
        init = NO;
    }
}

然後實作按下後縮小離開時恢復大小的功能

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [UIView animateWithDuration:0.2f
                     animations:^{
                         self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 0, 0);
                     }
                     completion:^(BOOL finished) {
                     }];
}

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{

    [UIView animateWithDuration:0.2f
                     animations:^{
                         self.bounds = CGRectMake(self.bounds.origin.x, self.bounds.origin.y, 30, 30);
                     }
                     completion:^(BOOL finished) {
                     }];

}

原始碼下載:homework0803

Posted on

上課筆記(五) – UIView操作及繪圖介紹

view介紹

  1. UIWindow是會在一開始便被xcode建立起來,裡面有很多view。
  2. View的操作:
    1. 於上層操作subview: 在supervview裡是用MutableArray的index去管理subView,這index就是tag,tag=0是代表subview自己。
      [self addSubView: view];//新增view
      [self insertSubView: view atIndex:0];//新增view在layer0
      [self insertSubView:view belowSubview:upView];//新增view在upView之下
      [self insertSubView:view aboveSubview:upView];//新增view在upView之上
      [self exchangeSubviewAtIndex:0 withSubviewAtIndex:1];//將第0層的物件和第1層的調換
      UIView view = [self viewWithTag:1];//取出在storyboard中tag為1的物件
    2. 移除自己: [view removeFromSuperview];
    3. 設為隱藏:[view setHidden:YES];
  3. 繪圖資訊相關的包括CGRect和CGSize及CGPoint,可以用NSValue包起來
        //將NSRect放入NSArray中
        NSMutableArray *array = [[NSMutableArray alloc] init];
        NSValue *value;
        CGRect rect = CGRectMake(0, 0, 320, 480);
        value = [NSValue valueWithBytes:&rect objCType:@encode(CGRect)];
        [array addObject:value];
        NSLog(@"array:%@",array);
    
        //從Array中提取
        value = [array objectAtIndex:0];
        [value getValue:&rect];
        NSLog(@"value:%@",value);
  4. view有兩個用來描述其資訊的rect:framebounds
    這個網頁有說明這兩者的不同:http://n11studio.blogspot.tw/2012/06/frame-bounds.html
    值得一題的是這兩者的定位點不同,bounds是在畫面的中心,frame則是在左上角。

    1. Frame:Frame指定了View相對於其父View座標系的位置和大小,用在想要加入一個新產生的view時。
    2. Bounds:Bounds則是view相對於自身坐標系的位置和大小,bounds的起點通常都是(0,0)。處理事件或要畫內部的元件時使用。
    3. Center:顧名思義就是view的Frame的中心。
  5. 要注意,在viewDidLoad裡取得的frame和viewWillAppear裡的並不一樣,在viewWillAppear裡時的x及y坐標位置才會被移到正確位置。
  6. 元件自動縮放:imageView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
  7. UIView的各個函數呼叫時間(請見此),以下說明幾個我曾用過的:
    1. setNeedsDisplay:讓uiview重新執行drawRect
    2. setNeedsLayout:重新計算view layout的大小
    3. layoutSubviews:需要調整subview的大小時呼叫(詳細解說
    4. 其他view相關處理事件
      – didAddSubview:
      – willRemoveSubview:
      – willMoveToSuperview:
      – didMoveToSuperview
      – willMoveToWindow:
      – didMoveToWindow
    5. view的動畫處理相關
      + animateWithDuration:delay:options:animations:completion:
      + animateWithDuration:animations:completion:
      + animateWithDuration:animations:
      + transitionWithView:duration:options:animations:completion:
      + transitionFromView:toView:duration:options:completion:

Drawing介紹

    1. 每次繪圖都需要取出繪圖物件:CGContextRef ctx = UIGraphicsGetCurrentContext();
    2. 畫三角型範例:
      - (void)drawRect:(CGRect)rect
          CGContextRef context = UIGraphicsGetCurrentContext();
          [[UIColor whiteColor] set];
          UIRectFill([self bounds]);
          CGContextBeginPath(context);
          CGContextMoveToPoint(context, 50, 50);
          CGContextAddLineToPoint(context, 50, 150);
          CGContextAddLineToPoint(context, 150, 50);
          CGContextClosePath(context);
      
          [[UIColor blueColor] setFill];
          [[UIColor blackColor] setStroke];
          CGContextDrawPath(context, kCGPathEOFillStroke);
      }
    3. 畫圓型範例:
      - (void)drawRect:(CGRect)rect
      {
          CGContextRef ctx = UIGraphicsGetCurrentContext();
          CGContextAddEllipseInRect(ctx, rect);
          CGContextSetFillColor(ctx, CGColorGetComponents([[UIColor blueColor] CGColor]));
          CGContextFillPath(ctx);
      
      }

 

Posted on

切換View會用到的函數

切換UIViewController的兩種方式

1. 有NavigationController時

方法一:右側進入

SecondViewController* svc=[[SecondViewController alloc]init];
[self.navigationController pushViewController:svc animated:YES];

返回到上一頁

[self.navigationController popViewControllerAnimated:YES];

方法二:下面切入

SecondViewController* svc=[[SecondViewController alloc]init];
[self.navigationController presentModalViewController:svc animated:YES];

返回到上一個UIViewController

[self.navigationController dismissModalViewControllerAnimated:YES];

2、没有NavigationController的切換方法

SecondViewController* svc=[[SecondViewController alloc]init];
[self presentModalViewController:svc animated:YES];

返回到上一個UIViewController

[self dismissModalViewControllerAnimated:YES];

取得子viewController或父ViewController的方式

假設View A是來源的ViewController,而View B是目標ViewController。
1.取得子viewController

((B *)self.presentedViewController).屬性名

2.取得父viewController

((A *)self.presentingViewController).屬性名

其中括號和類名是一種強制轉類型的用法。

Posted on

簡易記帳APP範例

source code在此:範例檔案下載AppPrototype

難得周日在家,就把昨天老師說的練習做完
老實說其實我弄很久(大概有八~九小時跑不掉= =)
可見真的很不熟悉,debug之類都超級慢的

首先就是在跳去圖表的地方,
原本在實作實際功能前可以正常出現圖表,後來不知為何會當掉,
光這個無聊的bug就找了兩三小時(遮臉)

後來發現是因為下面這段程式碼

-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        InputViewController * inputCon = segue.destinationViewController;
        inputCon.delegate = self;
}

因為只要是storyboard控制的頁面切換,都會呼叫prepareForSegue
我同時用storyboard去切換新增資料和圖表頁面,
所以圖表的segue.destinationViewController不是InputViewController就會跳錯誤。

在這邊也要說,老師說的inputCon.delegate等於setDelegate是很重要的
因為錯誤訊息一直說沒有setDelegate這個方法
結果我還一直傻傻看不懂是什麼意思~>”<~
(老師上課有說!妳有沒有在聽阿!)

然後第二個遇到的困難,就是我要在轉圖表畫面時,需要把現有資料傳至圖表畫面
一開始我還傻傻的在那又把CostViewController弄一個@protocol
想說要實作一個delegate機制,又是一方面要實作別人的delegate,又要是別人的protocol,
搞得我好混亂阿~

後來才發現delegate似乎是被呼叫者,要把資料傳給呼叫者時才需使用(不確定,有錯請指正)

後來為了要傳遞資料,我不去使用storyboard的切換頁面方式,改使用程式去自行切換頁面
以下為傳送列表資料的程式碼

- (IBAction)showPieChart:(id)sender {
    PieChartViewController * pieController = [[PieChartViewController alloc] init];
    [self.navigationController pushViewController:pieController animated:YES];
    [pieController showPieChart:self record:self.record];
}

【附記一下切換viewController的相關方式】

最後要做的是實作修改和刪除資料的功能
第一步是先將CostViewController實作UITableViewDelegate

@interface CostViewController : UITableViewController<inputViewControllerDelegate,UITableViewDelegate>

本來我還很害怕實作完還要設什麼delegate,或還要自己去呼叫該方法,
結果太令人開心了,只要實作了tableView,按table cell時他就會自己呼叫(灑花)

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    //修改該筆資料
    InputViewController *inputCon = [self.storyboard instantiateViewControllerWithIdentifier:@"inputCon"];
    [self.navigationController pushViewController:inputCon animated:YES];
    [inputCon modifyData:self.record[indexPath.row] forKey:indexPath.row];
    inputCon.delegate = self;
}

不過這樣子我還是遇到了一個問題,就是叫出來的畫面是一片黑。
百思不得其解也找不出原因,
後來嘗試的把原本在storyboard裡InputViewController和CostViewController的連接關係拿掉,
突然畫面就又跑出來了。

初步推論是或許一個畫面若用storyboard來切換就會無法自己寫程式碼去切換,
所以當有一個畫面有兩個按鈕都可以連接到時,就需要都自己用程式碼去做掉切換的動作。

剩的就把功能實作完就大功告成囉!

畫面介紹:一進入APP時的畫面
螢幕快照 2013-08-18 下午2.56.15
新增/修改記帳資料的畫面
螢幕快照 2013-08-18 下午2.56.32
帳目總計圖表
螢幕快照 2013-08-18 下午2.56.38

source code在此:範例檔案下載AppPrototype

Posted on

上課筆記(四) – Naming Convention、記憶體管理

Naming Convention

  1. 命名原則:
    1. 具解釋性
    2. 清楚
    3. 不會混淆
  2. 類別名稱:
    1. 第一個字大寫
    2. 前置namespace(在新增專案時會有Class Prefix,就是在設定這個)
    3. 駝峰式的寫法
  3. 變數名稱
    1. 變數類型不需特別表明,可以用名稱去讓人聯想型別,例:
      BOOL isEditable;
      NSString * accountName;
      NSMutableArray * mailBoxes;
      UIImage * previewPaneImage;
      NSDictionary * messageDict;
  4. 方法名稱:可以清楚表明意思
  5. 參數名稱:避免和ivar同名

 記憶體管理(IOS沒有gc)

  1. 配置記憶體:alloc;清除記憶體:dealloc
  2. zomble:不該用到卻用到(值為nil);memory leak:已沒辦法存取卻未把記憶體清掉
  3. 使用arc比較容易發生zomble,不用arc則容易發生memory leak。
  4. 若是有一段程式碼之中會用到大量的記憶體,則可以用@autoreleasepoll{…}包起來
    缺點:速度會慢一些
    優點:記憶體較好(在ios裡面,記憶體較重要)
  5. strong與weak的差別:變數的宣告值strong代表這個變數會增加retain的值,而weak則不會。
    weak比較像依賴的關係,當今天主要strong的變數被刪除了,weak裡的值也會變為空值。
    而strong則會增加retain的值,主要變數被刪除了,因為retain還是大於1,所以變數的記憶體並不會被釋放。(要小心Memory leak)
Posted on

上課筆記(三) – Operation、Delegation、基本類別介紹

Operation

  1. 取得class資料的相關變數
    Class me = [obj class];
    NSString * name = [obj className];
    //判斷是否繼承於UIView
    if( [ obj isKindOfClass:[UIView class] ] ){
    NSLog(@"obj繼承於UIView");
    }
    //判斷是否是這個class的實體
    if( [ obj isMemberOfClass: [NSString class] ] ){
    NSLog(@"obj的類別是NSString");
    }
  2. 確認兩個變數指向同一個記憶體位置
    if(obj1 == obj2) {}

    確認兩個實體的值相同

    if([obj1 isEqual: obj2]){//使用者可自行在class裡面新增此一函數,來決定該如何判別兩個類別相同}
  3. 呼叫物件的描述,回傳字串 [obj description];//等於NSLog(@”%@”,obj);

Delegation

  1. 定義:物件要完成某件行為時利用別人定義好的某項功能來完成。
    所以專案第一個被呼叫的應該是Delegate裡的

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  2. xcode專案的main.m
    #import ㄑUIKit/UIKit.h;
    
    #import "AppDelegate.h"
    
    int main(int argc, char *argv[])
    {
      @autoreleasepool {
           //argc:在argv裡元素的個數,通常對應main的參數
           //argv:一串輸入值
           //UIApplication或其子類別實體的class name,如果值是nil則會認為是UIApplication。
           // NSStringFromClass([AppDelegate class] 系統做完事後要通知誰
           return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }

基本類別

  1. NSObject:最基本的物件
    [obj className];//自我檢視
    [obj isEqual:obj2]//物件比較
  2. NSString
    [NSString stringWithFormat:@"It is %@",@"Tom"];//格式化字串
    //常用method
    str=[str stringByAppendingString:"abc"];//加字串在尾端,會回傳str
    str=[str stringByAppendingFormat:"name %@",@"Tom"];//加格式化字串在尾端,會回傳str
    BOOL equ = [str isEqualToString:str2];//字串相同
    //比較兩字串相同的方法
    if([str compare:str2 options:NSLiteralSearch]){}
    if([str idEqualToString:str2]){
    //字串搜尋
    NSRange range = [str rangeOfString:str2];
    NSLog(@"str2位於str1的第%d的位置,長度為%d",range.location,range.length);
  3. NSMutableString:可在原本字串之間插字串(用insertString)
    NSMultableString *str = [NSMultableString string];
    [str appendString:@"test"];
    [str insertString:@"hello" atIndex:1];
    NSLog(str);//ThrlloEST
  4. Set:無順序性的集合,資料有惟一性(物件要比較是否相同須覆寫isEqual和hash)
    NSMutableSet:queue的概念,用在List把移出畫面的東西暫存。
    NSOrderedSet:依照加入的順序排(比起NSMutableSet)
    相關網頁:
    http://stackoverflow.com/questions/6545722/help-with-isequals-and-hash-in-iphone
  5. Array:有順序性的集合,當資料從中間刪除,剩的index會自動往前補。
    NSArray: 不可新增修改的array。
    NSMutableArray:可新增修改,保證是0,1,2,3的index不會跳號。
  6. Dictionary:存在key-value型式資料的集合
    NSDictionaryNSMutableDictionary
  7. 走訪所有元素的寫法
    for(NSString *ele in array){
           NSLog([ele description]);
    }
  8. NSNumber:才可以丟進set或array裡
  9. NSNull:為了要丟進array裡面,因舊式寫法array宣告nil代表尾端,所以要丟空值要改丟NSNull
  10. Scalar:把物件當陣列般存取。例:
    Car * car = [car new];
    
    car[1] = @"name"; //寫入scalar
    NSLog(@"%@",car[1]);//讀取scalar
    
    car[@"dict"] = @"value";//寫入object
    NSLog(@"%@",car[@"dict"]);//讀取object

    要注意的是,如果我們呼叫car[1]代表執行下面兩個函數而不代表真的是array

    - (id) objectAtIndexedSubScript:(NSInteger) index {
      return @"Car";
    }
    -(void) setObject:(id)thing atIndexedSubscript:(NSInteger) index{
       //在此設定index為index的值為thing
    }

    如果果們呼叫car[@”dict”]則是執行下面兩個函數

    -(id) objectForKeyedSubscript:(id)key{
        return @"dict";
    }
    -(void) setObject:(id) thing forKeyedSubscript:(id<nscopying>) key{
         //在此做設定
    }
Posted on

Flash時間軸運作相關注意點

遮罩相關:

當圖層在遮罩之下時,影格每次前進,所有的物件都會重新運算並重新產生

  1. 若有一個動態label在遮罩之下,若其原本的顯示的文字為”舊的值“,在影格1時,用script將其值設為”新的值“,當影格前進到下一個影格時,又會變回顯示”舊的值“(沒有關鍵影格也是)。
  2. 同理,如果我們在影格1時,在as裡指定某個MovieClip,當進入下一個影格時,原本在as指定的MovieClip會與當下畫面上的物件不同,而造成無法對其做操作的狀況,這時候應該要到某定點影格時,再在as裡對物件做指定的動作

關鍵影格相關:

每次重新經過一個關鍵影格,其物件都會重新產生。

  1. 若在影格1裡,有元件a b c,然後在影格2裡,有元件d e。當我們用as,在影格1裡指定a b c元件至某個變數裡,當時間軸跑到影格2,再跑回影格1。原本指定的變數裡面所存的影格,會是上一次影格1裡的元件,但會與現在在畫面上影格1顯示的元件不同
  2. 若某MovieClip的第1影格第5影格都是關鍵影格,在關鍵影格上的都有同樣的元件a b c。若我們在影格1時用AS指定元件abc,當影格跑到影格5時,畫面上所顯示的元件也會與影格1的元件不同(若沒關鍵影格的狀況下則會是相同的)。

效能相關:

  1. 形狀或顏色漸變,是每進入一次影格,便會整個圖層做重繪。因此他的漸變對象不能為元件,而需要是向量圖形。(耗效能)
  2. 傳統移動漸變,則是單純對元件的位置或大小做改變,元件本身不會重繪。因此對象一定要是元件。
  3. 重繪效能會與重繪區域大小有關,最耗效能的為向量漸層或色塊,因此若無必要,盡可能將元件改使用bitmap點陣形式。
  4. flv動畫在初始載入時會較花效能,但若是畫面上同時有多個動畫,則flv會較省效能。
Posted on

利用ant 構建和部署專案

Ant 可以代替使用 javac 、 java 和 jar 等命令來執行 java 操作,從而達到輕松的構建和部署 JAVA 項目的目的。

1. 利用ant 的javac命令來編譯JAVA程式

Ant 的javac命令用於實現編譯JAVA 程式的功能。下面來看一個簡單的例子:首先我們建立名為 JAVATestPro的JAVA項目,建立src目錄為源代碼目錄,在src目錄下建立HelloWorld.java這個類檔。該類檔的內容如下:

public class HelloWorld {
    public static void main(String[] args) {
       System.out.println("hello world!");
    }
}

同時在JAVATestPro項目的根目錄下建立build.xml 檔,在該檔中編譯sr 目錄下的JAVA檔,並將編譯後的class檔放入build/classes 目錄中,整個項目的目錄結構如下:

|JAVATestPro
|src
|build
|classes
|build.xml

在編譯前,需清除classes 目錄,該檔的內容如下:

<?xml version="1.0" ?>

<project name ="javacTest" default="compile" basedir=".">
<target name="clean">
<delete dir="${basedir}/build"/>
</target>
<target name="compile" depends ="clean">
<mkdir dir ="${basedir}/build/classes"/>
<javac srcdir ="${basedir}/src" destdir ="${basedir}/build/classes"/>
</target>
</project>

在項目根目錄(C:\ThinkInJAVACode\JAVATestPro)執行ant命令後,可在該目錄下發現新生成的build/classes子目錄,編譯後生成的HelloWorld.class檔就在該目錄下。

2. 使用java命令執行JAVA程式

Ant 中可以使用 java命令實現運行JAVA程式的功能。可以在上面的build.xml基礎上做修改來實現:

<?xml version="1.0" ?>
<project name ="javacTest" default="run" basedir=".">
<target name="clean">
<delete dir="${basedir}/build"/>
</target>
<target name="compile" depends ="clean">
<mkdir dir ="${basedir}/build/classes"/>
<javac srcdir ="${basedir}/src" destdir ="${basedir}/build/classes"/>
</target>
<target name="run" depends ="compile">
<java classname ="HelloWorld">
<classpath>
<pathelement path="${basedir}/build/classes"/>
</classpath>
</java>
</target>
</project>

接著,就可以在主控台看見輸出:” hello world!”

3. 使用jar命令生成jar檔

還可以在上例的基礎上更進一步,來生成jar包,可在run 這個 target 下再加上如下 target :

<?xml version="1.0" ?>
<project name ="javacTest" default="jar" basedir=".">
<target name="clean">
<delete dir="${basedir}/build"/>
</target>
<target name="compile" depends ="clean">
<mkdir dir ="${basedir}/build/classes"/>
<javac srcdir ="${basedir}/src" destdir ="${basedir}/build/classes"/>
</target>
<target name="run" depends="compile">
<java classname ="HelloWorld">
<classpath>
<pathelement path="${basedir}/build/classes"/>
</classpath>
</java>
</target>
<target name="jar" depends="run">
<jar destfile="helloworld.jar" basedir="${basedir}/build/classes">
<manifest>
<attribute name="Main-class" value="HelloWorld"/>
</manifest>
</jar>
</target >
</project>

其中,project的default 屬性設置為應設為jar,ant運行完畢後,可看到在項目的根目錄下生成了一個 helloworld.jar的jar包 。可通過運行以下命令來執行該jar包:

java -jar helloworld.jar

4. 使用war命令打包JAVAEE項目

建立一個JAVAEE項目,其中src 為JAVA源代碼目錄,WebContent為各jsp存放目錄,lib 為項目引用的的包的目錄。在WebTest項目目錄下建立了build.xml 檔,該檔為該工程的 Ant 構件檔。

|WebContent
|src
|build
|classes
|WebContent
|META-INF
|MANIFEST.MF
|WEB-INF
|lib
|classes
|HelloJSP.jsp
|build.xml

讀者可以 src 目錄下放入在前續例子中開發的 HelloWorld.java 檔,並在 WebContent下建立 HelloJSP.jsp 檔,其內容很簡單,就是輸出 Hello 資訊,代碼如下所示:

<%@ page language="java" contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "HTTP://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta HTTP-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>war test for ant</title>
</head>
<body>
Hello JSP!Hello Ant!
</body>
</html>

接下來編寫 build.xml 檔,其內容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<project name ="WebTest" default ="war" basedir =".">
<property name ="classes" value ="${basedir}/build/classes"/>
<property name ="build" value ="${basedir}/build"/>
<property name ="lib" value ="${basedir}/WebContent/WEB-INF/lib"/>
<!-- 刪除build 路徑-->
<target name ="clean">
<delete dir ="${build}"/>
</target>

<!-- 建立build/classes 路徑,並編譯class 檔到build/classes 路徑下-->
<target name ="compile" depends ="clean">
<mkdir dir ="${classes}"/>
<javac srcdir ="${basedir}/src" destdir ="${classes}"/>
</target>

<!-- 打war 包-->
<target name ="war" depends ="compile">
<war destfile ="${build}/WebTest.war" webxml ="${basedir}/WebContent/WEB-INF/web.xml">
<!-- 拷貝WebRoot 下除了WEB-INF 和META-INF 的兩個檔夾-->
<fileset dir ="${basedir}/WebContent" includes ="**/*.jsp"/>
<!-- 拷貝lib 目錄下的jar 包-->
<lib dir ="${lib}"/>
<!-- 拷貝build/classes 下的class 檔-->
<classes dir ="${classes}"/>
</war>
</target>
</project>

在C:\ThinkInJAVACode\WebTest目錄下運行ant後,就生成了WebTest.war 檔了,然後可以將其放入Web容器(如Tomcat)的相應目錄下(${Tomcata安裝目錄}\webapps)運行該web項目了。

Posted on

ANT常用命令

1. copy 命令

copy主要用來對檔和目錄的複製功能。舉例如下:

eg1. 複製單個檔:

<copy file="original.txt" tofile="copied.txt"/>

eg2. 對檔目錄進行複製:

<copy todir="../dest_dir">
<fileset dir="src_dir"/>
</copy>

eg3. 將檔案複製到另外的目錄:

<copy file="source.txt" todir="../home/philander"/>

2. delete 命令

對檔或目錄進行刪除,舉例如下:

eg1. 刪除某個檔:

<delete file="/home/photos/philander.jpg"/>

eg2. 刪除某個目錄:

<delete dir="/home/photos"/>

eg3. 刪除所有的備份目錄或空目錄:

<delete includeEmptyDirs="true">
<fileset dir="." includes="**/*.bak"/>
</delete>

3. mkdir 命令

創建目錄。 eg :

<mkdir dir="/home/philander/build/classes"/>

4. move 命令

移動檔或目錄,舉例如下:

eg1. 移動單個檔:

<move file="sourcefile" tofile="destfile"/>

eg2. 移動單個檔到另一個目錄:

<move file="sourcefile" todir="movedir"/>

eg3. 移動某個目錄到另一個目錄:

<move todir="newdir"> <fileset dir="olddir"/></move>

5. echo 命令

該任務的作用是根據日誌或監控器的級別輸出資訊。它包括 message 、 file 、 append 和 level 四個屬性,舉例如下

<echo message="Hello,ANT" file="/home/philander/logs/ant.log" append="true">