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

Android Dialog

dialog可以說是超常用也超好用,其實官網就有很完整的範例,但是在custom dialog部份有點讓人看不懂(至少我是...)



xml的部份我想就不必多說,跟官網一樣也可以,new出來的部份就有點不同
Dialog dialog = new Dialog(AAAAA.this);
dialog.setContentView(R.layout.dialog_login);
dialog.setTitle("測試一下");


AAAAA的部份就是class的名稱
title就看使用時自己訂,當然也可以從strings.xml取得
內容就直接從xml去繪製,而按鈕、所有東西要使用也是跟一般一樣要先跟R.java做好連結

相關連結
Android Developers
Android Developers Creating Dialogs

Android Resources strings

建立Android專案時就會自動產生strings.xml(位置於專案/res/values/strings.xml),這簡單來說就是把文字部份拉出來整理,需要做多國語言時相當的重要,就算不是要作多國語言建議還是用一下,不然哪天為了找一個字串還要找半天,而且裡面不只可以放單一字串,字串的陣列也可以。

單放字串
<string name="app_name">我是懶人</string>

app_name就是這字串的帶稱想要使用時在一般xml檔就如下列用法
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name">
</TextView>


但總是會遇上需要在.java內要用上的時候,以上方那個TextView為例
text.setText(getString(R.string.app_name));

字串陣列在strings.xml檔內
<string-array name="myArray">
<item>11111</item>
<item>22222</item>
<item>33333</item>
</string-array>


<!-- 也可以直接抓取string內容來用 -->
<string name="red">Red</string>
<string name="orange">Orange</string>
<string name="yellow">Yellow</string>
<string name="green">Green</string>
<string name="blue">Blue</string>
<string name="black">Black</string>

<string-array name="colorArray">
<item>@string/red</item>
<item>@string/orange</item>
<item>@string/yellow</item>
<item>@string/green</item>
<item>@string/blue</item>
<item>@string/indigo</item>
<item>@string/violet</item>
</string-array>


取得陣列
Resources res = getResources();
String[] array = res.getStringArray(R.array.myArray);

//直接寫在一起
String array[] = getResources().getStringArray(R.array.colorArray);


相關連結
Android Developers
Android Developers R.string

Android SharedPreferences

簡單來說就是儲存些變數在app內部,如同iOS可以存在app內的plist一樣,plist可以存array、dictionary、integer、boolean、number、string......等,目前沒多去試android這是否也這麼齊全,但至少可以用key/value的方式來存一般來說也夠了,真的要多存點東西就可以考慮用sqlite

儲存
SharedPreferences myPreferences = getSharedPreferences("myPreferences", MODE_PRIVATE);
SharedPreferences.Editor e = myPreferences.edit();
e.putInt("myInteger", 1000000);
e.commit();


e.putInt這邊可以自己選擇型態,懶一點跟我一樣就補全再上下選擇要用的就好

讀取
SharedPreferences myPreferences = getSharedPreferences("myPreferences", MODE_PRIVATE);
int i = myPreferences.getInt("myInteger", 0);


myPreferences.getInt("myInteger", 0);
getInt("變數名稱", 預設變數);

相關連結
Android Developers
Android Developers SharedPreferences
Android Developers SharedPreferences.Editor

Android Activity 切換傳參數

Activity在參數間互傳應該也算是個相當常用上的功能
兩個Activity就像傳球一樣一個丟另一個接
由於我腦容量比較小還是來紀錄一下...以防忘記

現在狀況是ActivityA切換到ActivityB
然後丟一個參數過去

ActivityA找個要切換的地方寫上
Intent intent = new Intent();
intent.setClass(this, ActivityB.class);
Bundle bundle = new Bundle();
bundle.putInt("KEY_A", 1);
intent.putExtras(bundle);
startActivity(intent);


注意:
1. intent.setClass(this, ActivityB.class);,這中間的this如果不是直接寫在Avtivity下面那層會出現錯誤,只需要更正為
intent.setClass(ActivityA.this, ActivityB.class);

這個this在objectC中就像是self,但是又不大相同,常常會用上,但是要小心

2. bundle.putInt("KEY_A", 1);,bundle中可以依照需求增加更多參數,不限制Integer一種型態,就算是String、boolean、......各種型態都可以,自動補全按下去慢慢挑就好(如下圖)

裡面的("KEY_A", 1)就是常用的key/value,objectC中就是NSDictionary

ActivityB這端來接
Bundle bundle = this.getIntent().getExtras();
int key = bundle.getInt("KEY_A");
//System.out.println("key = "+key);


