Edited at

AndroidとiOS開発をTitanium Mobileでやった時に得られたTipsまとめ

More than 5 years have passed since last update.

半年前に仕事で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」がおすすめ。


手順


  1. Titanium stadio で iOSシュミレータ立ち上げ


  2. Litaを起動。


  3. 「DBFile」でビルドしたアプリのsqlファイルまで移動。

    ちなみにAlloyで作成した場合のファイルパスはデフォルトで



    ~/Library/Application Support/iPhone Simulator/<iOSのバージョン>/Applications/<アプリのGUID>/Library/Private Documents/_alloy_.sql



    にあります。


  4. 「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();
}

ここを参考にしました。

https://developer.appcelerator.com/question/148160/destroy-model-in-alloy-doesnt-seem-to-be-working


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の両方でうまくいったので参考までに。

リファレンスはしっかり見ないと(反省)


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しか設定できないようなので、拡張子をリネームして、下記のファイルを設定した。

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"
}

以上