Android Timer

在iOS中timer執行時就是直接new一個Thread來執行,但在Android中似乎有別的方式可以選擇

宣告Timer出來,並給予啟動時間,如下面例子timer.schedule(線程名稱, activity停頓幾秒後開始執行, 每隔幾秒執行一次),這部份直接放在onCreate內
Timer timer = new Timer(true);
timer.schedule( timerTask, 2000, 3000); //(taskName, wait(ms), repet(ms))
//timer.cancel(); //stop timer


timer.schedule裡面也一樣有相當多的method,如上面所寫的那是不斷去執行直到你自己去關閉,那如果只要執行一次就使用
Timer timer = new Timer(true);
timer.schedule( timerTask, 2000); //(線程名稱, 開始後多久執行(ms))
//timer.cancel(); //stop timer


但這裡面可以用的東西也是太多了,我這只有2k的大腦記不住這麼多,補全給他按下去就對了,慢慢挑慢慢選


開始執行Timer的線程,其實也可以直接寫在todo something這邊,但是在線程中作這些實在有點不保險,常常會有意想不到的收穫,所以在這還是會推薦在handler內來作。這部份直接放在activity內
/* timer task */
TimerTask timerTask = new TimerTask(){
public void run() {
//todo something
handler.sendEmptyMessage(10);
}
};


由於用上了handler就必須在activity內再加上要執行的部份,如上面的task內所傳送一個數字過去(就是"10")所以在下方handler內的msg.what所印出來的就是"10",只要寫下"if(msg.what == 10)"要作啥事情即可,以此類推就可以有更多的變化
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//TODO something
System.out.println("msg = "+msg.what);
if(msg.what == 10){
//
}
}
};


相關連結
Android Developers
Android Developers - Timer
Android Developers - TimerTask
Android Developers - Handler

Android get Device infomation 取得手機裝置資訊

透過android系統可以很方便的取得Device的相關資訊,能夠取得的資訊真的是相當的完整,就如下面這範例想要取得手機的電信業者名稱(如中華電信這類的,但大多是代號),這些資訊作族群分析等這些資訊就變的相當重要,一般使用者可以透過"Settings(設定)>About phone(關於手機)"裡面可以查到手機資訊,不過這只能夠得知自己的資訊,如果今天寫app想要作族群用戶分析等,在android這系統上也可以很輕鬆的取得這些資訊。

下面這carrierName就是電信業者的名稱
String carrierName = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE)).getNetworkOperatorName();
System.out.println("carrierName = "+carrierName);


透過"TelephonyManager"還可以取得更多的資訊(如下圖),補全給他按下去慢慢選就好,真的都要記下來太累了



取得裝置名稱
android.os.Build.DEVICE

在"android.os.Build"這裡面還有很多參數,以我這腦容量超小的狀況下還是使用補全慢慢挑慢慢選就好


或者可以從"android.os"這裡面也有很多資訊可以取得

可以再搭配前幾篇的"Android get sdk version & app version 取得版本資訊"、"Android 取得畫面解析度"來取得所需的資訊,站在廠商的角度真的要感謝google,感謝facebook,讓我們可以取得更完整的用戶資訊來作分析改善,以便找出淺在用戶。

相關連結
Android Developers
Android Developers - TelephonyManager
Android Developers - Build
Android Developers - android.os

Android get sdk version & app version 取得版本資訊

UI設定在某些狀況下會產生位置錯亂,並不是因為解析度問題,真的是因為版本對於該參數所定義上有修改。

一般來說直接判斷sdk version應該是比較方便,app version可以用在顯示目前app版本的地方,讓使用者知道當前版本。
//sdk verson
String sdkName = Build.VERSION.RELEASE;
String sdkVerson = Build.VERSION.SDK;
System.out.println("sdkName = "+sdkName+" || sdkVersion = "+sdkVerson);
//sdkName = 2.1-update_1 || sdkVersion = 7


//app verson
try {
String versionName = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
int versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
System.out.println("name = "+versionName+" || code = "+versionCode);
} catch (NameNotFoundException e) {
//Handle exception
}


相關連結
Android developers
Android developers - Build.VERSION

Android 取得畫面解析度

取得畫面解析後才方便修改UI的比例來作調整,修改UI大小的方式在前一篇"Android setWidth setHeight 設定UI寬高"當中有提到,就可以配合著取得畫面解析度來作調整。