注意:
1. int key = bundle.getInt("KEY_A");這邊就依照key名稱去對應所得到的value,getInt也需要依照類型的不同更改為getString等的方式去更改型態,等號前面的型態也記得要與等號後方相同

--
前面的部份是單純傳過去,那如果Activity傳過去後在ActivityB執行過程中還需要切回ActivityA,或者兩邊可能要同步作一些動作就需要再加上一點東西

ActivityA原來上面那段稍作修正
Intent intent = new Intent();
intent.setClass(this, ActivityB.class);
Bundle bundle = new Bundle();
bundle.putInt("KEY_A", 1);
intent.putExtras(bundle);
//startActivity(intent); //修改這行
startActivityForResult(intent, 0);


ActivityA再內加上
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//TODO something
//System.out.println("requestCode = "+requestCode+" || resultCode = "+resultCode+" || data = "+data);
int keyB = data.getExtras().getInt("KEY_B");
//System.out.println("keyB = "+keyB);
}


其中data就是你所回傳的intent,從其中取出bundle所帶的值即可

ActivityB需要執行回傳的地方加上這段
Bundle bundle = new Bundle();
bundle.putInt("KEY_B", 1);
setResult(RESULT_OK, (new Intent()).putExtras(bundle));


由於跟上面一樣就不再多加敘述


當然還可以用更多的方式來傳參數,這只是其中的一個解決方案。

相關連結
Android Developers
Android Developers - Activity class

Android EditText Listening soft keyboard

EditText是個基礎元件,基本預設按下去就會彈出鍵盤來讓你輸入內容,算是相當人性化,在部份情況下可能只要需要使用"EditText.getText()"來取出文字判斷就好,但在需要即時(如鍵盤按下瞬間就進行判斷)就需要用上其他方式

Listening soft keyboard 檢查軟體鍵盤(就是畫面彈出那個)
EditText.addTextChangedListener(new TextWatcher(){

@Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
System.out.println("afterTextChanged || 按下後");
}

@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
System.out.println("beforeTextChanged || 按下前");
}

@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO Auto-generated method stub
System.out.println("onTextChanged || 按下中");
}
});


但是光這樣還是有些鍵是檢查不到的 EX:ENTER(keyCode = 66)、BACKSPACE(keyCode = 67),必須再搭配下面這方式,下面這方是也可以偵測到外接鍵盤(可能對tablet比較常用)
EditText.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
System.out.println("keyCode = "+keyCode);
//return true = finish || false = track
return false;
}
});


如果只是要得到硬體鍵盤、按鍵,可以使用下面這方式
/* hardware keyboard */
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
//todo something
System.out.println("keyCode = "+keyCode+" || keyEvent = "+event);

//ex: ENTER
if(keyCode == KeyEvent.KEYCODE_ENTER){
//press ENTER
}

return super.onKeyDown(keyCode, event);
}


那如果都是使用外接鍵盤覺得內建鍵盤很礙眼就是要把他隱藏起來
//先宣告
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

//隱藏起來 soft keyboard
inputManager.hideSoftInputFromWindow(editText.getWindowToken(), 0);

//叫出鍵盤 soft keyboard
inputManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);


相關連結
Android Developers
Android Developers - InputMethodManager
Android Developers - TextWatcher
Android Developers - EditText

iOS 檢查網路狀態

有需要連線至網路的app如果沒了網路就跟死魚一樣(如facebook for iphone)
所以沒網路時一定要告訴使用者你現在沒連網路

Download Code

使用方式
step1.把檔案抓進專案中


step2.加入 SystemConfiguration.framework




step3.在.h檔內import
#import "CheckNetwork.h"


step4.在.m檔中使用
CheckNetwork *checkNetwork = [[CheckNetwork alloc] init];
// YES = connect / NO = death
NSLog(@"connectedToNetwork = %d", [checkNetwork check]);
[checkNetwork release];


其實在官方也有個Sample Code也是同樣的功能
不過...小弟我不才看不太懂(明明就是懶...)

-
相關連結
iOS Developer Library
iOS Developer Library - System Configuration Reference
Download Code

iOS 影音媒體播放

一般在使用內建的mediaplay感覺很麻煩
我是個懶人,看到很多code就會想睡
所以就乾脆獨立出個檔案來作比較方便

主要內容如下
1. 播放時全螢幕,調整為適合畫面比例
2. 當device旋轉時影片也會自動跟著轉
3. 只要把URL(string)丟進去就會開始執行了

Download Code

用法一樣很簡單
step1. 把檔案抓進專案中


step2. import MPMediaplayer framework




step3. 在.h將IskenMediaPlayer import進去
#import "IskenMediaPlayer.h"

