2016年10月11日火曜日

カメラアプリを作ろう!

Cordovaを使ってカメラアプリを作成します。
以下の手順にしたがって進めてください。
  1. Monacaで新規プロジェクトを作成する
  2. テンプレートとして Onsen UI V1 Minimum を選択する
  3. 「設定」→「JS/CSSコンポーネントの追加と削除」でjQuery (Monaca Version)をインストールする
  4. index.htmlを以下のように修正する。

list.html

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <meta http-equiv="Content-Security-Policy" content="default-src * data:; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'">
    <script src="components/loader.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <link rel="stylesheet" href="css/style.css">
    <script>
        ons.bootstrap();

        var images = null;
        // ギャラリーから画像のパスを取得する
        var getPictureFromGallery = function(onSuccess) {
            var options = {
                quality: 50,
                sourceType : Camera.PictureSourceType.PHOTOLIBRARY,
                destinationType: Camera.DestinationType.FILE_URI
            };
            
            navigator.camera.getPicture(function(imageURI) {
                onSuccess(imageURI);
            }, onFail, options);
        };
          
        // 写真を撮影して保存する
        var getPictureFromCamera = function(onSuccess) {
        
            // デバイスのカメラアプリを利用して撮影し保存
            var options = {
                sourceType : Camera.PictureSourceType.CAMERA,
                saveToPhotoAlbum: true,
                correctOrientation:true,
                destinationType: Camera.DestinationType.FILE_URI 
            };
            
            // カメラアプリを起動し、撮影して保存
            navigator.camera.getPicture(function(imageURI) {
                onSuccess(imageURI);
            }, onFail, options);
        };
        
        function onFail() {
            console.log("写真を取得できませんでした");
        }
        
        $(document).on('pageinit', '#main-page', function() {
            var onSuccess = function(pictureUrl) {
                images = [];
                var $img = $('<img>');
                $img.attr('src', pictureUrl);
                images.push({
                    element: $img,
                    label: '無加工'
                });
                myNavi.pushPage('image.html');
            }
            
            $(this).on('click', '.take-from-camera', function() {
                getPictureFromCamera(onSuccess);
            });
            $(this).on('click', '.take-from-gallery', function() {
                getPictureFromGallery(onSuccess);
            });
        });
        
        $(document).on('pageinit', '#image-page', function() {
            var elements = images.map(function(image) {
                var element = $('<ons-carousel-item><div class="converted-image-label-wrapper"><div class="converted-image-label"></div></div></ons-carousel-item>');
                $(image.element).addClass('converted-image');
                element.prepend(image.element);
                element.find('.converted-image-label').text(image.label);
                return element[0];
            });
            elements.forEach(function(element) {
                $('ons-carousel').append(element);
            });

            setTimeout(function() {
                imageCarousel.refresh();
            }, 100);
        });
            
    </script>
</head>
<body>
    <ons-navigator var="myNavi">
        <ons-page id="main-page">
            <ons-toolbar>
                <div class="center">画像変換アプリ</div>
            </ons-toolbar>
            <br>
            <ons-list>
                <ons-list-item modifier="chevron" class="take-from-gallery">
                    <ons-icon icon="ion-images"></ons-icon> ギャラリーから選ぶ
                </ons-list-item>
                <ons-list-item modifier="chevron" class="take-from-camera">
                    <ons-icon icon="ion-camera"></ons-icon> カメラで撮影
                </ons-list-item>
            </ons-list>
            <br>
        </ons-page>
    </ons-navigator> 
    <ons-template id="image.html">
        <ons-page id="image-page">
            <ons-toolbar>
                <div class="left">
                    <ons-back-button ons-if-platform="ios">Back</ons-back-button>
                </div>
                <div class="center">画像</div>
            </ons-toolbar>
            <ons-carousel var="imageCarousel" class="image-carousel" fullscreen auto-scroll overscrollable>
            </ons-carousel>
        </ons-page>
    </ons-template>
</body>
</html>

ons.bootstrap()

11行目のons.bootstrap()は,Onsen UIを読み込み,Onsen UIの機能をアプリで利用できるようにする。 javascriptの一番最初に実行する。 もし,これを書かなければOnsen UIを利用した処理は動かない。
なお,この関数は Onsen UI V1 Minimum (Onsen UI Version 1 最小限のプロジェクト)でなければ動かないみたい。既に Onsen UI V2 は出ているみたいだけれど動かなかった。

getPictureFromGallery()

15行目のgetPictureFromGalleryは,ギャラリーから画像のパスを取得する関数。 引数の onSuccess は成功したときに実行するコールバック関数で,実際には23行目で呼び出され,49~58行目で定義したものが実行される。 実行時,23行目に書いてあるように,引数として imageURI が渡される。 imageURIは,ギャラリーから画像のパスを取得するCordovaのAPIである navigator.camera.getPicture が成功したときに呼び出されるコールバック関数で,オプションの destinationType に Camera.DestinationType.FILE_URI を指定した場合,このコールバック関数の引数に画像ファイルへのパスが返される。

getPictureFromCamera()

28行目のgetPictureFromCameraは,写真を撮影して保存する関数。 CordovaのAPIである navigator.camera.getPicture を呼び出すときのオプション sourceType が Camera.PictureSourceType.CAMERA になっているだけで,他はgetPictureFromGalleryと同じである。

onFail()

CordovaのAPIである navigator.camera.getPicture が何らかの原因で失敗したときに呼び出される関数。

メインページが表示されたときのイベントハンドラ

