2010年12月24日 星期五

網頁與AIR程式互動

如果需要在Browser上可以與AIR應用程式做互動,如安裝AIR App,或由網頁上的Click觸發啟動AIR App,以下是相關執行程序

一.準備工作
1.AIR程式端設定AIR程式允許透過Browser觸發
設定AIR發布用的application.xml的
 <application xmlns="http://ns.adobe.com/air/application/2.0">  
建議設定發布為AIR 1.53以上版本,因為如果AIR為1.53之前的版本,Browser要呼叫AIR,一定要知道AIR的PID值(在AIR內可以用NativeApplication.nativeApplication.publisherID取得)。若AIR為1.53後的版本,若Browser不知道AIR的PID,可以用""代即可。

加入
 <allowBrowserInvocation>true</allowBrowserInvocation>  
讓AIR允許被Browser啟動

2.Browser端Flash取得AIR Browser API
Browser端Flash要操作AIR所要用的API都放在http://airdownload.adobe.com/air/browserapi/air.swf內
透過loader載入這個檔案即可使用相關功能

public var airSWF:Object;
public var airSWFLoader:Loader = new Loader();
public var loaderContext:LoaderContext = new LoaderContext();
loaderContext.applicationDomain = ApplicationDomain.currentDomain; 
airSWFLoader.contentLoaderInfo.addEventListener(Event.INIT, onInit); 
airSWFLoader.load(new URLRequest(" http://airdownload.adobe.com/air/browserapi/air.swf"), loaderContext);
private function onInit(e:Event):void { 
    airSWF = e.target.content;
    airSWF.getApplicationVersion(appID, pubID, versionDetectCallback);
}        

二.Browser檢查Client是否已經安裝AIR執行環境
var result:String=airSWF.getStatus();
取得回傳值
"available":可安裝但尚未安裝。
"unavailable":無法安裝
"installed":已安裝

三.Browser檢查Client端是否已經安裝AIR 應用程式
利用檢查AIR應用程式版本來確認程式是否已經安裝,執行後,若檢查完成
會呼叫執行所設定的versionDetectCallback函式,並在這callBack函式內
取得版本值,當版本值為null代表未安裝程式
airSWF.getApplicationVersion(appID, pubID, versionDetectCallback);
private function versionDetectCallback(version:String):void { }    

設定參數:
appID:應用程式ID,即AIR應用程式的application.xml內的id值,或在air內透過
    NativeApplication.nativeApplication.applicationID取得
pubID:發佈者ID,在air內透過
    NativeApplication.nativeApplication.publisherID取得

注意:要讓這功能有效可以使用,在使用air.swf一定要是載入http://airdownload.adobe.com/air/browserapi/air.swf
若將這air.swf複製到local端呼叫使用,則取得版本永遠會是null值

四.Browser安裝AIR App
airSWF.installApplication(installPath, runtimeVersion, arguments); 
設定參數:
installPath:String,要安裝的air檔案路徑
runtimeVersion:String,允許執行的AIR Runtime最低版本,例如"1.5"
arguments:Array,非必填項目,要傳遞給AIR的參數,這參數會被AIR內的
BrowserInvokeEvent.BROWSER_INVOKE事件所接收

五.Browser 啟動已安裝AIR APP
airSWF.launchApplication(appID, pubID, myArguments);

六.Browser傳遞參數給AIR App
airSWF.launchApplication(appID, pubID, myArguments);
只要將傳遞的參數放到myArguments陣列內即可

2010年12月9日 星期四

List物件客製CellRenderer與設定背景顏色

在使用List或DataGrid物件,如果需要自行設計CellRenderer,例如加入圖片,底圖或使用多文字框,以下是自訂CellRender操作流程

一.自訂 CellRenderer Class
自訂CellRenderer必須繼承Sprite或MovieClip且實作ICellRenderer介面
因此一個自訂的CellRenderer Class基本上一定具備以下結構

public class ItemRenderer extends Sprite implements ICellRenderer{
    private var _listData:ListData; 
    private var _data:Object; 
    private var _selected:Boolean; 
    public function ItemRenderer() {}
    public function set data(d:Object):void { 
    }
    public function get data():Object { 
        return _data; 
    } 
    public function set listData(ld:ListData):void { 
        trace("ListData=" + ld.label);
        _listData = ld; 
    } 
    public function get listData():ListData { 
        return _listData; 
    } 
    public function set selected(s:Boolean):void { 
        _selected = s;             
    } 
    public function get selected():Boolean { 
        return _selected; 
    } 
    public function setSize(width:Number, height:Number):void { } 
    public function setStyle(style:String, value:Object):void { } 
    public function setMouseState(state:String):void{ } 

}
雖然自訂的CellRenderer可以直接繼承自系統的CellRenderer,但最好還是實作ICellRenderer介面,可以有比較大的彈性應用

二.在Flash IDE上產生Instance
在Flash IDE上,依照需要設計產生CellRender所要對應的Symbol
並在Library內設定該Symble的Linkage
Class:完整的CellRenderer Class名稱
base Class:flash.display.MovieClip

