ハイブリッドアプリで無限スクロールを簡単に実現する方法
アプリでよく使われる機能として「スクロールすると無限に情報を表示できる」無限スクロール(infinite scroll)があります。 フェイスブックやツイッターのようにタイムラインがあるアプリによく使われています。 この無限スクロールは、安易にハイブリッドアプリで作成すると、アプリが重くなり、強制終了されてしまいます。 画面に表示されていない要素をそのまま保持した状態にしていると重たくなる原因となりますので、画面の外にある要素は削除する必要があります。
無限スクロール機能を提供している「onsenUI」
無限スクロール機能を基本機能として提供しているフレームワーク、onsenUIを紹介します。 onsenUIはハイブリッドアプリでよく使われるPhoneGapやCordovaに最適化して作成されたコンポーネントです。 monacaというクラウドベースの開発プラットフォームを提供しているアシアル株式会社が作成したもので、オープンソースなので無料で利用することができます。 世の中にはハイブリッドアプリのためのフレームワークが多数ありますが、onsenUIは日本企業が提供しているため、日本語のドキュメントが多く、理解がしやすいのが助かります。 余談ですが、onsenUIの名前の由来は「Single Page App → SPA → 温泉 → onsen」という連想からのネーミングだそうです。
ons-lazy-repeat
onsenUIが提供している無限スクロールの機能は、ons-lazy-repeatコンポーネントで使用できます。
このコンポーネント内で描画されるアイテムのDOM要素の読み込みは、画面に見えそうになった時まで自動的に遅延され、 画面から見えなくなった場合にはその要素は動的にアンロードされます。 このコンポーネントを使うことで、パフォーマンスを劣化させること無しに巨大な数の要素を描画できます。
引用元: http://ja.onsen.io/reference/ons-lazy-repeat.php
スクロールで画面に表示されそうになった要素は、画面外で描画され、準備状態になります。 スクロールされて、画面外の準備領域よりも外に移動した要素は、削除されます。 そうすることで、常に一定のアイテム数だけが描画されることになり、要素が何百万や、それこそ無限にあったとしても、アプリが落ちるようなことにはなりません。
動きとしてはこんな具合です。
See the Pen Lazy Repeat (Infinite list) by Onsen & Monaca (@onsen) on CodePen.
無限スクロールの情報をリフレッシュする
無限スクロールで情報を表示することはできましたが、リロードボタンでリロードできるようにしたいところです。 内容をリロードするには、configureItemScope の値を変更するだけで可能です。
$scope.MyDelegate = { configureItemScope: function(index, itemScope) { // repeatItemの中身を替えればリフレッシュされる itemScope.item = repeatItem[index]; }, calculateItemHeight: function(index) { return 110; // 要素の高さをpx指定 }, countItems: function() { return repeatItem.length; // 全体の要素数 }, destroyItemScope: function(index, scope) { // 画面外に移動した要素を削除 } };
最後に
onsenUIには、無限スクロール以外にも、画面を下に引っ張り、指を離すと更新するプルフック(pull hook)や、ダイアログやモーダルの機能が提供されています。 私自身、monacaでアプリを作成したとき、無限スクロールの情報をプルフックしたときにリフレッシュするという処理を実装する時に、onsenUIのおかげでずいぶんと楽に実装ができました。 当初は、angularJSのng-repeatを使って要素をスクロールするようにして作ってみたのですが、やはり数が膨大になるとアプリが落ちてしまいました。 幸いにも、私はmonacaでアプリを制作しており、しかもmonacaにはonsenUIが同梱されています。なので、ng-repeatで記述していた箇所をons-lazy-repeatに書き換えることで解決しました。