半年前に仕事でTitaniumMobileを使用したので、ハマった所やTipsを幾つか紹介。
多分にオレオレプラクティスになっているので、参考程度にお願いします。
Titanium mobileでNavigationWindowで数階層下にあるViewからRootWindowまで一気に戻る。
iOSのnavigationControllerでpopToRootViewControllerAnimatedみたいな操作が出来ないかなーと思って、色々調べたけど、標準ではAPIが用意されていないみたい。
したがって、遷移してきた画面をグローバル変数として配列で保持して、それをFor文で一気にクローズする方法を試してみました。
次の階層に遷移する際にグローバル変数の配列に画面を格納。
var nextView = Alloy.createController('nextController').getView();
// 他のControllerからアクセスできるようにグローバル変数に値を格納。
// Ti.App.NavigationViews = [] を予め定義しておくこと。
Ti.App.NavigationViews.push(nextView);
// 予め、Ti.App.NavigationRootView = $.controller_name でルートビューを入れておく。
Ti.App.NavigationRootView.openWindow(nextView);
Rootに戻るときは下記を記述。
for ( i = 0; i < Ti.App.NavigationViews.length; i++) {
closeView = Ti.App.NavigationViews.length[i];
Ti.App.NavigationViews.length.closeWindow(closeView);
}
Ti.App.NavigationViews = [];
現在表示しているViewは一番最後に消すのがミソ。
Titanium Mobileでデータベースを参照したい(iOS version)
SQLiteのクライアントソフトは「Lita」がおすすめ。
手順
-
Titanium stadio で iOSシュミレータ立ち上げ
-
Litaを起動。
-
「DBFile」でビルドしたアプリのsqlファイルまで移動。
ちなみにAlloyで作成した場合のファイルパスはデフォルトで
~/Library/Application Support/iPhone Simulator/<iOSのバージョン>/Applications/<アプリのGUID>/Library/Private Documents/_alloy_.sql
にあります。
- 「open」を押下。
これで見られます。
Titanium Mobileでトランザクション(trunsaction)。
AlloyでトランザクションをやってくれるAPI等はまだ提供されてないみたい。
なので、下記な感じで書きましょう。
var db = Ti.Database.open('_alloy_');
db.execute('BEGIN TRANSACTION');
db.execute(String.format("insert into table_name (title) values ('%s')", "title_name"));
// Alloy.createModel(ほげほげ).save() みたいなことやってもトランザクションの対象にはなりません。
db.execute('COMMIT;');
db.close();
【問題点】
Alloy使ってる人は、上記でinsertしたレコードにはalloy_idが入らないので、注意してください。
Titanium Mobile にて 特定のテーブルのレコードを全消去する
Truncateがなかったので、パフォーマンスがアレですが、下記でテーブルのレコードが全部、消せます。
var collection = Alloy.createCollection('model_name');
collection.fetch();
while (collection.length) {
collection.at(0).destroy();
}
ここを参考にしました。
Titanium Mobile(Alloy)でModelにID属性を定義したい
configあたりで下記のように書くと出来ました。
exports.definition = {
config: {
columns: {
// 制約をここに書く。
"id": "integer PRIMARY KEY AUTOINCREMENT",
"column001": "text",
"column002": "text"
},
adapter: {
type: "sql",
collection_name: "AnswerRecord",
idAttribute: 'id' // 「id」がID属性であることを宣言。
},
},
// 以下、モデルの定義
Titanium Mobile で整形した日付を取得する
SQLiteではデータ型が存在しないので、text型で保存するのが定番らしい。
Alloyには日付操作ライブラリ「moment.js」が提供されているので、それを使って、text型の日付が取得できる。
var moment = require('alloy/moment');
var now = moment() // これで現在日付が取得可能。
var foematedNow = now.format("YYYY-MM-DD HH:mm:ss");
console.log(foematedNow); // 2014-01-21 20:01:87
SQLiteの関数 date(), time(), datetime() の書式に合わせるのが、良いらしいので、上記ではdatetime()の書式に合わせて、記述しました。
これでソートもうまく出来るそうな。(未検証)
【参考にしたサイト】
Alloyの公式ドキュメントによるmoment.jsの説明
http://docs.appcelerator.com/titanium/latest/#!/api/Alloy.builtins.moment
moment.jsの公式サイト
http://momentjs.com/
Titanium Mobile で navigation window にて「戻る」ボタンを非表示
// ウィンドウオブジェクトのidがwindow
$.window.leftNavButton = Ti.UI.createLabel({text:' '});
カッコ悪いけど、これが一般的っぽい。
Titanium Mobileでtsvファイルから初期データを入れる。
tsvファイルから初期データ読み取って、テーブルに入れる方法を考えた。
■ tsvファイル
1行目 | column1 | column2 | column3 |
---|---|---|---|
2行目 | aaa | AAA | 111 |
3行目 | bbb | BBB | 222 |
4行目 | ccc | CCC | 333 |
5行目 | ddd | DDD | 444 |
/**
* tsvファイルからインスタンスを作成し、データ作成。
* 1行目はテーブルのカラム名を記述。
* 2行目以降は1行目の同列のカラムのデータを記述。
* @param {Object} filePath tsvファイルまでのpath
* @param {Object} modelName モデル名
*/
function insertInititalData(filePath, modelName) {
var fileData = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory, filePath);
var fileContents = fileData.read().toString();
var fileLines = fileContents.split(/\r\n|\r|\n/);
// カラムを取得
var tableColumns = fileLines[0].split('\t');
// 2行目以降のデータでインスタンス化
for (var i = 1; i < fileLines.length; i++) {
var modelLineWords = fileLines[i].split('\t');
var params = _.object(tableColumns, modelLineWords);
var model = Alloy.createModel(modelName, params);
model.save();
}
}
ファイルの末尾に空行が入ってるとうまく動かないと思うので、そこだけ除去してください。
上記の方法の利点はカラムが増えても、改修不要な点。
JavaScriptもさることながら、Underscore.jsがかなり使える。
Ruby on Rails 使ってる人はすぐに慣れるはず。
Titanium Mobile で定数管理
Alloyで定数を管理するファイルを作成して、グローバル変数で読み取る方法を考えた。
■ 定数ファイル (app/lib/constants.js)
module.exports = {
config : {
code001 : "XXXXXXXXXX",
code002 : "YYYYYYYYYY"
}
};
■ alloy.js
Alloy.Globals.constants = require("constants");
■ index.js
console.log(Alloy.Globals.constants.config.code001); // XXXXXXXXXX
2014-02-01 追記
module.exports を exportsにしてしまうと、Androidでは動きませんので注意。
Titanium Mobileでナビゲーションバーの半透明を無くす方法
class = "container"として、
".container" : {
barColor : "green",
translucent : false
}
Titanium Mobileでrequireを使用して、モジュールを読み込む方法(Android/iOS)
定数管理したJSファイルを読み込もうとしたんだが、
constants.js (app/lib/constants.js)
exports = {
test : {
name : "hogehoge"
}
}
alloy.js
Alloy.Globals.constants = require("constants");
console.log(Alloy.Globals.constants.test.name); // point1
これだと、point1でiPhoneでは、値が出力されるが
何故かAndroidだと動かない。
(constantsが{}になっていて、constants.test.nameがundefinedになる。)
これを下記のように直したら、動いた。
constants.js (app/lib/constants.js)
module.exports = {
test : {
name : "hogehoge"
}
}
CommonJSやmodule.exportsやexportsがまだ、そこまで理解できてないので、時間を見つけて、調べる予定。
Titanium Mobile でOSのバージョンを取得する。(iOS/Android)
Titanium MobileでOSのバージョンを数値で取得(Android 2.3.3で試す場合)
var version = parseInt(Ti.Platform.version.slice(0,2), 10);
console.log(version) // 2.3
OSのバージョンは「4.0.4」とか「2.3.3」と言った表記なので、
そのまま数値にすると「NaN」になってしまう。
したがって、「4.0」や「2.3」などに整形する必要がある。
Titanium MobileにてBackボタンを無効化する。(Android/iOS)
両OSでBackを無効化したかったので調べた。
色んな所で使用したかったため、グローバル関数にしてみた。
alloy.js
Alloy.Globals.disableBack = function(windowObj) {
if (OS_IOS) {
// iOSはナビゲーションバーを使用している前提。
windowObj.leftNavButton = Ti.UI.createLabel({
text : ' '
});
} else if (OS_ANDROID) {
windowObj.addEventListener('android:back', function() {
// 何もしない。
});
}
};
index.js
Alloy.Globals.disableBack($.index) // windowオブジェクトを引数にする。
これで、動きました。
Titanium MobileのSQLite3でBooleanを使用する際の注意点(Android/iOS)
SQLite3のカラム型でBoolean型は存在しないので、代わりにInteger型を使用することになると思うのですが、iPhoneだとtrueを設定しても、SQLiteでは自動的に「1」が登録されます。
しかし、Androidでは自動的に変換は行われず、nullになってしまうので、注意が必要です。
素直にtrueなら1、falseなら0を使うようにしましょう。
Titanium MobileでBase64でエンコードされた画像を表示。(Android/iPhone)
サーバーからBase64でエンコードされた画像データを表示したかったのだが、AndroidとiPhoneでハマったので、メモ。
// 画像取得先のURL(仮のもの)
var url = "http://hogehoge.com/getImage";
var xhr = Ti.Network.createHTTPClient({
onload : function(e) {
// レスポンスは{"img_string" : "Base64でエンコードされた文字列"}で返ってくるものとする。
var response = JSON.parse(this.responseText);
var img = Ti.UI.createImageView({
image : Ti.Utils.base64decode(response["img_string"]),
width : Ti.UI.FILL
});
// 以下、imgを画面に表示する処理を書く。
},
onerror : function(e) {
// 失敗した時の処理
},
timeout : 3000 // 3秒経過したら、タイムアウト
});
xhr.open("GET", url);
xhr.send();
ここで注意したいのは、最初、createViewのbackgroundImageで表示しようと思ってたのだが、何故かandroidでは表示できなかった。
Ti.UI.createView({
backgroundImage : Ti.Utils.base64decode(response["img_string"]),
width : Ti.UI.FILL
});
仕方ないので、createImageViewでやったら、AndroidとiPhoneの両方でうまくいったので参考までに。
-
ImageVIewのimage
http://docs.appcelerator.com/titanium/3.0/#!/api/Titanium.UI.ImageView-property-image -
ViewのbackgroundImage
http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.UI.View-property-backgroundImage
リファレンスはしっかり見ないと(反省)
Titanium Mobileで親Viewにイベント伝搬させない
親Viewと子Viewに同じClickイベントを設置した時、デフォルトでは子ViewのClickイベント後、親Viewのclickイベントが発生します。
var parentView = Ti.UI.createView({
height : Ti.UI.FILL,
width : Ti.UI.FILL
});
parentView.addEventListener('click', function(e){
console.log("parent click!");
});
var childView = Ti.UI.createView({
height : Ti.UI.FILL,
width : Ti.UI.FILL
});
childView.addEventListener('click', function(e){
console.log("child click!");
});
parentView.add(childView);
click時
child click!
parent click!
これを制御するには、bubbleParentプロパティを制御します。
var childView = Ti.UI.createView({
height : Ti.UI.FILL,
width : Ti.UI.FILL,
bubbleParent : false
});
click時
child click!
これで親プロパティのイベントが発生させないようにできます。
Titanium MobileでAlloyでアプリ名を日本語にする
- プロジェクトのappフォルダと同階層に「i18n」フォルダを作成
- i18n/ja/app.xmlを作成。
- app.xmlを編集
<?xml version="1.0" encoding="UTF-8" ?>
<resources>
<string name="appname">日本語アプリ名</string>
</resources>
これで、ビルドするとiOSとAndroidの両方でアプリ名が日本語になります。
Titanium MobileのiOS用スプラッシュ画像はちゃんとしたpng拡張子を使おう
お前は何を言って(略
Titanium MobileでiOS用のスプラッシュ画像を使うためにgifファイル用意してたんだが、pngしか設定できないようなので、拡張子をリネームして、下記のファイルを設定した。
- Default.png
- Default-568h@2x.png
- Default@2x.png
iOS Simulatorは指定したインチに沿って、使用するスプラッシュ画像を選択してくれてたんだけど、
いざ、実機で確認すると「Default-568h」を使用すべきところを「Default」しか使ってくれず、
そのおかげで、アプリ自体、640 × 960 サイズになって、上下に黒い部分の領域ができてしまっていて、この解消方法がわからなかった。
解消方法は素直に gif → png にちゃんと画像変換すれば、設定出来ました。
今考えると、拡張子リネームしただけで、変換できるか!!って言われそうだけど、
寝てなかったから頭が回ってなかったんです。(言い訳)
AlloyのControllerで作成したViewにtssの定義を適応させる。
Controllerで作成したViewはそのままだとtssで定義したclassを当てはめることができないので、Dynamic Styleを使うと良い。
index.tss
// Button 全てに以下の定義が適用。
"Button" : {
backgroundColor : "black"
}
".buttonEnabled" : {
opacity : 1.0,
touchEnabled : true
}
".buttonSize" : {
width : "50dp",
height : "50dp"
}
index.js
var button = Ti.UI.createButton();
var style = $.createStyle({
apiName : "Button" // tssの「Button」を指定。
classes : ["buttonEnabled", "buttonSize"] // 複数のClassを指定。
});
button.applyProperties(style); // tssのクラスが適用される。
【参考URL】
http://docs.appcelerator.com/titanium/3.0/#!/guide/Dynamic_Styles
Alloyで画面下部に広告スペース載せるときのレイアウト見本
画面下部に高さ50の広告エリアを表示する際の指定方法が結構、ハマったので書いておく。
Viewファイル
<Alloy>
<Window>
<View class="notAdViewArea">
</View>
<View class="adViewArea">
</View>
</Window>
</Alloy>
Tssファイル
".notAdViewArea" : {
backgroundColor:"green",
bottom:"50dp"
}
".adViewArea" : {
backgroundColor:"yellow",
height:"50dp",
bottom:"0dp"
}
以上