screenWidth就是螢幕的寬,screenHeight就是螢幕的高,所call的getScreenScaleType就是取得畫面比例
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();
String scapeType = getScreenScaleType(screenWidth, screenHeight);
System.out.println("scapeType = "+scapeType);

//getScreenScaleType
public String getScreenScaleType(int width, int height){
String scaleType = "unknow";
float scale = (float) 0.0;
boolean landspace = false;

if(width > height){
landspace = true;
}else{
landspace = false;
}

if(landspace == true){
scale = (float)width/(float)height;
}else{
scale = (float)height/(float)width;
}
// System.out.println("width = "+width+" || height = "+height+" || scaleType = "+scaleType);

if(scale >= 1.76 && scale <= 1.8){ //WVGA800 or WQVGA432 if(landspace == true && width == 432){ scaleType = "WQVGA432landspace"; }else if(landspace == true && width == 854){ scaleType = "WQVGA854landspace"; }else if(landspace == false && height == 432){ scaleType = "WQVGA432"; }else if(landspace == false && height == 854){ scaleType = "WQVGA854"; }else{ scaleType = "5:8"; } }else if(scale >= 1.66 && scale <= 1.78){ //WVGA800 or QWVGA400 if(landspace == true && width == 400){ scaleType = "QWVGA400landspace"; }else if(landspace == true && width == 800){ scaleType = "WVGA800landspace"; }else if(landspace == false && height == 400){ scaleType = "QWVGA400"; }else if(landspace == false && height == 400){ scaleType = "WVGA800"; }else{ scaleType = "3:5"; } }else if(scale == 1.5){ //HVGA if(landspace == true && width == 480){ scaleType = "HVGAlandspace"; }else if(landspace == false && height == 480){ scaleType = "HVGA"; }else{ scaleType = "2:3"; } }else if(scale >= 1.3 && scale <= 1.4){ //QVGA if(landspace == true && width == 320){ scaleType = "QVGAlandspace"; }else if(landspace == false && height == 480){ scaleType = "QVGA"; }else{ scaleType = "3:4"; } } System.out.println("width = "+width+" || height = "+height+" || scaleType = "+scaleType); return scaleType; }


不過在解析度這部份還是有個地方不太了解,由於我也沒有手機可以來作實測沒辦法了解到詳細狀況,目前在emulator裡面看到的狀況如下
HVGA =>320*480(width*height) || 實測取得為 320*480(width*height)
QVGA =>240*320(width*height) || 實測取得為 320*427(width*height)
WQVGA400 =>240*400(width*height) || 實測取得為 320*533(width*height)
WQVGA432 =>240*432(width*height) || 實測取得為 320*480(width*height)
WVGA800 =>480*800(width*height) || 實測取得為 320*533(width*height)
WVGA854 =>480*854(width*height) || 實測取得為 320*569(width*height)

紅色的部份為實測時發現不同的部份,不過目前也沒有這麼多的device來確認究竟是哪邊的問題,好消息是如果真的都與實測相同那解析度在寬的部份都是相同的(皆為320),歸類起來高也只有4種(427, 480, 533, 569),在device上面目前只有看到320*427(HTC Wildfire)、320*480(HTC hero)、320*533(HTC Desire)是比較確定的,其他的...除非有人願意借我測看看吧。

相關連結
Android Developers
Android Developers - Activity
Android setWidth setHeight 設定UI寬高

Android setWidth setHeight 設定UI寬高

Android的裝置上解析度真的太多了,尤其在2.1以前跟2.2以後的margin定義方式也不同,所以在runtime狀況下能動態的改變layout是個最好的方式,但是在weight裡面有個屬性為"setWidth"、"setHeight"實際上設定下去weight卻不一定會跟著改變,只要有個地方發生問題就會整個掛掉,小則畫面變黑一下子,重責直接重開機。可以配合另一篇"Android 取得畫面解析度"來作更完善的調整。

現在就以一個按鈕為例,在xml中設定寬高方式如下
其中"wrap_content"就是照你按鈕內容改變大小,"fill_parent"就是直接佈滿,當然也可以直接以"100px"(100像素)這種方式直接給予固定的大小
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content">



在runtime中要給予大小,就以上面那button為例,在Activity中的onCreate內加上下面這段
Button myButton = (Button)findViewById(R.id.button);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(100, 100);
myButton.setLayoutParams(params);


直接給予他一個layout的方式來修改就可以順利改變,或者可以採用下面這種方式(兩種效果相同)
Button myButton = (Button)findViewById(R.id.button);
myButton.getLayoutParams().width=150;
myButton.getLayoutParams().height=150;



相關連結
Android Developers
Android Developers - LinearLayout.LayoutParams
Android 取得畫面解析度

Android check internet connection 檢查網路連線狀態

之前有一篇是寫到iphone檢查網路連線狀態,現在很多的app都會需要用上網路,所以檢查是否能夠連線就變成相當重要的部份。

首先需要在"AndroidManifest.xml"這個建立專案時就產生出來的檔案內加上下面這兩行,第一行為讓app可以順利的使用網路存取資料,第二行為開放檢查網路狀態
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


詳細位置可以參考下圖


接著在class內加上這function
//check internet connetion
public boolean checkInternetConnection(){
ConnectivityManager cm=(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni=cm.getActiveNetworkInfo();
if(ni!=null && ni.isConnected()){
// System.out.println("ni.isConnected() = "+ni.isConnected());
return ni.isConnected();
}else{
// System.out.println("ni.isConnected() = "+ni.isConnected());
return false;
}
}


可以參考下圖,就把這function放在Activity內即可


最後需要檢查網路狀態就使用下列方式即可,回應為true就是有連線,false為沒連線
if(checkInternetConnection()){
//TODO something
}


可參考圖片



相關連結
Android Developers
Android Developers - NetworkInfo

征服烏來 - 單車公路賽

上次在烏來偶然遇到這活動的承辦單位的人介紹了這活動,雖然說上週末開始中感冒到現在都沒完全好,但是之前說好要來參加還是乖乖的來,以前大多是騎到烏來老街吃吃喝喝曾經有走過一次環山路,不過當時不知道他可以接回台9甲(下山的路),所以就原路折返回去,今天才知道原來另一條路(往福山)這麼漂亮。

環山路上的瀑布公園(上次所走的路線)


今天早上真的好冷,整個腳冷到都沒啥力氣,說實在的在騎完後才開始有暖身完的感覺,不過今天主角是溫泉,所以騎多遠不重要!反正是開車來,泡完溫泉也不怕又滿身汗。

看到這種景色真的讓人想到南投


149末段


騎完後看到溫泉券有點傻眼,當下嚴重眼殘的只看到日月光溫泉,用iphone查了一下發現距離我當時所在位置還有5公里,有點無言的就找了附近的溫泉店去泡,不過也只花了100元


整體來說還是很感謝告訴我這活動的這小姐,讓我知道騎到烏來不要老是吃吃喝喝,然後買一堆小米酒回家,後面還有這樣的美景!

相關連結
「征服烏來」單車公路賽 - 官網
征服烏來單車公路賽相簿

Android Dialog in thread 線程中放Dialog

Dialog放在線程(thread)中會直接掛掉,造成程式沒辦法順利運行,這部份就沒有特別去研究過到底是啥原因,反正有解決方案就好了。線程這東西在這些行動裝置當中我真的覺得超重要,能夠好好的掌握就可以讓程式跑起來更加順暢,中間不會有任何的停頓造成使用上得感覺不好,UI這種東西按下去沒有給個回饋是很恐怖的,如果使用者發現沒有反應連續多點幾下後果可不刊設想。

一般使用線程(Thread)只要將需要用線程處理的地方放在todo something這邊就可以了
new Thread() {
@Override
public void run() {
//TODO something
}
}.start();


使用Thread必須把Dialog放在handler中也就是下面這code的todo something內,印出來的"msg.what"就是所傳送過來的參數,可以利用這參數作變化。
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//TODO something
System.out.println("msg = "+msg.what);

}
};


若為UI相關必須使用以下方式

runOnUiThread(new Runnable(){
public void run() {
//TODO something
}});


呼叫handler
//傳送"0"過去 msg.what這變數就會印出"0"
handler.sendEmptyMessage(0);


就可以利用這方是來呼叫Dialog出來用,至於Dialog的使用方法可以參考"Android Dialog"

相關連結
Android Developers
Android Developers - Dialog
Android Developers - Thread
Android Developers - Handler
Loading... - Android Dialog

iOS - PNG Animation 圖片動畫

用圖片串成動畫有點類似翻頁書方式

可直接在viewDidLoad當中加入
//new 一個ImageView來播放動畫,大小與ViewController相同
UIImageView *animationImageView = [[UIImageView alloc] initWithFrame:self.view.frame];

//把所有的圖片依照順序都丟進這陣列中
animationImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:@"CM1.png"],
[UIImage imageNamed:@"CM2.png"],
[UIImage imageNamed:@"CM3.png"],
[UIImage imageNamed:@"CM4.png"],
[UIImage imageNamed:@"CM5.png"],
[UIImage imageNamed:@"CM6.png"],
[UIImage imageNamed:@"CM7.png"],
[UIImage imageNamed:@"CM8.png"],
[UIImage imageNamed:@"CM9.png"], nil];