step4. 在.m檔內使用
IskenMediaPlayer *player = [[IskenMediaPlayer alloc] init];
[player setMediaURL:@"http://XXXXXXXXXXXXX"];
[self presentModalViewController:player animated:YES];



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

Download Code

iOS - 檢查Email格式 check email format for Objc

前幾天稍微找了一下(真的是一下...只點開兩個連結看...)檢查email格式的東西好像沒有針對objc的
反正剛好要用上就順手寫了一下
稍微看過email標準格式後就開始動工
其實也只算是寫個大概...簡單判別一下而已
沒有作email驗證的email address大概90%都是亂寫的吧(至少我自己就是這樣)

大致上判斷以下這些部份
1. 簡單判斷內容是否有特殊字元
2. 是否有手癢多輸入了幾次 @
3. 是否有手癢多輸入幾個 .
4. 結尾有沒有加.com、net、tw...等

Download code

使用方式
1. 先在.h把檔案import進來
#import "CheckEmailFormat.h"


2. 把物件new出來並且判斷
只要把check:後面加入要判斷的email address即可
會回覆你YES/NO
如果是NO會自動彈出一個警告告訴user錯誤
CheckEmailFormat *checkEmail = [[CheckEmailFormat alloc] init];
//retuen 1 = ok || 0 = error
NSLog(@"This Email address is %d", [checkEmail check:@"abc@11111111111111.com"]);
[checkEmail release];


參考資料
iOS Developer Library
iOS Developer Library - NSString Class Reference

Download code

iOS dial a phone call 在app內撥電話

iOS上面並沒有直接呼叫撥號功能的方式,必須透過UIWebView來進行(iOS 3.1後,iOS 3.1前是使用UIApplication),也就是用URL的方式進行撥號,根據官方文件“Mail Links”、“Phone Links”、“Text Links”、“Map Links”、“YouTube Links”、“iTunes Links”等的東西都可以透過url來呼叫,以下就先介紹撥號。

撥號的URL格式為:"tel:1234567890"
如果需要加上#字號 ex:"tel:1234567890#123",請將"#"換為"p" ex:"tel:1234567890p123"
需要二次撥號加上"," ex:"tel:1234567890,123"

NSURL *phoneNumber = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"tel:123456789"]];
if ( [[UIApplication sharedApplication] canOpenURL: phoneNumber] ){
//get iOS version
NSString *osVersion = [[UIDevice currentDevice] systemVersion];
//NSLog(@"iOS version = %@",osVersion);

if ([osVersion floatValue] >= 3.1) {
NSLog(@"iOS version is after 3.1, version = %@",osVersion);
UIWebView *webview = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
webview.alpha = 0.0;
NSLog(@"phoneNumber = %@",phoneNumber);
[webview loadRequest:[NSURLRequest requestWithURL:phoneNumber]];

// Assume we are in a view controller and have access to self.view
[self.view addSubview:webview];
[webview release];

}
else {
// On 3.0 and below, dial as usual
NSLog(@"iOS version is below 3.1, version = %@",osVersion);
[[UIApplication sharedApplication] openURL: phoneNumber];
}
}else{
//can not get phone call
UIAlertView* alert = [[UIAlertView alloc] initWithTitle: @"系統訊息"
message: @"無法順利撥號"
delegate: nil
cancelButtonTitle: @"確認"
otherButtonTitles: nil];
[alert show];
[alert release];
}


撥號成功
系統會自動產生是否確定撥號的訊息


撥號失敗
我們自己寫的警告無法順利撥號


參考連結
iOS Reference Library
iOS Reference Library - Apple URL Scheme Reference

iOS -shouldautorotatetointerfaceorientation 自動旋轉畫面

使用iPhone、iPad、iPod Touch系列產品常常很自然的會把他轉來轉去(如下圖)

圖片來源:http://www.sopods.com/images/apps/FullScreenIPhone_rotation_bars.jpg

根據官方文件(Launching your Application in Landscape)使用起來確實超級簡單(此方法使用於iOS 2.1以後版本)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}


就這樣短短幾行就可以把畫面固定在橫的
Home鍵在右手邊
那如果要轉其他邊或者瘋狂的隨你轉呢
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
NSLog(@"shouldAutorotateToInterfaceOrientation = %d",interfaceOrientation);

