2011年9月8日木曜日

Windows で Titanium Studio を使って Android アプリ開発の準備

■はじめに

我が家には MacBookPro があって、既に Titanium Studio で開発ができています。

今回、Titanium Mobile によるスマートフォンアプリ開発勉強会(DevHub西新宿)を開催することになったので、 Windows での環境構築のチェックを兼ねて、 KitchenSink が動作するところまでを試してみました。

この記事は勉強会の資料を兼ねています。

環境

今回試した環境は Windows XP SP3 の AMD AthlonX2マシン。メモリは4GBです。Titanium Studio は要はeclipseであるのと、アンドロイドエミュレータも激重なので、マシンパワーのあるほうがいいと思います。

また、Titanium Compatibility Matrixにあるように、NT や Vista はサポート外のようなのでご注意ください。

1.JDKのインストール

既にJDKが入ってる場合は不要です。JREではダメ。PATHの設定等、やってないと忘れることだらけなので落ち着いてやりましょう。失敗してもアンインストールしてやり直せばいけます。(何度もやり直しましたw)

http://www.oracle.com/technetwork/java/javase/downloads/index.html から、Java Platform (JDK) 7をダウンロードして実行。。。してはだめでした。

http://jira.appcelerator.org/browse/TIMOB-5010

今まだこのような問題があるので、JDKは7ではなく6を使う必要があります。

ダウンロードするのはJDKの6(これを書いてる時点ではU27)です。

インストール先を変更すると、JRE6はそのディレクトリに入りますが、JDK はまんまと C:\Program Files\java の下に入ってしまうので、変更しないほうがいいかもしれません。

インストールが終わったら、お作法にならって、システム環境変数に

JAVA_HOME
インストールしたディレクトリ(C:\Program Files\Java\jdk1.6.0_27 など)

を設定して、PATH環境変数に、 ;%JAVA_HOME%\bin を追加します。

PATH環境変数に直接フルパスを書いても大丈夫ですが、BlackBerry をターゲットにする場合に JAVA_HOME がないと言われるので、素直にお作法にのっとりましょう。

2.Android SDKのインストール

http://developer.android.com/sdk/index.html から、Android SDKをダウンロードします。

バグってると言われるRecommendedのインストーラーでも概ね問題ありませんでした。

インストール先ディレクトリに空白を含まないほうがいいといわれているので、 C:\android\android-sdk とかにしておくといいと思います。

インストールが完了すると、SDK Managerが立ち上がる(上がらなかったらメニューから)ので、起動までしばらく待ちます。

Choose Package to Install になったら、必要なSDKを選択します。

この時、google 関連はあきらめて全部いれるのと、SDK Platform Android 2.1 (API 7 のやつ)は必須でいれるといいです。(不要なものはRejectすればよいです)

Titanium Studio が API 7 がないと SDK を認識しないとゆー残念な仕様なのでここも注意。

HDDとネットワークに余裕があるなら全部つっこんでも構わないと思いますが、全部いれると3GB程度あるそうなので自己責任で。(でも全部いれたほうが安全っぽい)


※勉強会ではプロキシ用意できたらいいんだけど。。。


気長に待ち終わると、ADB Restart ダイアログがでるので、はいを選択。

インストールまで終わったら、Android-sdk フォルダ中の /platform-tools/adb.exe を /tools/adb.exe にコピーしておきます。

PATH環境変数 に /platform-tools と /tools を加えておきます。

3.Titanium Studio のインストール

http://www.appcelerator.com/products/titanium-mobile-application-development/ から Titanium Studio をダウンロードします。ユーザアカウントの作成が必須です。Studio の起動のたびにこのアカウントによるログインが必要になります(明示的にログアウトしなければ自動です)。

ダウンロードしたファイルをダブルクリックでインストールして終了します。ここは迷うことはないかと思います。

4.デフォルトプロジェクトでAndroidエミュレータ実行

画面左の Project Explorer ペインから create で適当なプロジェクトを作ります。設定が済んだらRUNしてみましょう。

5.キッチンシンクのインストール

https://github.com/appcelerator/KitchenSink から KitchenSink をダウンロードして展開後、Studio の Project Explorer で右クリックImport からインポートして RUN します。