//動畫全部的播放時間 單位:秒
animationImageView.animationDuration = 9.42;
//總共要播放幾次,0 = 播放無限循環
animationImageView.animationRepeatCount = 2;
//開始播放動畫
[animationImageView startAnimating];
//把ImageView丟進ViewController的View中
[self.view addSubview:animationImageView];
//由於addSubView會自動將retainCoint加1,所以釋放一個ImageView
[animationImageView release];


相關連結
iOS Developer Library
iOS Developer Library - UIImageView Class Reference

iOS screen capture & UIImagePickerController 快速擷取螢幕&取得相片圖檔

一般來說在iOS系列產品只要同時按下Home+Power即可把螢幕快速擷取,擷取下來的圖片就會存在"相片"中(語言設定為英文即顯示"Photos"),是個相當好用的功能,但在程式中該如何快速擷取呢?以下就來講解一下

Download Example

首先來講擷取部份,先在.h檔內import
#import <QuartzCore/QuartzCore.h>

再來到.m檔內設定一個按鈕來做擷取的動作,在此直接在viewDidLoad直接建立按鈕,當然也可以用IB等其他方式來做
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 410, 140, 40)];
[myButton setBackgroundColor:[UIColor blackColor]];
[myButton setTitle:@"screen capture" forState:UIControlStateNormal];
[myButton addTarget:self action:@selector(capture) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myButton];
[myButton release];