48~66行目はメインページが表示されたときのイベントハンドラ。 前述したように49~58行目にgetPictureFromGalleryやgetPictureFromCameraの引数に指定するコールバック関数が定義されている。 この関数では,引数に渡された画像ファイルのURIをsrc属性に指定したimgタグを作り,'無加工'というラベルとともにimages配列へ追加している。 ただ,このとき,50行目でこの関数に入る都度images配列を初期化しているため,何回この関数が呼び出されても配列imagesには1枚の画像しか格納されない。なぜそうなっているのかは不明。 50行目を取り除き,13行目でimagesを定義するとき空の配列 [] で初期化すれば複数の画像が配列に格納される。 60~62行目,63~65行目は,それぞれボタン「カメラで撮影」,「ギャラリーから選ぶ」がクリックされたときの呼ばれるイベントハンドラである。 ここで $(this) は,メインページを表す。一般に,イベントハンドラ内では$(this)はそのイベントを取得したオブジェクトを表す。 したがって,メインページ内でセレクタが '.take-from-camera' のオブジェクト(classプロパティにtake-from-cameraが設定されているタグ)がクリックされたとき関数getPictureFromCamera()が, '.take-from-gallery' のオブジェクトがクリックされたとき関数getPictureFromGallery()が呼び出されるようにイベントハンドラが登録される。

画像ページが表示されたときのイベントハンドラ


68~83行目は画像を表示するためのページ(image-page) が表示されたときに呼び出されるイベントハンドラの定義である。
まず,69行目のimages.mapであるが,これは配列(この場合はimages)の各々の要素に対してある処理を行って,その結果から得られるデータを要素とする配列を返す関数である。
具体的には,ギャラリーから取得した,あるいは,カメラで撮影した画像を指定して50~56行目で作成したimg要素配列の各々に対して,70~73行目で画像をカルーセル表示するためのons-carousel-item要素を作成し,それを要素とする配列を変数elementsに格納している。
そうして作成した配列elementsの各々の要素を,76~78行目でons-carousel要素の子要素として加えている。
最後に80~82行目でカルーセルを0.1秒後にリフレッシュしている。ここで,81行目のimageCarouselは115行目でons-carousel要素につけた変数名である。
なお,ons-carousel要素はテキストのP161に説明があるが,左右や上下に項目を並べてスワイプで移動できるようにしたものである。詳細はオンラインマニュアルを参照のこと。

プリプロード

後で表示する画像を前もって読み込んでおき,表示するときにスムーズにすることをプリプロードという。 51~56行目でこのプリプロードを行っている。プリプロードを行うにはまずimgタグ要素を作成する(51行目)。jQueryでhtml要素を作成するには$()内にセレクタでなくhtmlタグ(この場合は<img>)を書く。このとき返される値はjQuery要素である。こうして作成されたhtmlタグ要素にattrメソッドでsrc属性に画像のurlを設定する(52行目)。

2016年10月5日水曜日

ゼミの初日

今日はゼミの初日。ガイダンスとスケジュールの確認,ゼミの進め方を説明し,早速Monacaでスマホアプリの作り方を練習しました。
以下は秋学期のスケジュール。


1限目はMonacaを使ったスマホアプリ技術の修得。2限目は各自の研究テーマの文献調査。
今日の話し合いで暫定的なテーマが決まりました。次回はそれについての調査報告。
  • K君:スマホによる質問紙によってメンタルな問題を抱えている人の心の状態を定量化し,それと他の様々な要因(歩数など運動量,睡眠時間,食欲,天候,・・・)との相関を経時的にグラフ表示して見える化し,心が不安定になる兆候を自分自身で把握し予防できるようなスマホアプリを開発する。
  • T君:育児日記の電子化。ただ電子化するだけならブログと同じ。そこで,育児日記をつける際赤ちゃんの夜泣きの数,飲んだおっぱいの量,睡眠時間,・・・といった値を記録し,産後うつの指標(エジンバラ産後うつ病自己評価票)との相関を経時的にグラフ表示して見える化し,うつになる兆候を自分自身で把握し予防できるようなスマホアプリを開発する。
  • Kさん:月経時の頭痛などの体調不良は適切な時間にピルを服用することにより予防できる。しかし,ピルの服用には種類によってタイミングがあり,時期を逸すると効果があまり期待できない。そこで,前回の月経,体温,食欲などのデータをスマホアプリで管理し,ピルを服用すべき適切な時期をアラートするスマホアプリを開発する。
各自のテーマについての先行研究や事例研究を文献調査して毎回のゼミで報告する。

2016年10月4日火曜日

明日からゼミが始まります!

いよいよ明日から3年生の新しいゼミが始まります。これから1年間半ゼミ活動を続けていくことになりますが,明日はその初日ということでいろいろな準備を行います。
  • Googleアカウントの取得
  • Googleドライブの共有設定
  • ブログの共有設定
  • Monacaのアカウント登録
次に「Monacaで学ぶ初めてのプログラミング」というオンラインブックを読みます。ただし,この本は無料で読むことのできるのは27ページまでです。それ以降を読みたければ購入しなければなりません。
また,Monacaには公式ガイドブックがあります。 ただし,この本は昨年出版されたもので,その後Monacaは進化を遂げているため,多少記述が古く,書いてある通りに操作ができないという難点があります。それでもMonacaでアプリを開発する際には常に携帯しておく価値のある本です。
また,Monacaのホームページには「はじめてのHTML5モバイルアプリ時開発講座」というのがあり,これにしたがって学習していけばおおよそのMonacaでのアプリ開発手法を学ぶことができます。