アンドロイドエミュレータ起動までの道のり

実はこのマシンの前に Vista と XPのSP2マシンと VirtualPC で試していたのですが、どれも動作確認までいけませんでした。ホストマシン(実機)で始めて成功したので記事にした次第です。

別のJavaが入っている場合は、Pathに注意しないといつまでもいけませんので注意が必要です。

今回の作業中で、jqs.exeというものに始めて気づきました。ついでに止めることができるようになったので、xpマシンが初期の頃のような快適さを取り戻す副作用がありましたw

SDCardを保存する場所(?)をこちらでコントロールできないにも関わらず、デフォルトで空白の入るフォルダに突っ込んでおいて、空白を含むフォルダにおいてあると起動しない、というなんともいえない状況になりました。後でリストする参考にしたサイトにも出ていますが、上記手順の場合、下記が必須です。

# builder.py 400行目くらい

# start the emulator
emulator_cmd = [
	self.sdk.get_emulator(),
	'-avd',
	avd_name,
	'-port',
	'5560',
	'-sdcard',
	'"%s"' % self.sdcard,    # self.sdcard から書き換えた
	'-logcat',
	'*:d,*',
	'-no-boot-anim',
	'-partition-size',
	'128' # in between nexusone and droid
]

参考にしたページなど

http://www.sawadaru.com/blog/?p=151
http://cedar-mount.com/blog/?p=16
http://blog.alt-scape.com/archives/404
http://d.hatena.ne.jp/Cherenkov/20110112/p1
エミュレータが起動しない
http://blog.showvoid.com/2011/07/36/
最終的にコレでいけた
http://d.hatena.ne.jp/stackbox/20110725/1311620528
参考
http://www.hlplus.jp/android/

2011年2月18日金曜日

NSDate と NSDateFormatter

-(void)showDateFormatter{
  NSDate *date = [NSDate date];
  NSDateFormatter *df01 = [[[NSDateFormatter alloc] init] autorelease];
  [df01 setDateStyle:NSDateFormatterMediumStyle];
  [df01 setTimeStyle:NSDateFormatterNoStyle];
  NSString * df01s = [df01 stringFromDate:date];
  NSLog(@"date formatter locale : %@", [[df01 locale] localeIdentifier]);
  NSLog(@"df01s : %@", df01s);

  NSDateFormatter *df02 = [[[NSDateFormatter alloc] init] autorelease];
  [df02 setDateStyle:NSDateFormatterFullStyle];
  [df02 setTimeStyle:NSDateFormatterFullStyle];
  NSString * df02s = [df02 stringFromDate:date];
  NSLog(@"df02s : %@", df02s);

  NSDateFormatter *df03 = [[[NSDateFormatter alloc] init] autorelease];
  [df03 setDateFormat:@"HH:mm 'on' EEEE MMMM d"];
  NSString * df03s = [df03 stringFromDate:date];
  NSLog(@"df02s : %@", df03s);
}

日付のスタイルと時間のスタイルを別々にセットできる。三番目のようにフォーマットを指定するのもできる。

フォーマットに使用出来る形式はここにあるみたい(iOSライブラリのマニュアルに書いてあったリンク)

UIScreen と CGRect

    // ステータスバーを含む画面のサイズ
  CGRect rect = [[UIScreen mainScreen] bounds];
  DLogR(rect);
    // ステータスバーを含まない画面のサイズ
  CGRect rectapf = [[UIScreen mainScreen] applicationFrame];
  DLogR(rectapf);

CGRect と UIScreen を見てみた。ちなみに、DLogR() はこんなの。Prefix header に書いている。

define DLogR(p) NSLog(@"x:%f,y:%f,w:%f,h:%f",p.origin.x,p.origin.y,p.size.width,p.size.height);

2011年2月17日木曜日

UILabel

