読者です 読者をやめる 読者になる 読者になる

TitaniumMobile勉強記

Web系エンジニア向けのキャリアアドバイザーやってましたが現在はフリーランスで開発含めて色々やってます。技術ネタとしてはRuby/RailsとJavaScript関連(Node.js、Titanium)あたり

040-insertRowAfterは利用せずTableView.setDataを活用するやり方に切り替えました。

タイトルそのままですが、insertRowAfterにこれ以上固執してもうまくいかない気がしてきたので違うやり方を検討しました。

TableView.setData(data)を活用した処理の流れ

自分はまだ1.5.1使っているのでこのTitanium Mobile のAPIドキュメントを改めて読んでみました。

Arguments
data [array] data array of rows either as objects or row objects
properties [object] animation properties

いくつかのrowのオブジェクトが含まれた配列dataを引数にすればこのTableViewは利用出来るし、実際そのように使ってきました。

この配列dataに対して以下のオペレーションすれば、きっと自分がやりたいことが出来るとおもって取り組みました

  1. CreateTableViewクラスのTableViewのscroll&scrollEndイベントにより追加のエントリ情報を取得開始
  2. 上記イベントによりBloggerクラスのgetEntry()が呼び出される。(次に取得するべきページ数を引数に渡しておりその情報を元にしてサーバ上のエントリ情報が取得できる)
  3. getEntry()の処理が終わり、追加のエントリ一覧のオブジェクト生成されたら、CreateTableViewクラスのreceive()が呼び出される
  4. CreateTableViewクラスのmake()メソッドでTableViewのrowのオブジェクトを1つづつ生成して配列に格納
  5. 4.で生成したオブジェクトをローカルのキャッシュで利用出来るようにするため、別に用意した配列に格納
  6. 4.と5.の配列を結合
  7. 結合された配列をTableView.setData()の引数に渡して、TableViewを更新する

うーん、文章にするとなんかまとめきれないから、おおまかな処理としてこんな感じをイメージしていました。

//1ページ5件のエントリを読み込む
entryList1 = ['1','2','3','4','5'];
Titanium.App.EntryCache = [];
someClass.cached(entryList1); // Titanium.App.EntryCacheに ['1','2','3','4','5']が格納
var data = someClass.union(entryList1,Titanium.App.EntryCache) // 変数dataには['1','2','3','4','5']が格納される

//2ページ目
NextEntry = ['6','7','8','9','10'];
someClass.cached(NextEntry); // Titanium.App.EntryCacheに ['1','2','3','4','5','6','7','8','9','10']が格納
var data = xx.union(NextEntry ,Titanium.App.EntryCache) // 変数dataに ['1','2','3','4','5','6','7','8','9','10']が格納

結局こんな感じになりました

Titanium MobileはJavaScript 1.6, CommonJSがサポートされているというのをTitanium Mobile で幸せになるための3つのポイントで読んでいたのですが、JavaScript 1.6で配列の集合演算が出来るかどうか調べたことがないので、それっぽいものを取り入れつつこんな感じにしました

ブログの更新に時間がかかっただけでコードは昨日書き終えていました。

※このブログを書き始めてコード読み返していて気づいたのですが、ローカルのキャッシュ処理する際にブロガー毎にキャッシュするようにしてないから、この部分は別途書きなおす必要が有る。。