if(interfaceOrientation == UIInterfaceOrientationLandscapeRight){
NSLog(@"RIGHT");
//return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}

if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft){
NSLog(@"LEFT");
//return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

if(interfaceOrientation == UIInterfaceOrientationPortrait){
NSLog(@"Portrait");
//return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

if(interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown){
NSLog(@"PortraitUpsideDown");
//return (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown);
}
return YES;
}


僅只需要將最後的return設定為YES畫面就會隨著你轉
想要固定在某一邊只需要改成return下面這幾個
上:UIInterfaceOrientationPortraitUpsideDown
下:UIInterfaceOrientationPortrait
左:UIInterfaceOrientationLandscapeLeft
右:UIInterfaceOrientationLandscapeRight

而我在這function內最上面所印出來的log最後面帶的數字就是代表這方向的數字
想要使用只需要將這function加入在.m檔中的@implementation與@end中間
但.h檔必須繼承UIViewController

光是能轉換還是會遇上座標的問題
如果本來所使用都是固定座標就必須把座標系轉換一下(如下圖)

圖片來源:http://blog.sallarp.com/shouldautorotatetointerfaceorientation/



參考連結
iOS Developer Library
iOS Developer Library - UIApplication Class Reference
iOS Developer Library - Technical Note TN2244 Launching your Application in Landscape
http://blog.sallarp.com/shouldautorotatetointerfaceorientation/

iOS addSubview & removeFromSuperview

view的這概就有點像是
Photoshop的layer
Android的layout
Flash的symbol

app內可不像flash不想要這個symbol就丟進垃圾桶
要用時再直接從library拉出來
(當然全都使用Interface builder是有點類似啦)

要使用code來丟入場景中所使用的就是"addSubview"
如以下範例
[self.view addSubview:myView];


加入後會自動將myView的retainCount +1
如果原本的view不需要用上請記得釋放掉


那該怎樣知道現在這個View內有多少View
下面這範例會把內容全都println出來
NSLog(@"view = %@", [self.view subviews]);



知道有多少後該怎樣移除也是個重點
以下這就是將你指定的view移除
[myView removeFromSuperview];



那假設view中的按鈕很多看起來很礙眼想要一次移除
for(UIView *subview in [self.tabBarController.view subviews]) {
if([subview isKindOfClass:[UIButton class]]) {
NSLog(@"remove UIButton");
[subview removeFromSuperview];
} else {
// Do nothing - not a UIButton or subclass instance
}
}


isKindOdClass顧名思義就是找出跟他相同class的東西


參考資料
iOS Developer Library
iOS - UIView Class Reference
iOS - UIViewController Class Reference

Android - arrayAdapter refresh row

如果在固定大小的情況下還是建議使用BaseAdapter
但在大小未知的狀況下還是使用arrayAdapter比較適當
arrayAdapter有著四大常用的功能add(新增一筆到最後)、insert(新增一筆到指定位置)、remove(刪除最後一筆)、clear(全部清除)
但是每次新增的時候如果沒有讓getView作reDraw的動作並不會出現
或許新增的時候並不在畫面中沒有感覺
但新增或刪除在畫面中沒反應會造成使用上的困擾
廢話好像有點多...

如果只是想要做一般的reDraw
也就是將整個arrayAdapter刷新只需要使用下面這行即可
用了這個會全部重新繪製(也就是會跳回最開頭)
ListView.setAdapter(arrayAdapter);


如果想要保留在畫面中但是又更新請使用下面這兩行
用NotifyChange的方式告訴arrayAdapter資料更新
arrayAdapter.notifyDataSetChanged();
arrayAdapter.setNotifyOnChange(true);


此方法不僅在一般的ListView可以使用
在自動完成(AutoCompleteTextView)也一樣可以使用
做出類似google搜尋輸入時的建議搜尋


標題為啥要用英文定...因為我也不知道該怎樣比較適當的翻譯成中文一一

參考資料
Android Developer
Android - ArrayAdapter
Android - ListView

Android - Button in ListView

在Android內ListView是個相當常用的東西
相對於iOS來說也就是TableView
在iphone上面用tableView或Android的ListView上面加個按鈕都是相當容易的事
但是加了按鈕後該如何取得按鈕的事件(按下、放開、點兩下、長按、拖曳....等)在Android上面卻成了問題
當按鈕與ListView同時出現時只能夠偵測到按鈕的事件
而原本所使用的interfave或OnItemClickListener來偵測按鈕事件卻會失效

如下圖就是兩個同時存在的狀況(ListView & checkBox)


如果沒有解決辦法我也不會來po這篇XD
在Listview的interface內getView加上下面這行(以圖中這範例為例,使用checkBox)
checkBox.setFocusable(false);


但是光這樣設定還是會造成checkBox跟背景沒辦法融合
也就是點按鈕是按鈕,ListView的row是不同的地方
只要加上以下這行,把checkBox設定為不可按就搞定了
checkBox.setClickable(false);


在Android內的ListView、GridView與ListView內容都必須靠Adapter來作管理(iOS為NSArray或NSMutableArray)
只需要宣告上作修改就可以用另一種方式作呈現

參考資料
Android Developer
Android - ArrayAdapter
Android - ListView
Android - GridView

台灣自行車節-挑戰200K

已經好久沒有騎這麼長途,總覺得會報名這種只有兩種人-1.瘋子、2.不知道狀況被騙來,我就是那瘋子,可惜騙不到人跟我一起來XD,在出發前三週還摔車,整整休息一週後免強開始練車,這兩週的時間假日就是150km+平日55km,每天下班稍微吃點東西就是衝出去騎車,55km就大約是兩小時的時間,回家還可以稍微休息一下,運氣也很好的規劃出發前一天晚上休息,這兩週也是那天晚上才開始下雨,其實大約才一週的時間體力就調整回很好的狀態,加上新買的車褲實在太強勁了,長途騎乘屁屁完全不會痛,但是出發前還是警惕自己"計畫趕不上變化"當天會有多少突發狀況會導致連騎完都是個問題。

出發當天早上下著小雨,不禁開始擔心當天會在雨中渡過,還好雨在過花蓮後就停了,但沿途有在注意風向就開始有點怕到,看花草被風吹到有點變形就知道明天去程應該會騎得很辛苦,反程如果太晚風向改變....就繼續痛苦XD。由於我搭的是專車,車子可以值接上臺鐵(完全不用拆),人坐再別的車廂內,看到一堆人還沒出發就穿著車衣車褲的殺氣騰騰,今天行程應該只有搭火車,穿著車衣車褲有比較厲害嗎?這點就是愛吧,原本沒查過原來花蓮有停,可以讓你下車買點吃的,那時也差不多中午,根本也不知道可以下車買就乖乖的跟主辦單位一起訂便當,NT90,cp值好像有點低,可惜這停靠時間還是有限,沒辦法讓我衝下車去買點麻薯當作明天騎車的點心。

到了知本後出車站根本還稿不清楚方位就傻傻的跟著大家騎,大概知道方向後....我前方就沒人了一一,一路騎到明天出發點"溫泉國小"作場勘,稍作查看後開始找尋訂的旅館,剛看到旅館老闆有點嚇到,滿嘴檳榔牙齒也掉一堆,講話口音也頗有台灣味,但其實人還不錯,做完check in後太無聊就騎去台東市區晃了一下,吃點晚餐,換了手把帶,買了隔天的早餐就回旅館看電視,那滿口檳榔的老闆還請了我一顆耶子,不過在這讓我很痛苦...房間再大沒有好的洗澡品質也是很討厭。隔天五點開賽,沒好好的睡覺就等著瘋狂抽筋吧,還好出發前跟媽媽拿了半顆安眠藥,讓我順利的在9點左右就順利入睡。

隔天早上四點半起床昏昏沉沉的吃著昨天買的早餐,不過似乎買的有點多,還帶了一條麵包路上吃,四點多,天都還暗,而我住的地方距離出發點大約500公尺,但大約有300公尺的上坡,還在路上被一隻忽然衝出來的黑狗嚇到摔車,在到會場前沒看到半個人,到會場後人山人海...一堆人早等在出發點,還看到了標哥也出現在會場,站在會場的最內看著旁邊還一堆人,但已經過5點了,照理來說應該是已經出發才對,最後跑到出發大門一看人早已早光....問了一下工做人員「早就出發了阿」幹...我怎麼都不知道,馬上開始衝,起初遇上一個騎登山車的人有點對不起他,說好跟他輪車但衝的有點太快,才過大約10公里就看他很喘的跑進便利商店,大約在20公里左右前方已經看不太到人,大約6點開始天也開始亮起來,快到50公里左右騎到有點想睡,還好遇上一位君悅車隊的人陪我聊天....不然應該會睡著,在大約50公里有大會的休息區,提供了大量的香蕉與餐包,那香蕉的量真的很恐怖,說是個小山丘真的不為過,往後每50公里都有這樣的休息站,都有一座小山...

由於算是長途平常也沒騎這麼遠,路上為了紓解守的壓力開始練習放雙手騎,慢慢的放開>>上半身開始拉筋>>拿起背後的麵包出來吃>>一手麵包一手飲料,我想接下來應該挑戰炒飯,進一步便當,最後希望能進步到吃牛肉麵....我會努力往這一步邁進的!!轉入193時開始有點小雨,也在旁邊看到兩道彩虹,聽說晚一點到的雨勢超大,很幸運的準備了雨衣但都沒用上。

在大約180公里時忽然全身沒力,有種要虛脫的感覺,看了手錶發現原來12點了....八成是肚子餓,馬上找了間便利商店吃了一碗泡麵,這泡麵真是美味阿!!肚子餓食物果然很棒,但還有20公里...結束後可以大吃特吃,速速的吃完後果然體力也回來了,最後的20公里均速應該有30以上,整個像活過來一樣,比想像中的早了不少到達,而且到達後還沒有很累,火車要到2130才發車,還有8小時該怎樣渡過,問了工作人員一下附近有啥好玩的,答案都是「我不是在地人」「那個好像哪邊還不賴」,最後跑去附近的海邊晃了一下,開始了覓食的行程,跑去附近的7-11吃了一堆東西,大概是兩個便當兩罐飲料加甜點這大概平常一天的量吧....才沒多久就吃光,時間還早又跑去車站、知本附近逛了逛,只能說這邊還真小...沒兩下就真的不知道該去哪,最後只好又跑去7-11吃東西...

在吃得過程中遇上了一位一起從松山發車的潘大哥,就這樣喇賽到發車前,在上火車找到座位的那瞬間外面開始下起傾盆大雨,這雨真的大到很誇張,真的要說有多誇張的話...在隔一兩天就是蘇花坍方,也就是持續一兩星期新聞不斷播放的那個,真的想當得幸運,出發前的練習時間都沒遇上下雨,順利的完賽,直到搭上火車才開始傾盆大雨,回到松山後凌晨3點多,也只有些許的毛毛雨,也希望天災所造成的傷害能在時間的流逝中慢慢撫平,花東這大自然的鬼斧神工也能夠繼續的保存下去,讓這美景分享給更多人。

相關連結
相簿:知本玉里200k
NeverStop官網

iOS HTTP Request - POST

這東西不管是作網站或者網路服務都超給他重要的

/****************************************************************************************/
//會員與新會員統計
//宣告一個 NSMutableURLRequest 並給予一個記憶體空間
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
//宣告一個 NSURL 並給予記憶體空間、連線位置
NSURL *connection = [[NSURL alloc] initWithString:@"http://XXX.XXX.XXX.XX/XXXXXXX.php"];
//宣告要post的值
NSString *httpBodyString=[NSString stringWithFormat:@"XXX=%@&XXX=%@&XXX=%@", YYY, YYY, YYY];
//NSLog(@"httpBodyString = %@",httpBodyString);
//設定連線位置
[request setURL:connection];
//設定連線方式
[request setHTTPMethod:@"POST"];
//將編碼改為UTF8
[request setHTTPBody:[httpBodyString dataUsingEncoding:NSUTF8StringEncoding]];

//轉換為NSData傳送
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//看request出來的值
NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
/****************************************************************************************/


NSURL *connection = [[NSURL alloc] initWithString:@"http://XXX.XXX.XXX.XX/XXXXXXX.php"];
再這裡用php作代表
但會因為使用的server語言而變 ex.asp、aspx、php、jsp.......等
XXXXX的部份就是你的url

NSString *httpBodyString=[NSString stringWithFormat:@"XXX=%@&XXX=%@&XXX=%@", YYY, YYY, YYY];
XXX所代表就是在server端要接收的變數名稱
YYY就是要丟進去的值
記得每一筆都要給他用&分開

相關連結
iOS Library
iOS Library - NSMutableURLRequest Class Reference
iOS Library - NSURL Class Reference
iOS Library - NSData Class Reference
iOS Library - NSString Class Reference

Objective C - String

真的用太多了...不過用了很多還是記不住XD
腦容量大概2k吧我....

============== 字串搜尋 ================
比對字串內容 - string2 去比對 string1內容
NSString *string1 = @"我是個大笨蛋";
NSString *string2 = @"笨蛋";
NSRange range = [string1 rangeOfString:string2];

//print出來
NSLog(@"位置:%d || 字串相同長度:%d", range.location, range.length);


NSRange range = [字串 rangeOfString:字串];
range.location 為所在位置
range.length 為字串相同長度

由以上的code可以得到一長串的句子中哪幾個字是你要的
是從哪一個字元開始,長度為何

※如果一串字中有好幾個重複的字 ex:我是個超級大笨蛋笨蛋笨蛋
他只會找到地一個笨蛋


============== 字串比對 ================
比對兩個字串是否相同
NSString *myString = @"我是個大笨蛋";
NSString *string1 = @"無敵大笨蛋";
NSString *string2 = @"我是個大笨蛋";

//結果為false / NO
if( [myString isEqualToString:string1 ] )

//結果為true / YES
if( [myString isEqualToString:string2 ] )


[字串 isEqual:字串] or [字串 isEqualToString:字串]
在比對字串時上面兩個用法都可以用
用法詳細區別請去看官方library...

============== 抽取字串 ================
從字串開頭開始擷取到指定位置
很抽象對吧...由範例比較好懂

NSString *string1 = @"我是個笨蛋";
NSString *string2 = [string1 substringToIndex:2];

//print "我是"
NSLog(@"string2:%@",string2);

[字串 substringToIndex:數字];
由上面這範例可以清楚了解到所print出來就是從字串頭開始算你要幾個字

--
當然也可以從想要的地方開始找
NSString *string1 = @"我是個笨蛋";
NSString *string2 = [string1 substringFromIndex:3];

//print "笨蛋"
NSLog(@"string2:%@",string2);


[字串 substringFromIndex:數字];
從某一個字開始找字串

--
任意取出字串中想要的部份
NSString *string1 = @"我是個笨蛋";
NSString *string2 = [string1 substringWithRange:NSMakeRange(1, 4)];

//print 是個笨蛋
NSLog(@"string2:%@",string2);


[字串 substringWithRange:NSMakeRange(起始點(數字), 終點(數字))];
就可以直接取得想要的部份


相關連結
iOS Library
iOS Library - NSString Class Reference

iOS UIPickerView tutorial - IB

UIPickerView究竟是啥(請見下圖)


這是個在iphone內相當常見的一個功能
還可以切成好幾個欄位來作選取滾動
操作上是相當好用的
但是該如何切入使用呢?

首先開啟Interface Builder隨便拉一個UIPickerView


接著來看看有哪些屬性
先點下Inspector


就會出現這畫面,告訴你這個UIPickerView內有哪些東西可以來設定


但是光這樣拉UIPickerView compile後也是看不到任何內容
記得要把delegate還有dataSource連結上File's Owner
以及在.h檔內加上IBOutlet
#import

/*
UIPickerViewDelegate, UIPickerViewDataSource
上面這行是最重要的東西,也就是所謂的Delegate
一定要加入
*/
@interface UIPickerViewViewController : UIViewController {
//這兩行是讓Interface Builder裡面拖拉的物件連結在一起
IBOutlet UIPickerView *myPicker;
IBOutlet UILabel *myLabel;

//等一下要丟入的內容的陣列
NSMutableArray *myArray;
}

//這兩行是用property方式管理記憶體
@property (nonatomic, retain) IBOutlet UIPickerView *myPicker;
@property (nonatomic, retain) IBOutlet UILabel *myLabel;

@end


存檔後繼續開啟Interface Builder再來玩一下連連看
File's Owner上面點下右鍵連到picker


File's Owner上面點下右鍵連到label


如果你動作都跟我長得一樣現在在File's Owner上面應該會長這個樣


接著就是把內容丟入picker啦
開啟.m檔 直接把這些貼進去
#import "UIPickerViewViewController.h"

@implementation UIPickerViewViewController

//有使用property請記得這邊也要相對應
@synthesize myPicker, myLabel;

- (void)viewDidLoad {
[super viewDidLoad];

//給這個陣列一個記憶體位置
myArray = [[NSMutableArray alloc] init];
//一次塞100行進去看起來比較厲害
for(int i = 0; i < 100; i++){
//[陣列名稱 新增一個內容:內容(這邊給他一個字串)];
[myArray addObject:[NSString stringWithFormat:@"這是第 %d 行",i+1]];
}

}

/*************************************************************/
//當前所選擇為哪一項
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
//把選擇到的丟入 myLabel中顯示出來
myLabel.text = [myArray objectAtIndex:row];
}

