jQueryMbile個人的まとめ

jQueryMobileをちょろっと使ってみました。
今後も使う機会が多いのでまとめときます。きっとあとで追記したりする。
ちなみに使ったバージョンは1.0b1

基本中の基本

  1. data-role="page"で囲む
  2. 基本的にはdata-role="header" , data-role="content" , data-role="footer"で区切る
  3. 1ページに複数のdata-role="page"を持つことも可能(どういうとき使うの?普通に別ページに飛ばすときとどう作り分ける?)
  4. 戻るボタンはdata-rel="back"(ただしhref属性には値を入れる)
  5. ヘッダーに戻るボタンをつける場合はpageにdata-add-back-btn="true"を付与(デフォルトではオフになってる)

俺俺テンプレート

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>title</title>

<!-- βからはviewportを勝手に設定しなくなった? -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!--アプリケーションモードを使う場合-->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />

<link type="text/css" rel="stylesheet" href="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<!-- jQueryMobileの設定を上書きする場合はここで -->
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0b1/jquery.mobile-1.0b1.min.js"></script>

</head>
<body>
<div data-role="page" class="page" data-add-back-btn="true">
<div data-role="header" data-theme="b" class="header">
<h1>title</h1>
<a href="#" data-icon="home" data-iconpos="notext" data-direction="reverse">Home</a>
<!-- /header --></div>
<div data-role="content" class="content">
<p>content</p>
<!-- /content --></div>
<div data-role="footer" class="footer">
<p>fotter</p>
<!-- /footer --></div>
<!-- /page --></div>

</body>

リンク

  • 下記の指定をするとAjax遷移をしない

 rel="external" , data-ajax="false" , target="hoge"

トランジション

リンクにdata-transitionを指定

  1. slide(デフォルト)
  2. slideup(下から)
  3. slidedown(上から)
  4. pop(ふわーんってなる)
  5. fade(popと違いがよくわからん)
  6. flip(多分3Dっぽく入れ替わるんだけど微妙じゃね?)

※動きを逆にする場合はdata-direction="reverse"を指定

ダイアログ

リンクにdata-rel="dialog"を指定

  • 履歴に残らない
  • 適当に四方に余白を取ってダイアログっぽくしてくれる

テーマ

data-theme="a" a-e

ヘッダ、フッタ

  • フッタ内のリンク、ボタンは全てボタン形状になる
  • 全ページで固定にする場合は全てのヘッダ(フッタ)に同じdata-idを指定してdata-position="fixed"を指定する

ナビゲーションバー

divにdata-role="navbar"

  • 5個まで1行で表示

ボタン

ボタン要素にdata-role="button"

  • アイコンを指定する場合はdata-icon="arrow-l"
  • アイコンの表示位置指定はdata-iconpos top , left , right , bottom

http://dev.screw-axis.com/doc/jquery_mobile/#/doc/jquery_mobile/components/buttons/buttons_icons/

  • よく使いそうなもの
    • 矢印 arrow-l , arrow-r , arrow-u , arrow-d
    • 削除 delete
    • プラス plus
    • チェック check
    • 設定 gear
    • リロード refresh
    • ホーム home
    • 検索 search
  • data-inline="true"でインラインボタン(テキスト内容に合わせて長さを調整)
  • グループボタン
    • ボタンをdata-role="gontrolegroup"で囲む
    • data-type="forizontal"で横方向

レイアウトグリッド(本当に必要なときだけ使うように)

  • class="ui-grid-a/b/c/d"(a:2カラム〜d:5カラム)
  • class="ui-bock-a/b/c"(子コンテナ)
  • 各グリッドは親要素の長さから均等に分割される

開閉式コンテンツ

data-role="collapsible"
data-collapsed="true"(ロード時に閉じている)

フォーム

  • 必ずaction,methodの指定されたformで囲む
  • idを指定するときはページ内だけでなくサイト全体でユニークにすること
  • ラベルとinput要素はdata-role="fieldcontain"で囲むこと推奨
  • その他色々ありそうだけど、現状使ってないのでなんとも。きっと後で追記

リスト

