40歳からのキャリアチェンジ

20代はエンジニア・PM、30代はWeb系エンジニア向けのキャリアアドバイザー。40代の今はフリーランスで開発含めて色々やってます。技術ネタとしてはRuby/RailsとJavaScript関連あたり

画面下部までスクロールしたタイミングでTableViewの情報更新する

RSSリーダーtwitterクライアント等でよくあるUIとして、画面下部までスクロールしたタイミングで、サーバ(もしくはローカルにキャッシュしてある)情報を追加して読み込むというのがあるかと思います。

自分のこれまで作っていたアプリでは、このようなUIを実現しようと一時期TableViewのinsertRowAfterが活用できないか模索していた時期があるのですが、ドキュメント通りに引数指定しても、思ったような動作しなかったので諦めていたのですが、この前のTitanium Meetup中にkitchen Sinkのサンプルを何気なく眺めていたら、APIリファレンスの記述が間違っていたようです。。

  • 誤:insertRowAfter (index, row, properties)
  • 正:insertRowAfter (row,index, properties)

APIリファレンスが必ずしも正確でないのは、過去何度か経験していたのですが、何とも言えない懐かしさを感じましたww

使い方がわかったこともあって、折角なので自分がつくっているアプリでも実装できないか試すことにしました。

あすなろブログReader

こういうエントリ一覧に対応するクラスとしてEntryListというのを作っています。

関連しそうなソースを抜粋して、簡単に解説しておきます

EntryList = function(blogger){
  this.blogger = blogger;
  this.tableView = Ti.UI.createTableView();
  this.last_update_date = null;
  this.init();
};
EntryList.prototype = {
  init:function(){
    this.makeTableHeader();
    this.tableViewEvent();
  },
  tableViewEvent:function(){
    var self = this;
    this.tableView.addEventListener('scrollEnd',function(){
      var last_index = self.tableView.data[0].rows.length - 1;
      var post_date = self.tableView.data[0].rows[last_index].data.post_date;
      var controller = new Controller();
      /*
       controller.findメソッドで得られる値は、引数に指定した投稿日の
       エントリになるように実装してる。そのためlimit句に2を設定した上で配列の先頭
       から2番目の値を読み込む
       */
      var limit = 2;
      var entries = controller.find(post_date,limit,self.blogger);
      if(entries[1]){
        var row = self.makeRowLabels(entries[1]);
        self.tableView.insertRowAfter(last_index,row,{animationStyle:Titanium.UI.iPhone.RowAnimationStyle.DOWN});
      }
    });
  },
  makeRowLabels:function(item){
    var self = this;
    var last_update = new Date(item.post_date);
    var len = self.tableView.data[0].rows.length + 1;
    self.last_update_date = last_update.getTime()/1000;
    var row = Ti.UI.createTableViewRow({
      hasChild:true,
      data:item,
      html_body:item.html_body,
      height:80
    });

    // 記事の詳細情報を表示
    row.addEventListener('click', function(e){
      var current = e.index+1;
      var entry_summary = new EntrySummary(e.rowData.data,len,current,self.blogger);
    });

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

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

    var post_date = Ti.UI.createLabel(styles["postDate"]);
    post_date.text = "投稿日時:" + item.post_date.match(/\d{4}\/\d{2}\/\d{2}/gi,"");
    row.add(post_date);
    return row;
  },
  makeTableHeader:function(){
    //省略
  }
  1. クラス初期化時に、init()が呼び出されるようにしているのですが、そこでtableViewEvent()というメソッドを実行してます
  2. 画面下部までスクロールしたタイミングでTableViewの'scrollEnd'イベント発火される
  3. 表示されているエントリ一覧の一番古い投稿日を取得(変数var post_dateの所)して、その値を引数にしてControllerクラスのfindメソッドが呼び出される。(得られる結果は引数に指定した投稿日を含めて、過去2件分のエントリ情報を配列として得られるように実装してます)
  4. 配列の先頭から2番目の値が存在しているか確認した上で、rowのデータを生成するmakeRowLabelsを実行し、得られた結果をinsertRowAfterの引数に渡してTableViewの情報を追加する(理由としては一番古いエントリの投稿日を引数に指定すると、それよりも古いエントリ情報は存在しないため配列の先頭から2番目の値は存在しない)

自分がつくっているアプリの特性考えると、insertRowAfterを使えればいいなぁーと思っていたので、これでまた1つ成長した感じがします