//設定滾輪總共有己欄
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView {
return 1;
}

//設定滾輪總共有幾個項目
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component {
return [myArray count];
}

//設定滾輪每一個行位內容為啥
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return [myArray objectAtIndex:row];
}

/*************************************************************/


- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.
}

- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}


- (void)dealloc {
//記得記憶體要把他給釋放掉(必須在[super dealloc]前)
[myPicker release];
[myLabel release];
[super dealloc];
}

@end


完成後的demo影片


下次再來介紹如何使用code直接在.m檔內實做UIPickerView以及UIDatePicker

相關連結
iOS UIPickerView Class Reference
Download this example

Google Analytics for Picasaweb

從很早很早以前就對picasa不能紀錄每張照片瀏覽次數很度爛
明明就是個很簡單的功能(有了也是很兩光)
最近google對於他自家的軟體整合性有大幅度的提昇

就拿自次主題的picasaweb來說好了
以前要再google earth上面能夠看到有座標的照片一定要去申請Panoramio
不然你Picasaweb標滿了座標在google earth上面也看不到
現在每張照片點開後右下角(如下圖)就有選項了


回到主題
前幾天不小心發現到現在有這功能"使用「Google Analytics (分析)」追蹤相片的觀賞次數"
不知道Google Analytics究竟是啥鬼?請看先前介紹
接著又到step by step啦
step 1:開啟你的picasaweb點選右上角的設定>>相片設定