data-role="listview"

  • 分類リスト
    • li data-role="list-divider"を指定
  • サムネイルを表示する場合は単純にアイテムの最初の要素に画像を追加すればいい80px四方になる
    • 16px四方にする場合はui-li-iconクラスを指定する
  • 他のコンテンツとリストを同居させる場合はul,olにdata-inset="true"を追加する
  • 動的にアイテムを追加した場合は$(list).listview("refresh")を呼ぶ
  • フィルタとか色々あるけど使ってないのできっと後で追記

ざっとこんな感じ。ぶっちゃけ、ボタンとリストとナビゲーションさえ抑えておけば後は普通のWebアプリと同じような感じで出来るイメージ。簡単なものならだけど。
ただ、Ajax遷移はまだまだ微妙な気がします。特にAndroidだと不自然な遷移になる事が多々あったので、今回はAjax遷移をしないようにしています。

特定のURLをフックしてアプリを起動させる(暗黙的インテント)

今jQueryMobileの勉強を兼ねてつぶあにのスマートフォンサイトを作っているんですが、どうせならアプリと連携してみようということで調べました。

やりたいこと
・特定のURLクリック時にアプリをインストールしている場合はアプリケーション選択ダイアログが出て、ブラウザで遷移するかアプリを起動するか選べる
・アプリをインストールしていない場合は普通にページ遷移

具体的に言うと、オンエアー中のアニメのタイムラインを見に行こうとするとアプリケーションに移動するか、スマフォサイトで用意したページに行くか選べるようにしたいってこと。

まず、AndroidManifest.xmlを変更

   <activity
      android:name=".Test"
      android:label="@string/test.title">
      <intent-filter>
        <action
          android:name="android.intent.action.VIEW" />
        <category
          android:name="android.intent.category.DEFAULT" />
        <category
          android:name="android.intent.category.BROWSABLE" />
        <data
          android:scheme="http"
          android:host="test.tsubuani.com"
          android:pathPrefix="/live" />
      </intent-filter>
    </activity>

intent-filterにaction.View,category.DEFAULT,cateogry.BROWSABLEを追加。
dataにscheme,host,pathPrefixを追加。
schemeやhostとかはそれぞれ依存しているので注意が必要。
pathPrefixの他にも、pathやpathPatternなんてのも指定できる。
細かい仕様は下記参照。
http://www.techdoctranslator.com/android/guide/manifest/data-element

今回の例で言うと

http://test.tsubuani.com/live*******

っていうURLにアクセスしようとするとTestActivityが起動するという感じになる。

ActivityはonNewIntentに処理を書いてやればよい

  @Override
  protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    Intent intent = getIntent();
    if( intent != null ) {
      /** リンク先のURLを取得する。 */
      String data = intent.getDataString();
      if(data != null){
        //urlを元にごにょごにょ
      }
    }
  }

これで、アプリをインストールしている人はアプリで実況TLを見ることが出来るし、
そうでない人はスマフォサイトで用意したページに遷移させることが出来る。
Androidはこんな感じでアプリ同士の連携がすごい簡単に出来るのが素敵。

ローカルのxamp環境でサブドメインを使う

忘れそうなのでメモ

xamp/apache/conf/extra/httpd-vhosts.confにVirtualHost追加

<VirtualHost *:80>
DocumentRoot "D:/xampp/htdocs/hogehoge" #プロジェクトの場所
ServerName hoge.localhost
ServerAlias hoge.localhost
</VirtualHost>
<VirtualHost *:80>
DocumentRoot "D:/xampp/htdocst"
ServerName localhost
ServerAlias localhost
</VirtualHost>

C:\WINDOWS\system32\drivers\etc\hostsにドメイン追加

 127.0.0.1 hoge.localhost

apache再起動

終わり。

Twitterで話題のアニメを集めるつぶあにというサービスをリリースしました。

えごリブ作ってたときに知人に見てもらったときに、どういう経緯か忘れたけど
「アニメのツイートを話数毎にまとめたら面白いんじゃね?」っていう話が出たので作ってみました。
といってもβ版公開したのは6月末だけど。