接著把"capture"這個function也建立出來
-(IBAction) capture{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
//NSLog(@"viewImage = %@",viewImage);
}


到這可以build看看是否可以順利Run,按下按鈕會自動把畫面儲存至"相片"內,重點來了,可是這樣很麻煩,還要關掉app跑去"相片"看,為了懶總是要把這問題解決

目前作法是,畫面中一張圖兩個按鈕,一個按鈕擷取螢幕,另一個連結至"相片",並且相片選取後可以丟到畫面中的圖片位置,先把剛剛的.h檔改為
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

@interface screenCaptureViewController : UIViewController {
UIImagePickerController *imagePicker;
UIImageView *imageView;
}

@property (nonatomic, retain) UIImagePickerController *imagePicker;

@end


再來到.m檔部份,首先在@implementation與@end中間加入這行,將UIImagePickerController用property來管理記憶體
@synthesize imagePicker;


接著在viewDidLoad內加入
//set image
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
[imageView setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:imageView];

//set screen capture button
UIButton *myButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 410, 140, 40)];
[myButton setBackgroundColor:[UIColor blackColor]];
[myButton setTitle:@"screen capture" forState:UIControlStateNormal];
[myButton addTarget:self action:@selector(capture) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myButton];
[myButton release];

//set choose photo button
UIButton *myButton2 = [[UIButton alloc] initWithFrame:CGRectMake(170, 410, 140, 40)];
[myButton2 setBackgroundColor:[UIColor blackColor]];
[myButton2 setTitle:@"choose photo" forState:UIControlStateNormal];
[myButton2 addTarget:self action:@selector(gallery) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:myButton2];
[myButton2 release];

//set image picker
imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;


[self.view setBackgroundColor:[UIColor whiteColor]];


然後在@implementation與@end中間加入這兩個function&delegate
-(IBAction) capture{
UIGraphicsBeginImageContext(self.view.bounds.size);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageWriteToSavedPhotosAlbum(viewImage, nil, nil, nil);
//NSLog(@"viewImage = %@",viewImage);
}

-(IBAction) gallery{
[self presentModalViewController:self.imagePicker animated:YES];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)img editingInfo:(NSDictionary *)editInfo {
imageView.image = img;
[[picker parentViewController] dismissModalViewControllerAnimated:YES];
}


最後請記得在dealloc把imageView給釋放掉
- (void)dealloc {
[imageView release];
[super dealloc];
}


完成後build起來畫面應該是如下,可以按下screen capture來擷取畫面...不過我懶得寫個alert來提示


點下choose photo


點入相簿內


選取所擷取的畫面就會切回來並且把上面黑色那塊換掉


多用幾次就會變成這樣



Download Example

相關連結
iOS Developer Library
iOS Developer Library - UIImagePickerController Class Reference