三.List利用setStyle指定cellRenderer Class
當已經設計好CellRenderer Class並在fla上產生好Symbol
執行
list.setStyle("cellRenderer", ItemRendererClass);
(ItemRendererClass是自訂的Renderer Class)
即可讓list使用你自訂的CellRenderer

四.List加入資料
將要提供給cellRenderer用的資料以物件芳式包裝好,再以addItem()加入,這樣CellRenderer內的 set data(obj:Object)即會收到所傳入的物件

var obj=new Object();
obj.id=xxx;
obj.thumb=xxx;...
list.addItem(obj)

五.CellRenderer內各Method說明

1.data
當list以addItem將資料加入後,會自動執行set data()

在list上
list.addItem(_itemObj);    
在renderer上會觸發
public function set data(d:Object):void { 
    //--d即是addItem所帶的_itemObj
    //--利用d帶進來的資料,顯示在你要顯示的文字框或圖片--
}        

2.listdata
當list.addItem(_itemObj);也同時會觸發renderer內的這個listData method
set listData(ld:ListData)內的ld為一個ListData物件,包含了這個renderer在List內的相關資訊
例如ld.index即是這個render在list內所在位置的index值

3.selected
當render的選取狀態有變化時會觸發這個method,也就是說當使用者在list上click item
如果這個item被選取時,selected(s:Boolean)
s會得到true,若在非選取狀態,則為false
在這裡可利用s值,改變render顯示的樣式,顯示物件選取狀態

4.setSize
只要是在list上做關於size改變的設定,例如
list.setSize()或設定list.rowHeight,都會把在list上設定的大小值
傳給render的setSize(),你可以在這method內依照傳進來的值
調整render內物件的大小
5.setStyle
可用來設定CellRender的顯示樣式,若不想做任何處理,保持空的即可
但一定要保留這method在Class內

6.setMouseState
可用來設定CellRender的滑鼠狀態,若不想做任何處理,保持空的即可
但一定要保留這method在Class內

六.設定List的背景色
List本身並無backgroundColor的選項可供改變List的底色,使用serStyle("backgroundColor")也是無效,若要改變底色需要設定的是"skin"的style,並傳入一個你要當作背景的物件當作傳入值

var bg:Sprite = new Sprite();
bg.graphics.beginFill(0xff0000,0.5);
bg.graphics.drawRect(0, 0, 10, 10);
bg.graphics.endFill();
list.setStyle("skin", bg);

註:skin style預設是List_Skin這Class

2010年11月2日 星期二

Android Service-幾種應用範例(二)

一.實作要點

1.說明
Service提供兩種的使用方式,如果Client只是透過startService去執行Service的功能,只是把要做的功能寫在onStartCommand內即可,若是Client透過bindService來執行Service,就需要在Service內產生Ibinder物件,讓onBind可以把binder物件傳給Client,讓Client可以由取到的binder物件再取到Service物件,然後就可以呼叫Service上提供的method

2.Activity(使用Service的Client)
1.extends Activity物件
2.實做ServiceConnection(需要bindService時使用)
  實做並產生一個connection instance,在connection內
  override其onServiceConnected(ComponentName className, IBinder service)
  與onServiceDisconnected(ComponentName className)

3.onServiceConnected被自動呼叫(需要bindService時使用)
  在與Service連線成功後,onServiceConnected會被自動呼
  叫並接收到由Service內部onBinder所發送出來的IBinder物件,
  通常會是extends 自Binder
4.取得Service物件(需要bindService時使用)
  將取得的Binder物件呼叫其Class內定義的方法(如getService)
  來取得Service物件,如Service(binder.getService())
5.利用取得的Service呼叫執行中Service上的public method

3.Service(提供服務的元件)
1.extends Service物件
2.設計binder class extends Binder(需要bindService時使用)
  設計一個內部Binder Clas並產生binder instance,在class內
  建立getService function,可以透過這binder取得所在的service
3.overrider onBinder function(需要bindService時使用)
  將已建立的binder instance傳出去
4.override onStartCommand(需要用startService時使用)
  若只用startService啟動Service,只需要override這
  function即可

二.Sample:LocalService 使用startService

1.說明
透過startService呼叫Service會執行Service內部的onStartCommand(),所以把要執行的工作放在override的onStartCommand()內

2.Activity
Intent intent = new Intent(this, MusicService.class);
intent.putExtra("songPath","sdcard/mp3/mySong.mp3");
this.startService(intent);
3.Service
@Override
public void onCreate() {    
    super.onCreate();
    mp=new MediaPlayer();    
}
@Override
public void onStart(Intent intent, int startId) {
    super.onStart(intent, startId);
    String songPath=intent.getExtras().getString("songPath");
    try{
        mp.setDataSource(_path);
        mp.prepare();
        mp.start();
    }catch(Exception e){};
}

三.Sample:LocalService 使用bindService

