R&Dトレンドレポート

第23回 マッシュアップ開発のススメ[その9:モバイル版アプリケーションを作ろう③]

この記事を読むのに必要な時間:およそ 3 分

前回作成したテーブルビューは起動時にデータを取得し,そのデータを表示するだけでしたが,今回はテーブルビューの表示を動的に更新して切れ目なくリストが見られるようにします。

動的な更新

こういう動きを実装したい。でもまだコツがつかめない…。こんな時はキッチンシンクです。

キッチンシンクはiPhoneアプリやAndroidアプリの基本的な動作をTitaniumで実装したサンプル集です。実際に動作させた上でソースも確認できるのが非常にありがたいですね。

では今回の動作を実現するにはどのサンプルが必要でしょうか。example/table_view_dynamic_scroll.jsがそのサンプルのようです。

このサンプルの動作は,

  1. テーブルビューをスクロールさせる
  2. 画面上のリスト(ROWデータ)が終わりそうになったら,
  3. テーブルビューに次のリストを追記する。

という動作です。

この動きによって画面をスクロールさせるだけで,次々とリストが更新されます。直感的にもわかりやすく優れたUIの動作と思います。

それでは該当ソースの部分を見てみましょう。

var updating = false;
var loadingRow = Ti.UI.createTableViewRow({title:"Loading..."});

//更新開始関数
function beginUpdate()
{
  updating = true; //更新フラグをONにする(多重に動作しないように)
  navActInd.show(); //ナビバーのスピナーを表示

  tableView.appendRow(loadingRow); // “読み込み中”のローをテーブルビューに追記

  // just mock out the reload
  setTimeout(endUpdate,2000);  // 2秒後にendUpdate()を起動。

}

// 更新終了関数
function endUpdate()
{
  updating = false; // 更新フラグをOFF

  // “読み込み中”のローを削除する。
  tableView.deleteRow(lastRow,
    {animationStyle:Titanium.UI.iPhone.RowAnimationStyle.NONE});

  // simulate loading
  for (var c=lastRow;c<lastRow+10;c++)
  {
    tableView.appendRow({title:"Row "+(c+1)},
       {animationStyle:Titanium.UI.iPhone.RowAnimationStyle.NONE});
  }
  lastRow += 10;


  navActInd.hide(); //スピナーを非表示に。
}

var lastDistance = 0; // 距離の初期値

// テーブルに対するスクロールイベントハンドラを追加。
tableView.addEventListener('scroll',function(e)
{
  var offset = e.contentOffset.y; // テーブルの見えてる部分の位置(上)
  var height = e.size.height; // テーブルの見えてる部分のサイズ(画面内の)固定
  var total = offset + height; // テーブルの見えている部分の位置(下)
  var theEnd = e.contentSize.height; // テーブルのサイズ
  var distance = theEnd - total; // テーブル全体の底辺からの今見えてる部分の距離(最底辺だと0)

   // 距離が前回の距離より短くなっている=テーブルが上に向かってスクロールしている。
  if (distance < lastDistance) 
  {
    var nearEnd = theEnd * .75;

    // 更新中じゃなく,テーブルのサイズの75%以上までスクロールしたら。
    if (!updating && (total >= nearEnd))
    {
      beginUpdate();  //更新を開始する!
    }
  }
  lastDistance = distance; //現在の距離を保存する。
});

ここの動作のポイントとしては,スクロールイベントのハンドリング部分にあります。

  • テーブル全体の大きさと今現在見えている部分のサイズを比較
  • テーブル下部へ近づいていることを検知
  • 全体の75%の部分までスクロールしたらデータの更新を行う

という一連の動作によりスムーズな操作を実現しています。

ここの部分の動きは変更する必要がありません。強いて変更するなら75%で判別している部分がテーブルのサイズが動的に大きくなることを考えると適切ではないと思われます。ここは固定のピクセル値に直してもよいかもしれません。

図1 iPhoneとテーブルにおける各変数の意味

図1 iPhoneとテーブルにおける各変数の意味

紫の線で表現したのがテーブルビューの全体です。見えている部分と隠れている部分のサイズがどのように取得できるかがよくわかると思います。distance値がnピクセル以下になったら次のデータを取得する,という方法でも問題がないことがわかりますね。

もうひとつのポイントはソース内の文字色を変えた部分です。こちらはサンプルですので模擬的に2秒後にendUpdate()が発火するように書いてありますが,実際にはHTTPClientでサーバからデータを取得し,取得後のコールバック関数でendUpdate()を起動させる。という風に書かないといけません。

また,HTTPClientでのデータの取得も次のn件という風にページャ的に取得する必要があります。

ここではいろんな実装方法があるかと思いますが,HTTPClientのリクエストを行うクラスを作成し,データを取得する関数を呼び出すたびにページャがインクリメントされていくようにしてみます。

var MovieData = function(tableView)
{
  this.rows=10; //1ページ毎の行数
  this.page=1;  //初期ページ数
  this.lastRows = 0;
  this.tableView =  tableView;
}
MovieData.prototype = {
  getNext: function()
  {
        //動画一覧を取得するAPI
        var URL=”http://example.com/jikkyou/list.cgi?page=”+this.page+”&rows=”+ this.rows; 

        // Titaniumで用意されているHTTPClient関数
        var c = Titanium.Network.createHTTPClient();
        var json;
        var row;
        c.open("GET", URL); // GETで取得
        c.send();
        c.onreadystatechange = function() // HTTPClientのステータスが変わったら。。(コールバック関数)
        {
                if ( c.readyState == 4 )
                {
                        if ( c.status == 200 )
                        {
                                this.tableView.deleteRow(this.lastRows, { animationStyle:Titanium.UI.iPhone.RowAnimationStyle.NONE });;
                                json = eval("("+c.responseText+")");
                                for( i = 0; i < json.data.length; i++ )
                                {
                                        //JSONからデータを取得
                                        var data = json.data;
                           // tableRowdデータの生成・・
                  …略…
                                               this.tableView.appendRow(row);
                                               this.lastRows++;
	                }
                     this.page++;
                     f(); //コールバック関数を起動(endUpdate)	            }
	      }
	}
}

このクラスを外部ファイルに保存します。名前はMovieData.jsにしましょう。

titaniumで外部ソースを使う場合は次のようにします。

Titanium.include("MovieData.js”);

var md = new MovieData(tableView);

このようにソースの先頭の方でインクルードします。ファイルへのパスはカレントディレクトリからの相対パスで大丈夫です。

最初の文字色の違う部分を以下のように差し替えます。

md.getNext(endUpdate);

2つめの文字色の違う部分を削除します。

ちょっと付け焼き刃的なコーディングになってしまった気がしますが,以上で動的なテーブルビューの更新を行うことが可能となりました。

スクロールさせるたびに新たにデータが読み込まれるのはなかなか快感です。こういったUIをいち早く取り入れたiPhoneはさすがですね。

それでは次回はテーブルビューからの画面遷移についてご紹介します。

著者プロフィール

脇本武士(わきもとたけし)

都内中小IT企業(メイサンソフト(株))に所属。某大手自動車会社でのシステム開発,運用を経て,現在は研究開発部署に席をお借りしています。DB周りの保守サポート,ウェブ技術開発を主に手がけてきました。現在は大規模計算フレームワークの活用とKVSに注目しています。普段はOS Xを使用していますが一番よく使うアプリはTerminalです(笑)。

R&Dトレンドレポート(てくらぼ)

コメント

コメントの記入