-(void)showLabel{
  UILabel * label1 = [[[UILabel alloc] initWithFrame:CGRectMake( 60, 300, 200, 40 )] autorelease];
  UILabel * label2 = [[[UILabel alloc] initWithFrame:CGRectMake( 60, 350, 200, 40 )] autorelease];
  UILabel * label3 = [[[UILabel alloc] initWithFrame:CGRectMake( 60, 400, 200, 40 )] autorelease];
  
  label1.textAlignment = UITextAlignmentLeft;
  label2.textAlignment = UITextAlignmentCenter;
  label3.textAlignment = UITextAlignmentRight;
  
  label1.text = @"UITextAlignmentLeft";
  label2.text = @"UITextAlignmentCenter";
  label3.text = @"UITextAlignmentRight";

  label1.font = [UIFont fontWithName:@"Arial-BoldItalicMT" size:[UIFont systemFontSize]];
  label2.font = [UIFont fontWithName:@"Cochin-BoldItalic" size:[UIFont systemFontSize]];
  label3.font = [UIFont fontWithName:@"Courier" size:[UIFont systemFontSize]];
  
  label1.backgroundColor = [UIColor clearColor];
  label2.backgroundColor = [UIColor blackColor];
  label2.textColor = [UIColor whiteColor];
  label3.backgroundColor = [UIColor darkGrayColor];
  
  [self.view addSubview:label1];
  [self.view addSubview:label2];
  [self.view addSubview:label3];  
}

UILabel をコードで描画して配置する。textColor を clearColor にしたら文字が見えなくなった。背景を透過する抜き文字みたいにするにはどうしたらいいのかな。。。

2011年2月16日水曜日

UISwitch をコードで描画してみたけれども

- (void)showSwitch{

// center で描画すると座標が真ん中なのがね。。。
//  UISwitch * mySwitch = [[[UISwitch alloc] init] autorelease];
//  mySwitch.center = CGPointMake(200, 300);

// と思って CGRect にしたんだけど、UISlider と違って、
// サイズを指定しても背景領域もヒット領域も見た目も変化ないみたい?
  CGRect frame = CGRectMake(100.0, 100.0, 50.0 , 50.0 );
  UISwitch * mySwitch = [[[UISwitch alloc] initWithFrame:frame]autorelease];

  mySwitch.backgroundColor = [UIColor blueColor]; // スイッチの四隅に少し色がつくだけ?
  mySwitch.on = YES;
  [mySwitch addTarget:self action:@selector(showSwitchChange:) forControlEvents:UIControlEventValueChanged];

  // つまんないから縦にしてみたw
  CGAffineTransform scale  = CGAffineTransformMakeScale(1.0, 1.0);
  CGAffineTransform trans  = CGAffineTransformMakeRotation(270.0f * M_PI / 180.0f);
  CGAffineTransform concat = CGAffineTransformConcat(scale,trans);
  [mySwitch setTransform:concat];
  
  [self.view addSubview:mySwitch];
  
}
  // アクションセレクターにもだいぶ慣れた(同じ事しかしてないけど)
-(void)showSwitchChange:(id)sender{
  NSString * message = [[NSString alloc] initWithFormat:@"Switch : %d",[sender isOn]];
  
  UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"スイッチは"
                                                   message:message
                                                  delegate:self
                                         cancelButtonTitle:@"OK"
                                         otherButtonTitles:nil];
  [alert show];
  [alert release];
  [message release];  
}

スイッチも書いてなかったみたいなので。コメントにも書いたけど、UISwitchってUISliderと違ってサイズを指定してもヒット領域とかに変化が見られないみたい?書き方が悪い訳ではないと思うんだけど。。。

2011年2月15日火曜日

UIPickerView をコードで追加するのと、delegate と dataSource を外だしにしてみた

需要があるかどうかは知らないけど、今の機能に手軽にピッカーを追加できるように考えたら、ピッカーの追加とデータ取得だけを現在のコントローラに書いて、Delegate と dataSource はひとまとめにした別のファイルにしたらいいんじゃないかと思ってやってみた。いつものとおり、IBは使わないコース。まぁ練習。

// MyPicker.h
#import 
@interface MyPicker : NSObject <UIPickerViewDelegate,UIPickerViewDataSource>
{
  NSArray *      pickerData;
}
@property (nonatomic,retain) NSArray *      pickerData;
- (id) makeObj;
@end