1.說明
透過bind方式要執行Service上的功能,是透過建立連線物件後,再直接對Service做操作,要做的工作就寫成Service內public method 讓人呼叫執行

2.Activity
private MusicService mService;
@Override
public void onCreate(Bundle _state){
    doBindService()
}
private void doBindService(){
    Intent intent = new Intent(MyActivity.this, MusicService.class);
    bindService(intent, connc,Context.BIND_AUTO_CREATE);
}
private ServiceConnection connc=new ServiceConnection() {            
    @Override
    public void onServiceDisconnected(ComponentName name) {    
    }
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {        
        mService=((MusicService.LocalBinder)service).getService();        
    }
};

要使用service上的function就可以直接用,如 mService.start(),mService.stop()...等定義在Service內public的方法。若要取得回傳值,只要service內的function有回傳值,直接接收就可以了,例如
String _path=mService.getSongPath();

3.Service
內定義一個內部Binder class可以用來取得service
public class LocalBinder extends Binder {
     MusicService getService() {
        return  MusicService.this;
    }
}
private final IBinder mBinder = new LocalBinder();
@Override
public IBinder onBind(Intent intent) {    
    return mBinder;
}

2010年11月1日 星期一

Android Service-概念(一)

一.說明
Service本身是個無介面,只單純提供功能讓其他元件(Activity)呼叫使用的元件,如果有需要長時間執行的獨立程序,可以寫成Service元件提供其他人呼叫使用。

Service本身並非一個獨立的Process,也不會脫離Main Thread獨立運作,如果需要做長時間的CPU運算,為了不影響到主程式運作,可以在Service內產生Thread,讓thread幫你作一些工作,Service的子類別IntentService即是一個有自己Thread 的Service,服務給並無直接與使用者互動的介面。
當Service元件被產生時,一定會執行其onCreate(),如果有需要產生Thread,則在這裡去產生新的thread

當Service有設定在Manifest上,就可以被自己與其他的App來做存取,其他App要使用service,需在他自己App的Manifest內的uses-permission作宣告

二.執行Service功能的兩種方式
1.startService
當有元件呼叫Context.startService(),系統會讀取被呼叫的Service(產生instance並執行其onCreate()),然後將client所傳進來的intent帶進Service的onStartCommand(Intent, int, int)內。這service會一直執行,直到有人呼叫Context.stopService()或內部呼叫stopSelf()而停止。

無論Context.startService()被呼叫了多少次,如果要停止Service,只要執行一次Context.stopService(),或在Service內執行stopSelf(),就會被停止。
如果是在Service內使用stopSelf(int),可以保證intent已經有被處理了,才會被停止。

對於已被啟動的Service,如果需要有不同的Service執行模式,可以在override onStartCommand()時,設定不同的回傳值。這回傳值常數已經被定義好在Service Class內
1.START_STICKY:
  當Service在執行時被砍掉後,若沒有新的intent進來,
  Service會停留在started state,但intent資料不會被保留
2.START_NOT_STICKY或START_REDELIVER_INTENT:
  當Service在執行時被砍掉後,若沒有新的intent進來,
  service會離開started  state,若沒有很明確的再啟動,
  將不會產生新的service物件

2.bindService
Client可以利用Context.bindService() 去建立與service持續性的連線,
如果建立連線時,還沒產生Service instance,會自動產生並呼叫執行onCreate。與startService不同的地方是,接下來並不會呼叫onStartCommand

要執行bindService,首先要先建立一個serviceConnection物件,把這conection物件當作參數放到bindService內讓Context與Service建立連線,Context.bindService(Intent service, ServiceConnection conn, int flags)
flags參數預設是Context.BIND_AUTO_CREATE,也就是bind的時候會自動產生service

當連線成功後,會自動呼叫執行這個connection內的onServiceConnected(ComponentName className, IBinder service) function,在這function會接收到由service內的onBinde()所丟出來的Ibinder物件
利用這IBinder物件取得Service物件,就可以直接操作Service內各個public 的method

三.Service Process重要性判斷
系統會盡量保持Service的Process運作,但若需要刪除Process,以下是重要性判斷準則
1.正在執行onCreate(), onStartCommand()或onDestroy()的
  Service會被當成前景執行程序,不會被刪除
2.假若這ervice已經被啟動(started),那這service所在的
  process的重要性,會是在visible process與hide process之間
3.如果service所在的Process上有元件是visible,那這Service
  的重要性就相當於是visible
4.一個已經started的service可以利用
  startForeground(int,Notification)放在比較重要的
  前景執行狀態,在記憶體少的時候比較不會被刪除

當記憶體不足Service被刪除後,它稍後還是會被重新啟動產生出來,但通常重新啟動後,原本的intent並不會被保留,如果需要在重新啟動後,再重發intent給Service,必須在override onStartCommand()時return START_REDELIVER_INTENT讓重新啟動的程序裡的onStartCommand(Intent intent, int flags, int startId)內的flags值=START_FLAG_REDELIVERY