Twitterのアニメのハッシュタグを収集してランキングしたり、エピソード毎にツイートのチャートを表示するサービスです。
DVDの売り上げランキングとかとはまた違った動きになるんじゃないでしょうか。

最初はアニメの番組表とか登録するの無理だろって思って作る気は無かったんだけど、
しょぼいカレンダーってところがAPIを公開していたので
これならハッシュタグ登録するくらいであとはほっといてもイケる!と思い、6月の頭頃からコソコソと作ってました。

Webアプリについて

今回は技術的にえごリブとやることはバッチ処理くらいしか違いがなかったので
2週間位で大体出来てあとはデザインとかキャッシュの
調整とかって感じで割りとサクっと作れたと思う。CakePHPに大分慣れてきた。
まぁバッチ作ってるときは動きが全然見えなくて何度かやめかけたけど。画面があるって大事。テンション的に。
ただ、ツイートのデータが1週間に50万とかのペースで増えていってるので
SQLのチューニングをちゃんとしないとすごーく遅くなりそうです。今までこんだけのデータ扱う経験は無かったので
SQLの勉強にはなった。

サービス的にはハッシュタグを集めてるサイトっていうとハッシュタグクラウドあたりが
有名だと思うけどアニメに特化してエピソード単位でツイートをまとめるサービスはあんまり
見かけないのでそれなりに新しいはず!きっと。

ちなみにTwitterStremingAPIを使用してツイートの収集をしていますが、
収集は自宅のPCでやってるので、うちの回線が止まるとツイートが取得できなくなるという
かなり危なっかしい構成です。実際開発中に一回30分くらい繋がらなくなった時がありました。
出来れば収集自体もよそでやりたいけどレンタル鯖とかじゃ無理だしVPSとかだとメモリが…。
誰かお金ください。

Androidアプリについて

最初は作る気無かったけど、AndroidでTwitterStreamingAPIっていけるのかな?
って思ったので、実況ツイートをリアルタイムで追えるという単純なアプリを作ってみました。
原型は2日位ですぐに出来たけど、やっぱりツイート量が多いと処理が厳しいっぽい。
まず、リストビューが意外に重い。秒間分間500ツイートくらいになるともう結構カクカクになる。
なので、ScrollViewを使って擬似リストビューっぽくしたらなんとか動くようになった。まだちょっと怪しいけど。
それからテストするチャンスが週に3回くらいしかないのがきついw(30分で2万ツイートを余裕で越えるようなのは今だとタイバニ・アイマス・青エク・プリキュアくらいしかない。全部見てないけど。)
あと初めてPreferenceActivityを使ってみたけどすごい便利!簡単すぎだった。

作ってみて思ったのはアニメのツイート量まじ多いのね。30分で5万ツイートとか頭おかしい。でもだからこそ結構見た目面白いサービスになったかなとは思います。まぁ僕は実況とかしないけど。

機能的にはユーザーページの機能をもう少し足してみたいのと、あといくつか細かい機能のアイデアがあるのでもうちょい作り続けそう。統計データも結構面白いのでそういうのも見れる画面を作ろうかなと思ってます。でもTERAのOBT始まったら多分そっちやる。TERAやりたい。まだ?
とはいっても僕自身が実況しないのでユーザーページの機能を拡張するモチベーションが続くかは謎。

Androidでゲーム・書籍管理!なえごリブ for Androidをリリースしました。

えごリブのAndroid版がやっと完成してリリースしました。\(^o^)/
デバッグを何人か知り合いに手伝ってもらい、色々致命的なバグが見つかって
ほんと自分のへたれ加減が良く分かったけどなんとかリリースできました。しちゃいました。

えごリブ for Android

どんな感じなのかは上記サイトから!
機能としては

  • キーワードによるアイテムの検索
  • バーコードの読み取り
  • テンプレート設定
  • 登録アイテムの編集
  • 登録アイテムの検索
  • 登録アイテムへのコメント(レビューとかメモとか)
  • 登録アイテムの関連商品検索

といったことが出来るアプリです。