Titanium.App.EntryCache = [];
CreateTableView = function(){
  this.entry_list_length = 5; // ここは設定ファイルなどで書きだす必要あり
  this.list = [];
  this.pagenumber = 1;
  this.win = Ti.UI.currentWindow;
  this.tableView = Ti.UI.createTableView();
  this.pulling = false;
  this.reloading = false;
  this.init();
};
CreateTableView.prototype = {
  init:function(){
    /*
     TableViewのヘッダー部分や、その中のEventListenerは
     初回だけ生成すればOK。
     */
    this.makeTableHeader();
  },
  receive:function(entryList,current_page){
    var entries = this.make(entryList);
    this.pagenumber = current_page;
    return this.setData(entries);

  },
  make:function(entryList){
    for(var i=0;i<this.entry_list_length;i++){
      var entry =  entryList[i];
      this.list.push(this.makeRow(entry));
    }
    var data = this.cached(this.list);
    return data;
  },
  cached:function(array){
    var result = this.union(Titanium.App.EntryCache,array);
    Titanium.App.EntryCache = result;
    return Titanium.App.EntryCache;
  },

  makeRow:function(data){
    var self = this;
    var row = Ti.UI.createTableViewRow({
      hasChild:true,
      data:data,
      height:80
    });
    // 記事の詳細情報を表示
    row.addEventListener('click', function(e){
      var win = Titanium.UI.createWindow();
      var title = e.rowData.data.title;
      var html_body = e.rowData.data.html_body;
      var permalink = e.rowData.data.permalink;

      var webView =  Ti.UI.createWebView();
      webView.html = '<html><head><title>' + title + '</title></head>'
		       + '<body>' + html_body + '</body></html>';
      win.add(webView);
      Titanium.UI.currentTab.open(win,{animated:true});
    });

    var title = Ti.UI.createLabel(styles["titleLabel"]);
    title.text = data.title,

    row.add(title);
    var content = Ti.UI.createLabel(styles["contentLabel"]);
    content.text = data.html_body.replace(/<\/?[^>]+>/gi, "");
    row.add(content);
    return row;
  },
  makeTableHeader:function(){
    var self = this;
    var border = Ti.UI.createView(styles["border"]);
    var tableHeader = Ti.UI.createView(styles["tableHeader"]);
    var arrow = Ti.UI.createView(styles["arrow"]);
    var statusLabel = Ti.UI.createLabel(styles["statusLabel"]);
    var lastUpdatedLabel = Ti.UI.createLabel(styles["lastUpdatedLabel"]);
    tableHeader.add(border);
    tableHeader.add(arrow);
    tableHeader.add(statusLabel);
    tableHeader.add(lastUpdatedLabel);

    self.tableView.addEventListener('scroll',function(e){
      var offset = e.contentOffset.y;
      if (offset <= -65.0 && !self.pulling) {
	var t = Titanium.UI.create2DMatrix();
	t = t.rotate(-180);
	self.pulling = true;
	arrow.animate({transform:t,duration:180});
	statusLabel.text = "手を離すと更新";
      } else if (self.pulling && offset > -65.0 && offset < 0) {
	self.pulling = false;
	var t = Titanium.UI.create2DMatrix();
	arrow.animate({transform:t,duration:180});
	statusLabel.text = "Pull down to refresh...";
      }
    });

    self.tableView.addEventListener('scrollEnd',function(e){
      if (self.pulling && !self.reloading && e.contentOffset.y <= -65.0)  {
	self.reloading = true;
	self.pulling = false;
	arrow.hide();
	statusLabel.text = "更新しています...";
	self.tableView.setContentInsets({top:60},{animated:true});
	arrow.transform = Titanium.UI.create2DMatrix();
	self.beginReloading();
      }
    });
    self.tableView.headerPullView = tableHeader;
    return;

  },
  setData:function(data){
    this.tableView.setData(data);
    var win1 = Ti.UI.currentWindow;

    return win1.add(this.tableView);
  },
  nextPage:function(){
    this.pagenumber++;
    return this.pagenumber;
  },
  beginReloading:function(){
    var self = this;
    var next_page = self.nextPage();

    Titanium.API.info('NEXT PAGE IS ' + next_page);
    setTimeout(function(){
      self.tableView.scrollToTop();
      self.reloading = false;
    },2000);

    var blogger = new Blogger('hibi');
    blogger.getEntry(next_page);
    return;
  },
    /*
   配列の集合演算を行う下記ソースを参考
   http://blog.kaihatsubu.com/archives/001498.html
   */
  include:function(item, target) {
    for (var i = 0, len = target.length; i < len; i++) {
      if (item == target[i]) {
	return true;
      }
    }
    return false;
  },
  union:function(arrayA, arrayB) {
    var result = arrayA.concat();
    for (var i = 0, len = arrayB.length; i < len; i++) {
      if (!this.include(arrayB[i], result)) {
	result.push(arrayB[i]);
      }
    }
    return result;
  }

};