// MyPicker.m
#import "MyPicker.h"
@implementation MyPicker
@synthesize pickerData;
-(id)makeObj
{
  NSArray * array = [[NSArray alloc] initWithObjects:@"test",@"test2",@"test3",nil];
  self.pickerData = array;
  [array release];
  return self;
}
#pragma mark -
#pragma mark UIPicker Data Source methods
  // コンポーネントの数を返す
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
  return 1;
}
  // コンポーネント毎(0始まり)の行数を返す
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
  return [pickerData count];
}
#pragma mark -
#pragma mark UIPicker delegate methods
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
  return [pickerData objectAtIndex:row];
}
#pragma mark -
- (void)dealloc {
  [pickerData release];
  [super dealloc];
}
@end

これで、現在のコントローラ中で以下のようにする。

// 現在のコントローラ.m

#import "MyPicker.h"

なんかの処理 {
  [self showPicker];
}
- (void)showPicker {
    // ピッカーを作って配置
  MyPicker * myPickObj = [[MyPicker alloc]autorelease]; // デリゲートとデータソースのオブジェクト
  [myPickObj makeObj];  // データいれる
    // ピッカーを描画
  UIPickerView * myPickerView = [[[UIPickerView alloc] init]autorelease];
  myPickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  myPickerView.showsSelectionIndicator = YES;
  myPickerView.delegate = myPickObj; // ここを self 以外にするにはどうすればいいのか悩んでた
  myPickerView.dataSource = myPickObj; // 単にオブジェクト作って与えるだけで問題なかった
  [myPickerView setTag:123]; // 先日 #hidev で教わった、addSubview したビューを後で拾うためのタグ付け
  [self.view addSubview:myPickerView];

    // ボタンを作って配置
  UIButton* button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  button1.frame = CGRectMake(0,300,200,50);
  [button1 setTitle:@"Pickeeeeer" forState:UIControlStateNormal];
  [button1 addTarget:self action:@selector(showPickerSelected:) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:button1];
}

-(void)showPickerSelected:(id)sender{
    // タグでビューからピッカーを引っ張る
  UIPickerView * myPickerView = (UIPickerView *)[self.view viewWithTag:123];
  NSInteger row = [myPickerView selectedRowInComponent:0];
  NSString * message = [[NSString alloc] initWithFormat:@"row : %d",row];
  
  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"選択したもの"
                                                  message:message
                                                 delegate:self
                                        cancelButtonTitle:@"OK"
                                        otherButtonTitles:nil];
  [alert show];
  [alert release];
  [message release];
}

使いどころもよくわからないまま書いたので、これの出番が本当にあるのかどうかはわからないけど、UIPickerViewの実装雛形としては使える、はず。MyPickerにデリゲートとデータソースを追いやることで、既存のソースとごっちゃにならないでいいかな、とか。

ちょっとぐぐっただけだと、デリゲートとデータソースを持ったViewControllerを使うサンプルばかりで、どうやってデリゲートとデータソースを外部化すればいいのか全然わからなかったけど、先日書いたサブクラス化のイメージでオブジェクト化すればいいんじゃね?と思ったらうまくいったのでよかった。

実はもっとうまい方法あるんだよ、とかあったら教えてください。

言うまでもないけど、先日書いた記事のとおり、Build and Analyzeしてるので、たぶん問題ないと思うんだけど、コードの書きっぷりとか、思う所があればそれも。。。

UIButtonをコードで追加する

    // ボタンを作って配置
  UIButton* button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  button1.frame = CGRectMake(0,0,200,50);
  [button1 setTitle:@"押してください" forState:UIControlStateNormal];
  [button1 setTitle:@"押されました" forState:UIControlStateHighlighted];
  [button1 setTitle:@"押せません" forState:UIControlStateDisabled];
  [button1 addTarget:self action:@selector(showAlert:) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:button1];

書いたような気がしたけど書いてなかったようなので。

あんまり画像使うの好きじゃないっつーか、HTML&CSSなら後で調整すればいい話なんだけど、iPhoneネイティブアプリの場合は、画像のサイズ合わせ、位置合わせが必要なので、結構めんどくさく感じる。やることはそんなに変わらないけれども。

あと、UIControlStateDisabled の使い道はイマイチわからない。ドキュメントには押せないボタンは作るべきじゃないとか書いてなかったっけ?