AsyncTask、ListViewあたりの扱い方のコツを掴むまでがキモでした。
そっから先はかなりサクサクと作ることが出来た。むしろAPI作成するほうがめんどいと感じるレベル。
特に画面遷移が簡単すぎ。Intentすげー…。
4月頭くらいからのんびりと作り始めたので制作期間は大体一ヶ月といったところですね。遅いね。ごめんね3流で。
ま、まあゲームもしてたし仕事もしてたしね!

開発以外の部分のディベロッパー登録や、
アプリケーションの登録、公開といった手順も非常に分かりやすくて感動した。
iPhoneと比べると雲泥の差だわ。

今後はWeb版共々少しずつ改良していこうと思います。
Android持ってる人で読書やゲームする人は是非使ってみて感想をぷりいいいいず。

Androidアプリは作っていてかなり楽しかったのでまた何か作ってみたいなあ。あとAndroidもっと普及してくれ!

えごリブがtwinaviに紹介されちゃった。

こないだツイナビにてえごリブが紹介されました。

といっても自分から載せてくださいお願いします!ってメールを出したわけですが。
さすがツイナビ様で、ユーザーも結構増えました。それまではほんと知り合いがたまにちょろっと書くくらいだったんですが。
特に4月頭あたりからGoogle先生が怒ってサイトタイトルで検索しても1ページ目に表示されないという状態になってしまってからはほとんど人は流れてきていなかったんですが、なかには既に僕よりアイテムの登録数多い人もいたりして驚き。
ていうかGoogle先生は何に怒っているんだろう…。スパムサイト扱いされてんのかな。特にやましいことはしているつもりないんですが。

そんなわけでまだまだ小さいですが、せっかく使ってくれてる人もいるので、少しずつ改善しながら育てていこうかと思います。
まぁ今はアンドロイドに夢中でWebの方は完全に放置してるんですけどね!
Android版の実装のほうは今日大方片付いて、今は必死にデバッグ中。GWまでにはリリースできそうな予感。
誰かデバッグ手伝ってよ!

機能的にはほぼWebで出来ることは大体網羅した感じ。自分で言うのもあれですけど、これは使いやすい!

スパロボやりたい。

ListViewの中のボタンのクリックイベントをActivityに通知する

ちょいとはまったのでメモ。

ListViewの中でどうしてもボタンを並べて、ボタンが押されたらActivityに通知させたかったのですが、なかなかスマートな方法が見つからなかった…。

結局OnClickListenerを実装させたListViewのサブクラスを作って、adapter#getViweの中でリスナーの登録をさせることによって実現しました。

具体的なソースは下記のような感じ。

まずレイアウト。サンプルなんで適当。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
>
  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="sample" />

  <FrameLayout
    android:layout_width="fill_parent"
    android:layout_height="0dip"
    android:layout_weight="1"
  >
    <com.sample.MultiButtonListView
      android:id="@+id/list"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent" />
  </FrameLayout>
</LinearLayout>

続いてリストの各行のレイアウト

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical"
>
  <TextView
    android:id="@+id/titleText"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
  <TextView
    android:id="@+id/authorText"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" />
    
  <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
  >
    <Button
      android:id="@+id/titleBtn"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="title" />
    <Button
      android:id="@+id/authorBtn"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="author" />
  </LinearLayout>
</LinearLayout>

で、問題のListViewのサブクラス。

/**
 * リスト内にボタンを配置してそれが押されたとき通知してくれるListView
 * @author Tom
 *
 */
public class MultiButtonListView extends ListView implements OnClickListener {
  
  /**
   * コンストラクタ
   * @param ctx
   */
  public MultiButtonListView(Context ctx){
    super(ctx);
  }
  
  /**
   * コンストラクタ
   * @param ctx
   * @param attrs
   */
  public MultiButtonListView(Context ctx , AttributeSet attrs){
    super(ctx , attrs);
  }
  
  /**
   * リスト内のボタンがクリックされたら呼ばれる
   */
  public void onClick(View view) {
    int pos = (Integer)view.getTag();
    this.performItemClick(view, pos, view.getId());//idって普通なに渡すの?
  }

}

