遅延ロードで必要なデータのみ取得

遅延ロードで必要なデータのみ取得

大量のデータや読みこみに時間がかかるデータを画面に表示するときに
かなり時間がかかる

この場合、最低限の画面を表示し
徐々にデータを表示したり
ユーザーがWikipediaのコンテンツみたいに
アコーディオンを開いたらオンデマンドで
データを取得、表示するという遅延ロードが有効

すごい時間をかけて全部でるより
徐々に表示されるほうがストレスは少ない

jQueryMobileで、
リスト表示で
この遅延ロードをするにはコツがある

なぜなら
Javascriptで後からタグを追加しただけだとjQueryMobileのスタイルが適用されないから

なので、追加したら
listview(“refresh”)
をする必要がある

まず、実践するには

<script src="js/loadform.js"></script>

で自作スクリプトの読みこみ

次に、開閉したい場所の領域を

<div data-role="corapsible" data-corapsed="true" id="corapsible-list">
<h3>遅延ロードの実践</h3>
<ul data-role="listview" id="list">
</ul>
</div>

として設定

data-role=”corapsible”
は、開閉する指定

data-corapsed=”true”
で開いた状態にする
デフォルトは false で閉じた状態

http://dev.screw-axis.com/doc/jquery_mobile/components/content/collapsible/
を参考にするとわかりやすい

そして、処理はJavascript

途中で
$.mobile.showPageLoadingMsg()

$.mobile.hidePageLoadingMsg()
があるけど、これは
ローディングアイコンの表示、非表示をする

この動作サンプルとしては
http://www.atmarkit.co.jp/fdotnet/chushin/jqmobile_06/jqmobile_06_02.html
にTwitterのつぶやきを表示したりするサンプルがあるので
こちらを参考にするとわかりやすい

あと
data: “param1=123&param2=homuhomu”
これは
http://semooh.jp/jquery/api/ajax/jQuery.ajax/options/

http://phpjavascriptroom.com/?t=ajax&p=jquery_ajax_requests

jQuery $.ajaxでJSON・JSONP読み込みに使用する主なオプション


をみたかんじだと
サーバーip/json.php?param1=123&param2=homuhomu
になる

$.trim()

