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

TitaniumMobile勉強記

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

048-エントリ一詳細ページのイベントハンドラの処理

Titanium Mobile javascript

前回のエントリでボタンクリック時のイベントハンドラの処理で実装しないといけない点がいくつかあるとメモしておきました。

一番新しい/一番古いエントリかどうか判定した上で、適切なボタンを配置

エントリ一覧画面生成する際に、Ti.UI.createTableViewRow()を利用しており、その中で下記のようなclickイベントハンドラを設定しています。

      // 記事の詳細情報を表示
      row.addEventListener('click', function(e){//});

e.indexで、エントリ一覧の何番目のエントリがクリックされたか取得出来ます。

e.indexは0,1,2....という感じになっているため、仮にエントリが10件表示されていて、一番上のエントリがクリックされたときにはe.indexは0が返り、一番最後のエントリがクリックされた時にはe.indexは9が返ります。

このe.indexの値をうまく使うことで、一番新しい・古いエントリの判断が出来そうです

ボタンクリックで、前のエントリ/次のエントリ情報を取得

これはまだ完全には実装しきれてないものの、こんな感じで取得できそうです。

  1. エントリ一覧画面からエントリ詳細ページに画面遷移する際に、iPhoneの画面上では表示させてないが、内部的には投稿日(post_date)を保持しているのでその値も渡す
  2. post_dateの前後の投稿日を、ローカルのキャッシュ情報に問い合わせる
  3. 前のエントリ(もしくは次のエントリ)の投稿日をControllerクラスのloadCache()の引数に渡して、そのエントリを取得

取得されたエントリ情報をWebView.htmlの値にセットして、エントリ情報を書き換え。同時にナビゲーションバーに配置したボタンも必要に応じてセットしなおす。

エントリ情報の書き換えはTi.UI.createWebView()のhtmlプロパティに値を再度代入すればOKっていうことに気づいたのでこれは意外とか簡単にできましたが、同時にナビゲーションバーに配置したボタンも必要に応じてセットしなおすのがちょっと厄介そうですぐに思いつかなかった。

現在のソースコード

この間までのソースで、エントリ一覧生成する画面と、エントリの詳細情報を生成する画面を1つのクラスで処理してましたが、見通しが悪くなったので分けることにしました。

//エントリ一覧を生成するクラス

EntryList = function(){
  this.list = [];
  this.pagenumber =1;
  this.tableView = Ti.UI.createTableView();
  this.pulling = false;
  this.reloading = false;
  this.init();
};
EntryList.prototype = {
  init:function(){
    /*
     TableViewのヘッダー部分や、その中のEventListenerは
     初回だけ生成すればOK。
     */
    this.makeTableHeader();

  },
  receive:function(entryList,current_page){
    var entries = this.make(entryList);
    this.pagenumber = current_page;
    this.setData(entries);

    return true;

  },
  makeRow:function(json){
    var self = this;
    var _row = [];
    var _json = eval(json);
    var len = _json.length;

    for(var i=0;i<len;i++){

      var row = Ti.UI.createTableViewRow({
	hasChild:true,
	data:_json[i],
	html_body:_json[i].html_body,
	height:80
      });
      // 記事の詳細情報を表示
      row.addEventListener('click', function(e){
	var title = e.rowData.data.title;
	var html_body = e.rowData.data.html_body;
	var permalink = e.rowData.data.permalink;
	var post_date = e.rowData.data.post_date;
	var current = e.index+1;
	var entry_summary = new EntrySummary(title,html_body,permalink,post_date,len,current);

      });

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

      row.add(title);
      var content = Ti.UI.createLabel(styles["contentLabel"]);
      content.text = _json[i].html_body.replace(/<\/?[^>]+>/gi, "");
      row.add(content);
      _row.push(row);

    }
    return _row;
  },
  makeSerachBar:function(){
    var searchBar = Ti.UI.createSearchBar({
      showCancel: false
    });
    searchBar.addEventListener('change', function(e) {
      return e.value;
    });
    searchBar.addEventListener('return', function(e) {
      return searchBar.blur();
    });
    searchBar.addEventListener('cancel', function(e) {
      return searchBar.blur();
    });
    this.tableView.search = searchBar;
    this.tableView.filterAttribute = 'html_body';
    return true;
  },
  makeTableHeader:function(){

    var self = this;
    self.makeSerachBar();
    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,current_page){
    this.pagenumber = current_page;
    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);

    setTimeout(function(){
      self.tableView.scrollToTop();
      self.reloading = false;
    },2000);

    var entry = new Entry('hibi');
    entry.getEntry(next_page);
    return;
  }
};
//エントリ詳細情報を生成するクラス
EntrySummary = function(title,html_body,permalink,post_date,number_of_entry,current_page){
  this.title = title;
  this.html_body = html_body;
  this.permalink = permalink;
  this.post_date = post_date;
  this.number_of_entry = number_of_entry;
  this.current_page = current_page;
  this.counter = current_page + '/' + this.number_of_entry + '件';
  this.button_bar = Titanium.UI.createButtonBar({
      style:Titanium.UI.iPhone.SystemButtonStyle.BAR
  });
  this.webView =  Ti.UI.createWebView();
  this.init();
};
EntrySummary.prototype = {
  init:function(){
    var win = Titanium.UI.createWindow();
    this.webView.html = '<html><head><title>'
      + this.title
      + '</title></head>'
      + '<body><h1>'
      + this.title
      + '</h1><hr />'
      + this.html_body
      + '</body></html>';
    win.add(this.webView);

    if(this.current_page===1){
      this.firstPageButton();
    } else if(this.current_page===this.number_of_entry){
      this.lastPageButton();
    } else {
      this.middlePageButton();
    }
    win.setTitleControl(this.button_bar);

    Titanium.UI.currentTab.open(win,{animated:true});
  },
  firstPageButton:function(){
    var self = this;
    self.button_bar.labels= [self.counter,'次'];
    self.button_bar.addEventListener('click', function(e){
    // e.indexにクリックされたボタンのindexがセットされます
    if (e.index===1){
	  self.webView.html = 'next click refresh!!';
	}
    });
  },
  lastPageButton:function(){
    var self = this;
    self.button_bar.labels= [self.counter,'前'];
    self.button_bar.addEventListener('click', function(e){
      if (e.index===1){
	var result = self.refresh();
	self.webView.html = '<html><head><title>'
      + result[0].title
      + '</title></head>'
      + '<body><h1>'
      + result[0].title
      + '最後のページまできた!!!!'
      + '</h1><hr />'
      + result[0].html_body
      + '</body></html>';

      }
    });
  },
  middlePageButton:function(){
    var self = this;
    self.button_bar.labels= [self.counter,'前','次'];
    self.button_bar.addEventListener('click', function(e){
      if (e.index===1){
	self.webView.html = 'before button click!!';
      }else if(e.index===2){
	self.webView.html = 'next butto click!!';
      }
    });
  },
  searchNextEntry:function(post_date){
    //仮実装
    return '2006-09-14T16:30:00Z';
  },
  searchBeforeEntry:function(post_date){
    //仮実装
    return '2006-09-06T01:21:00Z';
  },
  refresh:function(){
    //
    var c = new Controller('hibi');
    var post_date = this.searchBeforeEntry(this.post_date);
    var result = c.loadCache(post_date);
    return result;
  }
};