単にOnClickListenerを実装して、その中でpeformItemClickを呼んでるだけ。
viewのgetTagでpositionを取ってるけど、それはAdapterの中で設定してあげてます。ところで引数の3つ目のidってなに渡すの?使ったこと無いんだけど。
adapterはArrayAdapterのサブクラスで下記みたいな感じ。

public class BookAdapter extends ArrayAdapter<Book> {
  private LayoutInflater inflater;
  private int resId;
  
  public BookAdapter(Context ctx , int resId , List<Book> items){
    super(ctx, resId , items);
    this.resId = resId;
    this.inflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  }
  
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    MultiButtonListView list = null;
    try{
      list = (MultiButtonListView)parent;
    }catch(Exception e){
      e.printStackTrace();
    }
    if(convertView == null){
      convertView = inflater.inflate(resId, null);
      holder = new ViewHolder();
      holder.titleView = (TextView)convertView.findViewById(R.id.titleText);
      holder.authorView = (TextView)convertView.findViewById(R.id.authorText);
      holder.button1 = (Button)convertView.findViewById(R.id.titleBtn);
      holder.button2 = (Button)convertView.findViewById(R.id.authorBtn);
      holder.button1.setOnClickListener(list);
      holder.button2.setOnClickListener(list);
      
      convertView.setTag(holder);
    }
    else{
      holder = (ViewHolder)convertView.getTag();
    }
    Book book = this.getItem(position);
    holder.titleView.setText(book.getTitle());
    holder.authorView.setText(book.getAuthor());
    holder.button1.setTag(position);
    holder.button2.setTag(position);
    
    return convertView;
  }
  
  private static class ViewHolder{
    public TextView titleView;
    public TextView authorView;
    public Button button1;
    public Button button2;
  }
}

parentをキャストしているのと、buttonにリスナー登録しているところ以外は特に変わったことはしていない。

で、最後にActivity

public class MultiSample extends Activity implements OnItemClickListener {
    private static List<Book> books;
    static{
      books = new ArrayList<Book>();
      books.add(new Book("なれる!SE3 失敗しない?提案活動 (電撃文庫)" , "夏海 公司"));
      books.add(new Book("はじめてのAndroid2プログラミング", "柴田 文彦"));
      books.add(new Book("魔術はささやく (新潮文庫)" , "宮部 みゆき"));
      books.add(new Book("幽霊船が消えるまで―痛快本格推理 (祥伝社文庫―天才・竜之介がゆく!)" , "柄刀 一"));
      books.add(new Book("イニシエーション・ラブ (ミステリー・リーグ)" , "乾 くるみ"));
      books.add(new Book("GOTH 僕の章 (角川文庫)", "乙一 "));
      books.add(new Book("ネガティブハッピー・チェーンソーエッヂ (角川文庫)" , "滝本 竜彦"));
      books.add(new Book("アメリカ銃の謎 (創元推理文庫 104-10)" , "エラリー・クイーン"));
    }
    
    private ListView list;
    private BookAdapter adapter;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        list = (ListView)findViewById(R.id.list);
        adapter = new BookAdapter(this, R.layout.list_row, books);
        list.setAdapter(adapter);
        list.setOnItemClickListener(this);
    }
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
      Book book = adapter.getItem(pos);
      switch(view.getId()){
      case R.id.titleBtn :
        Toast.makeText(this, book.getTitle() , Toast.LENGTH_SHORT).show();
        break;
      case R.id.authorBtn :
        Toast.makeText(this, book.getAuthor() , Toast.LENGTH_SHORT).show();
        break;
      }
    }
    
}

Bookオブジェクトは単純にtitleとauthorを持っているだけ。

これで、リストの中のボタンイベントを普通にonItemClickで拾えるようになった!
サンプル画像は下記のような感じに。
f:id:tomstay:20110421220113j:image

そもそもリストの中にボタンなんぞ並べずにコンテキストメニューとか使えって話かもしれないけど。ただ、今考えてるのはボタンを押したらダイアログが出るような処理を考えているので2連続ダイアログっぽいのが出るのは微妙じゃね?って思ったのでボタン並べるという所業におよんだのでありました。

おかしなところがあったらそっと教えてください。