step2:勾選"相片追蹤:「Google Analytics (分析)」可為您追蹤相片的瀏覽人數。"
此時候面會出現一個框要你輸入號碼,先別鳥他


step3:開設Google Analytics
進入自己的google帳戶內找出他


step4:建立新帳戶(新增一個追蹤服務)


step5:跟著他所要求慢慢輸入
重點!! "網站網址:"這邊要填入"http://picasaweb.google.com"


step6:完成後在"報表"欄位上會看到一串英文用力把他複製起來


step7:回到picasaweb這邊把剛剛複製的貼上


step8:慢慢等待
照官方說法要24小時...不過實際上似乎不用這麼久
反正隔一天應該就可以好了...如果沒有好表示...你步驟有錯

相關連結
此設定Google教學
Google Analytics介紹

周春明 - 計程車的一片天



今天去聽了他本人的演講
演講內容確實蠻感人的,有種感染力
面對這種環境下依然可以順利的生存確實很強勁
聽完後大致上花了一兩分鐘思考整理
1.差異化經營 - 再競爭激烈的環境中要如何脫引而出這個很大的關鍵,能夠搶到先機持續下去肯定會有相對的成效,想要達成差異化我認為最快的途徑就是跨領域,結合不同領域的專長創造出新的應用,就以這角度來看,大學畢業後一定要走相同領域的路嗎?我真的覺得未必,未來肯定是要絕對的專業與跨領域的人交互合作,就看每個人如何去定義自己。

