search
LoginSignup
34
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

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

半年前に仕事で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();
}

ここを参考にしました。

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

以上

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
34
Help us understand the problem. What are the problem?