http://semooh.jp/jquery/api/utilities/jQuery.trim/str/
にあるように、空白の削除するメソッド
つまり
if($.trim($(“#list”).html()) ==””){
なら
id=”list”のタグの中身が空ならとなる

$.each()は
http://semooh.jp/jquery/api/utilities/jQuery.each/object%2C+callback/
にあるように、繰り返し処理をする
繰り返すのは
$(‘#list’).append(“

  • “+data[e]+”
  • “);
    でリスト追加処理

    $(document).on("expand","#corapsible-list",function(e,data){
    if($.trim($("#list").html()) ==""){
    $.mobile.showPageLoadingMsg();
    
    $.ajax({
    type:"POST",
    url:"./json.php",
    data:"param1=123&param2=homuhomu",
    dataType:'json',
    success:function(data){
    $.each(data,function(e){
    $('#list').append("<li>"+data[e]+"</li>");
    });
    $('#list').listview("refresh");
    $.mobile.hidePageLoadingMsg();
    },
    error: function(msg){
    alert(msg.responseText);
    $.mobile.hidePageLoadingMsg();
    }
    
    });
    
    }
    
    });
    

    そして、PHPファイルでデータの用意
    ファイル名は json.php

    <?php
    header('Content-type: application/json;charset=utf-8');
    $data = array("みかん","りんご","ブドウ","もも");
    echo json_encode($data);
    ?>
    

    json_encode()
    で配列をjson形式にしている

    Webアプリの通信量削除

    Webアプリのパフォーマンスチューニング

    まず、通信量の削減

    基本的に、ライブラリーを使うときには
    .min.js
    とついている圧縮版を使う

    自作のコードもできる限りツールや
    Webサーバーのモジュールで圧縮する

    圧縮にはgruntのプラグインなども使えそう

    あと、使われる物としては
    Google Closure CompilerでJavascriptのコード削減

    YUI Compressor によるJavascriptとスタイルシートのコード削減

    Apache のmod_deflate によるデータ圧縮など

    ただし
    Google Closure Compiler
    YUI Compressor
    についてはJavaが必要になる

    Google Closure Compilerは
    https://code.google.com/p/closure-compiler/
    を参考に

    また、Google Closure Compilerは、コマンドからの操作も可能

    http://www37.atwiki.jp/aias-closurecompiler/pages/16.html?&flag_mobilex=1
    の解説がわかりやすい

    このツールは
    改行などを削除し、コードを圧縮する

    使い方は

    java -jar compiler.jar --js ファイル名

    結果をファイルへ出力するには

    java -jar compiler.js --js ファイル名 --js_output_file 出力ファイル名
    

    とする

    また、ファイルを結合するなら

    java -jar compiler.js --js_output_file 出力ファイル名 元のファイル名 元のファイル名

    とする

    次に、YUI Compressor
    これは、Yahoo!が開発している
    Javascriptとスタイルシートの圧縮ツール

    詳しい使い方については
    http://stacktrace.jp/tools/yuicompressor/
    を参考に

    これも、コマンドから実行で行う

    CSSファイルを圧縮するには

    java -jar yuicompressor.jar --type css --charset utf-8 -o 出力ファイル名 圧縮したいファイル名

    となる

    これも2つのソースをまとめることが可能で

    jar -jar yuicompressor --type css --charset utf-8 -o 出力ファイル名 元ファイルその1 元ファイルその2

    とする

    次に、データ圧縮をする Apache mod_deflateについて

    HTTP 1.1をサポートするWebサーバーを使うと
    サーバー側で自動的に圧縮したデータをブラウザー側で解凍して表示できる

    Apacheの場合
    mod_deflateモジュールを使うことでできる

    まずは、http.conf
    つまり、Apache設定ファイルの中にモジュール読み込み設定があるか確認

    LoadModule deflate_modules/mod_deflate.so
    

    そして、
    http://httpd.apache.org/docs/2.2/ja/mod/mod_deflate.html#deflatecompressionlevel
    のリファレンスを参考に追記する

    DeflateCompressionLevel
    の値は、1~9で
    値を大きくすれば圧縮率はあがるけど
    代償としてサーバーの負荷も上がっていく

    devicePixelRatioと画面サイズ

    devicePixelRatioと画面のサイズ

    ピクセルが2倍になった状態で
    そのまま文字や画像を表示すると見かけ上のサイズが半分になり
    小さく表示される

    このため、鮮明な解像度の画面を使うときには
    文字や画像を拡大表示することで
    ちょうど良い大きさに拡大している

    このときに、何倍かするかという値が
    window.devicePixelRatio

    この値は、iPhone4以降なら 2.0
    ほとんどのAndroidなら 1.5

    あと、元々の画像の解像の値を
    物理値、物理座標といい

    修正された値は、
    論理値、論理座標という

    これは少し面倒だけど
    マウスイベントとか、タッチイベントの座標や
    cssでの設定は論理座標になるため
    Javascriptで制御するときには、論理座標になる

    スクリーンに対する情報は
    screen.height
    screen.availHeight
    window.innerHeight
    window.outerHeight
    などの値から情報を得られる

    注意点としては、OSにより値が
    物理値か、論理値か変わると言うこと

    iOSはすべて論理値なのに、Androidは物理値のものもある

    screen.height
    画面の高さ
    これは、ステータスバーなども含む
    Androidでは物理値扱い

    screen.availHeight
    利用できるスクリーンの高さ
    Androidでは物理値

    screen.innerHeight
    描画領域の内サイズ
    どっちも論理値になる

    screen.outerHeight
    描画領域の外サイズ
    Androidでは物理値

    Webアプリをネイティブアプリみたいに見せるには

    Webアプリをネイティブアプリみたいにみせる

    Webアプリをネイティブアプリみたいに見せるには
    アプリアイコンの登録と
    ブラウザバーやメニューを非表示にする

    まずは、アプリアイコンの登録

    これは、HTMLファイルのheadへ
    アプリアイコンへの設定をすることでできる

    <link rel="apple-touch-icon-precomposed" href="../images/icon_file.png"/>
    

    というように、
    href=””には、アイコンファイルへのパスを記述する

    アイコンのサイズは
    144×144がオススメ

    これは、アイコンのサイズが小さいと、
    解像度の高い端末だとボケて表示されてしまうから

    次に、アドレスバーの削除

    Androidでは、

    <meta name="apple-mobileweb-app-capable"
    

    が使えないため、Javascriptで制御する

    Androidでは、画面が1pxでも下へ移動するとURLバーが隠れるため
    この特性を使う

    $(document).on("mobileinit",function(){
    if(navigator.userAgent.match(/Android|iPhone/i)){
    $(document).on("pagechange",function()e,data){
    //1pxずらしてアドレスバーを隠す
    var hide_addressbar = function(){
    if(document.body.scrollTop==0){
    window.scrollTo(0,1);
    }
    }
    
    /*ページ縦幅がスクリーンの縦幅より狭い場合
    ページ縦幅をスクリーン+1して
    スクロールできるようにする*/
    if(navigator.userAgent.match(/Android/i)){
    var pageHeight = $(document).height();
    var windowHeight = window.outerHeight / window.devicePixelRatio;
    
    if(windowHeight => pageHeight){
    pageHeight = windowHeight+1;
    $('BODY').css('height',(pageHeight)+'px');
    }
    }
    hide_addressbar();
    
    //スクロール時に強制的に 1pxずらす
    $(window).on('scrollStop',function(){
    hide_addressbar();
    });
    });
    }
    
    });
    

    これは、Androidは、ページがスクリーンより短いと
    縦にスクロールできないため
    ページの縦幅を
    スクリーンの縦幅+1px広げることで
    スクリーン可能にしている

    それが

    if(navigator.userAgent.match(/Android/i)){
    var pageHeight = $(document).height();
    var windowHeight = window.outerHeight / window.devicePixelRatio;
    
    if(windowHeight => pageHeight){
    pageHeight = windowHeight+1;
    $('BODY').css('height',(pageHeight)+'px');
    }
    }
    hide_addressbar();
    

    の部分

    ただし、これだけだとせっかく隠しても
    ユーザーが上にスクロールすると
    隠したアドレスバーが出てしまう

    このため、その対処として
    スクロール時に画面が一番上に達したか
    チェックし、
    到達したなら強制的に1px下げることでアドレスバーを隠す

    その処理が

    $(window).on('scrollStop',function(){
    hide_addressbar();
    });
    

    2つのグラフ間の塗りつぶし

    2つのグラフ間の塗りつぶし

    塗りつぶしをするには
    fillBetweenを使う

    opts={
    
    fillBetween:{
    series1: 1,
    series2: 2,
    color:rgba(227,167,121,0.2),
    baseSeries:0,
    fill:true
    }
    }
    

    これは、トレンドを出すときに使える
    株価とか為替のレンジ範囲分析とか

    次に、基準値の表示

    グラフ上に、閾値や基準値を表示するには
    canvasOverlayプラグインを使う

    canvasOverlayプラグインを使うには
    HTMLでライブラリーを読み込む

    <script src="js/jqplot/plugins/jqplot.canvasOverlay.min.js"></script>
    

    次に、Javascriptのオプション項目で
    canvasOverlayを設定

    設定するには
    objects:{

    }
    の中で各種項目を設定していく

    opts={
    
    canvasOverlay:{
    show:true,
    objects:{
    {verticalLine:{
    x:2,
    lineWidth:2,
    lineCap:'butt',
    shadow:false
    }},
    {dashedHorizontalLine:{
    y:5,
    lineWidth:2,
    dashPattern:[8,16],
    lineCap:'round',
    shadow:false
    }}
    }
    },
    }
    

    となる

    objectsの中では、最初に線種を設定する
    利用できるものとして

    verticalLine
    垂直線

    dashedVerticalLine
    垂直線(点線)

    horizontalLine
    水平線

    dashedHorizontalLine
    水平線(点線)

    lineWidth
    線の幅

    lineCap
    線の端の設定
    buttは端形、roundなら丸くする

    dashPattern
    波線の長さと間隔の指定
    dashPattern:[8,16],
    というように、配列みたいに指定

    shadow
    陰を付けるかどうかをtrue/falseで指定

    Ajaxによるデータ取得

    Ajaxによるデータ取得

    Javascriptファイルの中にデータを記述するのではなく
    株価やFXのレートのようにリアルタイムで変化するデータを表示したり
    他のサービスのデータを取得して表示したい場合
    Ajaxを使ってサーバーからデータを取得し
    グラフを描画することができる

    Ajaxを利用したデータの読みこみをするなら
    jqplot.json2プラグインを読み込む

    <script src="js/jqplot/plugins/jqplot.json2.min.js"></script>
    

    Ajaxでデータを読み込む関数を定義し
    dataReadererオプションに設定する

    var dataAjaxReaderer = function(url,plot,options){
    var ret = null;

    $.ajax({
    async:false,
    url:url,
    dataType:”json”,
    success:function(data){
    ret = data;
    },
    error:function(XMLHttpRequest,textStatus,errorThrown){
    alert(“読みこみ失敗:”+errorThrown);
    }
    });
    return ret;
    };

    var opts = {
    title:”グラフタイトル”,
    dataReaderer:ajaxDataReaderer,
    axes:{
    xaxis:{

    }
    }
    }
    [/javascript]

    このとき、グラフを描くには
    データ配列ではなく、データのURLを指定する

    今回は、ローカルのファイルなので
    ファイルバスを指定する

    plot = $.jqplot('chart','./data.json',opts);
    

    chartは
    div id=”chart”
    で、表示する領域

    ./data.jsonはファイルパス

    opts は設定したオプション

    次に、data.jsonの中身

    今回は、JSON形式のデータ

    [
    [[1,1],[2,2],[3,5],[4,2],[5,4]],
    [[1,6],[2,4],[3,3],[4,4],[5,5]]
    ]
    

    数字ならこれでいいけど、文字列なら
    “”で囲む

    [
    ["etc",2.1],
    ["Android 4.4",9.6],
    ["Android 4.3",35.9],
    ["Android 4.2",50.4],
    ["Android 4.0",2.5]
    ]
    

    とする

    グラフのカスタマイズ

    グラフのカスタマイズ

    グラフの色を変更するには
    seriesColorsオプションを使う

    opts= [
    
    seriesColors :[
     "#FF0000",
     "#00FF00",
    ],
    
    ]
    

    複数グラフを作成するならかなり使える

    次に、日付
    グラフの軸に日付を付けることができる
    株価チャートとか、経済指標とかのグラフを作るなら有効な手法

    軸に日付を使うなら
    jqplot.dateAxisReadererプラグインを使う
    これを使うには、まずHTMLで

    <script src="js/jqplot/plugins/jqplot.dateAxisReaderer.min.js"></script>
    

    で必要なライブラリーを読み込む

    次に、Javascriptで日付データを配列で作成し
    オプションのX軸を描画するxaxisで
    設定する

    日付のデータは
    月/日/年
    のフォーマットで記述する

    data = [
    ["2014/03/20",17.5],
    ["2014/03/21",16.5],
    ....
    ],
    opts={
    axes:{
    xaxis:{
    renderer:$.jqplot.DateAxisReaderer,
    tickReaderer:$.jqplot.CanvasTickReaderer,tickOptions:{
    formatString:"'%Y/%#m/%#d'",
    angle:-30
    }
    },
    
    }
    }
    

    となる

    日付を利用したいのなら
    renderer:

    $.jqplot.DateAxisReaderer
    を指定し

    formatString:
    でフォーマットを指定する

    指定できるフォーマットは
    %Y
    2014 みたいな4桁の西暦

    %y
    14 みたいな2桁の西暦

    %m
    03 みたいな2桁の月

    %#m

    %B
    September みたいな英語での月の名前

    %b
    Sep みたいな英語のニュースででる省略された月の名前

    %d
    03 みたいな2桁の日付

    %#d
    日付

    %A
    曜日

    %a
    省略型の曜日
    日曜日ならSun

    %H
    03 など24時間表記の時間

    %#H
    3などの24時間表記の時間

    %I
    03などの12時間表記の時間

    %#I
    3などの12時間表記の時間

    %M
    2桁表記で分を表示

    %#M
    分を表示

    %S
    2桁で秒を表示

    %#S
    秒を表示

    基本的に#を付けないなら2桁表記になる

    マップ操作のイベント処理

    マップ操作のイベント処理

    まずは構文

    void setOnMapClickListener(GoogleMap.OnMapClickLister listener)
    マップ上のクリックを検知するリスナーを設定

    void setOnMapLongClickListener(GoogleMap.OnMapLongClickListener listener)
    マップ上の長押しを見地するリスナーを設定

    void setOnCameraChangeListener(GoogleMap.OnCameraChangeListener listener)
    マップ上でのカメラの返歌を検知するリスナーを設定

    oid setOnInfoWindowClickListener(GoogleMap.OnInfoWindowClickListenr lister)
    ピン上の情報がクリックされたことを検知するリスナーを設定
    これは他のAPIとの組み合わせに使えそう

    void onMapClick(LatLng point)
    マップ上でクリックされたときに呼び出される

    void onMapLongClick(LatLng point)
    マップ上で長押しされたときに呼び出される

    void onCameraChange(CameraPosition position)
    マップ上でカメラが変更されたとき呼び出される

    onInfoWindowClick(Marker marker)
    ピン上の情報がクリックされたときのイベント処理

    次に引数のメモ
    listener
    各イベントを検知するためのリスナー

    point
    マップ上で操作を行った位置情報

    marker
    クリックされたMarker インスタンス

    マップ上のイベントは各リスナーを登録することで検知可能になる

    では実践

    クリック時
    長押し時
    マップを移動したとき
    それぞれの状態のときに
    Toast で座標を表示する

    変更するのは MainActivity のみ

    まずはクリックから

    		//click event処理
    		map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
    			
    			@Override
    			public void onMapClick(LatLng point) {
    				// TODO Auto-generated method stub
    				
    			}
    		});

    の中に処理を書いていく

    Ubuntu の場合 Eclipse で Alt + / で
    コードの補完が使えるため
    すべて打ち込まなくても簡単に記述できる

    中の処理は

    Toast.makeText(getApplicationContext(), "クリックされた座標は "+point.latitude+", "+point.longitude, Toast.LENGTH_SHORT).show();

    としてトーストで座標を表示する

    		//click event処理
    		map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
    			
    			@Override
    			public void onMapClick(LatLng point) {
    				// TODO Auto-generated method stub
    				Toast.makeText(getApplicationContext(), "クリックされた座標は "+point.latitude+", "+point.longitude, Toast.LENGTH_SHORT).show();
    			}
    		});
    

    次に長押しイベント
    これもコード補完をつかって

    		//長押しイベント
    		map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
    			
    			@Override
    			public void onMapLongClick(LatLng point) {
    				// TODO Auto-generated method stub
    				//長押しで行う処理
    			}
    		});

    として処理内容を追加するだけ

    今回もトーストで表示なので

    Toast.makeText(getApplicationContext(), "長押しされた座標は "+point.latitude+", "+point.longitude, Toast.LENGTH_SHORT).show();
    

    全体としては

    		//長押しイベント
    		map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
    			
    			@Override
    			public void onMapLongClick(LatLng point) {
    				// TODO Auto-generated method stub
    				Toast.makeText(getApplicationContext(), "長押しされた座標は "+point.latitude+", "+point.longitude, Toast.LENGTH_SHORT).show();
    			}
    		});
    

    そしてカメラの状態変化イベント処理
    つまりマップを移動の処理

    		//カメラ状態変化処理
    		map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    			
    			@Override
    			public void onCameraChange(CameraPosition position) {
    				// TODO Auto-generated method stub
    				//カメラの状態変化のときの処理
    			}
    		});
    

    これも座標をtoast で表示

    LatLng point = position.target;
    				Toast.makeText(getApplicationContext(), "カメラの状態が変化した座標は"+point.latitude+", "+point.longitude, Toast.LENGTH_SHORT).show();

    一度座標を変数に格納してから使っている

    全体としては

    
    		//カメラ状態変化処理
    		map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    			
    			@Override
    			public void onCameraChange(CameraPosition position) {
    				// TODO Auto-generated method stub
    				LatLng point = position.target;
    				Toast.makeText(getApplicationContext(), "カメラの状態が変化した座標は"+point.latitude+", "+point.longitude, Toast.LENGTH_SHORT).show();
    			}
    		});

    このように
    それぞれの動作の中に処理を書けばいろいろな動作をマップでさせることができる

    google map にピンマーカー表示

    google map にピンマーカー表示

    やり方は
    GoogleMap インスタンスを取得
    マーカーの表示位置などを格納したMarkerOptions インスタンスを生成

    addMarker() でマーカー追加

    続いて構文メモ

    GoogleMap getMap()
    GoogleMap インスタンスを取得

    LatLng(double latitude,double longitude)
    マーカーの表示位置用インスタンス

    MarkerOptions position(LatLng position)
    マーカーの表示位置を設定

    MarkerOptions title(String title)
    マーカーのタイトルを設定

    MarkerOptions snippet(String snippet)
    マーカーのスニペット設定

    MarkerOptions icon(BitmapDescriptar icon)
    マーカー上に表示するアイコンを設定

    Marker addMarker(MarkerOptions options)
    マーカーを追加

    そして引数のメモ

    latitude
    緯度

    longitude
    経度

    position
    マーカーの表J位置を示すLatLngインスタンス

    title
    マーカーのタイトル用文字列

    options
    マーカー表示用オプション

    あと
    画面上にピン状マーカーを表示したいのなら
    getMap() で
    GoogleMap メソッドを取得し
    addMarker() でピンの追加をする

    マーカーのパラメータは
    MarkerOptionsインスタンスを生成して設定する

    では実験
    サンプルを元にカスタマイズしていく

    レイアウトファイルはそのまま使う
    使うのは zoom.xml

    MainActivity を書き換えるけど
    前回作成した東京駅を表示するマップを改造する

    		setContentView(R.layout.zoom);
    
    		FragmentManager fragmentManager = getSupportFragmentManager();
    		SupportMapFragment fragment =(SupportMapFragment)fragmentManager.findFragmentById(R.id.fragmentMap);
    		
    		//google map instance 取得
    		GoogleMap map = fragment.getMap();
    		
    		//表示位置の生成(東京駅)
    		LatLng posTokyoStation = new LatLng(35.681382, 139.766084);
    		
    		//東京駅の表示
    		CameraPosition.Builder builder=new CameraPosition.Builder();
    		//カメラの表示位置の指定
    		builder.target(posTokyoStation);
    		//ズームレベル
    		builder.zoom(13.0f);
    		//カメラの向きの指定(北向きなので0)
    		builder.bearing(0);
    		//カメラの傾き設定
    		builder.tilt(25.0f);
    		//マップの表示位置変更
    		map.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build()));
    

    はそのまま使い

    ピンの追加コードを書いていく

    		//ピン設定
    		MarkerOptions options=new MarkerOptions();
    		//ピンの場所を東京駅にする
    		options.position(posTokyoStation);
    		//マーカーの吹き出しの名前
    		options.title("東京駅");
    		
    		//ピンの追加
    		map.addMarker(options);
    

    基本的にはこれでOK

    これでピンをタップすると
    東京駅
    と表示される

    これを応用すればAPIとかと合わせてお店とかできそう