2.時時刻刻保持危機感 - 當你還沈靜在成功的喜悅同時會造成更多的盲點,而這些盲點也將會是致命傷,時時刻刻警惕著自己,不能忘記原點,更不要怕面對未來。

3.以顧客為中心 - 服務頁來說應該沒有比這更重要,簡單來說就是以人為中心,將心比心,幫顧客想到更多,能想到別人沒想到的部份就是成功的關鍵,就像設計一樣,創意存在於生活中,看到別人呼略掉的部份,將其加以包裝。

4.隨時保持觀察、聆聽 - 外在環境每分每秒都在變,關心身邊週遭的事,關心別人的事,關心世界的事,蝴蝶效應就是這樣來的,地球就這麼大,誰知道國外發生了一件事情會跟你沒有關係?

老實講看到演講過程中一堆人拼命抄ppt上面的嘉言美句,然後再把他倒背如流...又怎樣?說 誰不會?能夠將其實踐你就不是作在台下聽的那位,就算站在台上也要保持危機感,時刻的充實自己,學無止盡。回家問父母「有沒有需要幫忙的?」還不如用雙眼去觀察,用雙手去實踐。每一天都當作是自己的最後一天,精彩的去實現,一個人很厲害沒有用,要能讓所有人都跟你一樣厲害才有用。

MAC將隨身碟/硬碟代替光碟

抱歉這標題真的想不到啥好詞,總之重點就是常常我們有映像檔在安裝軟體時一定要燒成光碟,如安裝系統,如果在沒有其他台電腦可以直接作轉移的時候少了光碟還真是不知道該怎麼辦(當然還是有方法啦...但現在當作不知道),空白光碟片也是要錢,複寫片更是不便宜,只是為了久久一次的系統還原或幫別人裝(好人服務)實在有點浪費。

Step1:準備的就是個隨身碟(要用外接硬碟也都ok,只要能塞下光碟的容量),然後插入電腦中

Step2:打開磁碟工具程式(應用程式/工具程式/磁碟工具程式.app)
或者可以從finder/前往/工具程式/磁碟工具程式.app
再懶一點可以在finder按下"shift+command+U"就可以開啟工具程式,再找到磁碟工具程式即可


step3:找出親愛的隨身碟,點選回覆
在"來源"將準備好的光碟映像檔托移上去
在"目標"將親愛的隨身碟拖移上去


Step4:按下回覆
系統會問你是否要清除上面的內容,要省下一片空白光碟的錢,當然要!


Step5:輸入密碼,系統要確定你不是壞人想亂洗資料(我掰的)


Step6:準備零食飲料+電影...漫長的等候時光是不可以隨意浪費的!


Step7:完成
完成後會跳出這種畫面,就像你剛放入光碟進系統的自動執行一樣


桌面上則會出現光碟(如果有開啟顯示的話)


如果你也按照步驟到這邊恭喜你已經成功省下一片空白光碟,這用法不僅限於mac,要製作windows上得光碟一樣可以用,光碟在哪讀都一樣。