プログラマってこんなかんじ??

アプリ作ったり歌ったりしてます

中洲ジャズ 2017 非公式アプリを作りました (Android)

福岡の中洲で毎年開催されているジャズフェス、中洲ジャズの非公式版クライアントアプリを勝手に作ってみました。

play.google.com

どんなアプリか

全体のタイムスケジュールが見やすい

まず、公式サイトでは 「誰と誰の演奏がかぶっているか」 がちょっとわかりづらいです。ステージごとのスケジュールはわかりますが・・

nakasujazz.net

本アプリでは、

  • 同じ時間帯に
  • 各ステージで
  • 誰の演奏があっているか

が、 横ならび で一目でわかります (誇張)

近くのコンビニの場所がわかる

中洲ジャズは路上での無料イベント。ビール片手に歩きながら音楽を聴けるとか最高ですよね。ビール飲むとお手洗いも近くなりますよね。

  • ビールが買える場所
  • おつまみも買える場所
  • お手洗いがある場所

そう、コンビニ です。載せてます。

ジャズスポット載せてます

ふだんあまり聴けないジャズの生演奏。最高ですよね。もっと聴いてみたくないですか?

  • お酒飲みながら
  • ジャズが聴ける
  • しかも至近距離で

そう、ジャズバー です。載せてます。

中洲近辺にはいくつかそんなお店があります。これを機会に足を運んでみてはいかがでしょうか?

なぜ作ったのか?

公式サイトのタイムスケジュールがよくわからない

上のほうにも書いたのですが、公式サイトの各アーティストのタイムスケジュール。これが自分にはなかなか見づらくて。

自分が知りたい観点は

  • 各ステージで誰が演奏なのか?
  • おなじ時間帯に誰のライブがあっているのか?

これを何とか見やすくできないものか?と考えたのがきっかけです。公式サイトだと 前者 はわかるけど 後者 は・・という感じでした。聴きたいアーティストたくさんなので、効率よくまわらないといけないミッションがあるのです!

オフラインで情報を見たい

毎年恒例なのですが、開催期間中にサーバーがよく落ちます。。イベント当日にタイムスケジュールが見れないのはつらい。。自分のまわりでは、公式サイトのタイムスケジュールをキャプチャしたり画像保存して見てる人が多いです。

地元ミュージシャンの応援

自分の周りの友人たちから「生演奏が聴きたい」と話によくあがるのだけど、実は福岡でもたくさん聴ける場所あるし、今回もこんなにたくさんすごい地元ミュージシャンが演奏しているんだぜ、的なところを少しでも伝えれたらいいなーなんて。公式サイトの情報だと、演奏している方が「地元」の方なのかどこの方なのか一目でわからないのです。

自己スキルアップ

プログラマとしての話になるのですが、実は最近 Android アプリを実案件で作っていなかったので技術的に不安を感じていました。Android は、以前は OS のソースを読んでたくらい大好きな技術。最近は情報は追ってるけども「実際に使って試行錯誤する」というフェーズをほとんどできてない状態。アプリをしっかり作れば知見もたまるし一石二鳥かな、という点。

よかったこと

ひとまずリリースできた

最低限の機能を実装して最後までリリースできた、これは本当によかったです。公式サイトそのままよりは「少しだけ」見やすいものができた気がしてます。ひとまず目標達成。

Android の技術再確認

よく見る構成のアプリを「今の」「スタンダード技術」で 「丸ごと作成する勘所」はある程度身についたかなーと感じています。これで Android 老害な自分から少しは脱出できたかもしれませんw

よくなかったこと

ノリだけでプログラムファースト

「とにかくコード書きたい!作りたい!」が先行してしまい。プログラムファーストで進めた結果、UI 部分で何度も手戻りが発生してしまいました。時間が少ない時こそ、先にUIプロトタイピングツール等を使って先に検討したほうがよかったのかなーなんて思います。

\カラフル!/

\唐突な CardView !/

\迷走しだした!/

次回やりたいこと

ミュージシャン情報をもっと前面に表現したい

本当は地元ミュージシャンの特設ページ等を作りたかったのです。直接インタビューしたメッセージだったり、写真だったり。ミュージシャンのみなさま、来年はなにとぞご協力よろしくお願いいたします!!

地元の生演奏スポットへの誘導

中洲ジャズに行かれた方は、ジャズの生演奏を聴いて気持ちが高揚していると思うので、「ジャズバーや生演奏のお店に行ってみたい」という気持ちを後押しするような何かしらの導線を作りこみたかったです。

もっと見やすくしたい

正直まだまだ見にくいです。「どうせなら全ステージを同時に俯瞰したい」と友人からも意見をいただき。情報量を整理してうまいことできないかなーって思ってます。

モバイルサイトにすればいいのに

聞こえません。その話題には触れません。

アプリ作りの参考にしたもの

DroidKaigi 2017 official Android app

Android の技術イベント DroidKaigi の公式アプリです。アプリ内のパーツや作りは参考にさせていただいた部分が多いです。ソースの中身、非常に勉強になりました。Android オールスターズの集合知、すごい。 github.com

Material Design

UI、デザイン知識がほぼ皆無なので、色や各パーツの配置等はなるべくマテリアルデザインの原則に基づくように作ってみました。 material.io

最後に

実案件ではいくつかリリースしてるものがあるのですが、個人でアプリ作成してリリースしたのは今回初でした。副産物として、各種データのスクレイピングツールなどもできました。いまの実業務ではあまり使わないようなスキルが芋づる式に必要になってくるので、刺激も多くてやってて本当に面白かったです。エンジニアとしてチャレンジしてよかったなー、来年はもっと「ユーザ目線で」「実用的な」ものを作れたらなーって思ってます。

リリース後

いろんな方からアドバイスいただいたのですが、イベント当日までの期間が迫っており、すべてを対応することができませんでした。もっと早くから開発スタートしていればできたのに・・エンジニアとして悔しい気持ちです。特に @konifar さん、@fkm さん、@yuki930 さんに感謝。ありがとうございました。今後のアプリづくりと来年の中洲ジャズアプリに活かします!

AndroidStudio+GAE/J+JAX-RS(Jersey)

Java でサーバサイド書きたいけど Eclipse に戻りたくない、という理由で、まずは GAE/J+JAX-RS(Jersey) の環境構築手順を調べてみた。

手順

module 作成

  • [File > New > New Module] を選択
  • [Google Cloud Module] を選択
  • [App Engine Java Servlet Module] を選択

build.gradle

Servlet 作成

  • Jersey 実装した Servlet を作成

web.xml

コード

gist.github.com

動作確認

  • [Run > Run モジュール名] で作成した module を実行
  • アクセスして JSON 返却されることを確認
curl localhost:8080/jersey/events | jq .

f:id:dai4649:20160404130556p:plain

参考

qiita.com

Google Apps Script で spreadsheet のデータを JSON として読み込む

Android アプリに限らず、JSON データを表示するようなアプリって作ること多いですよね。で、自分はその時いつもサーバサイド作るのがめんどくさくなっちゃう。なので、spreadsheet のデータをそのまま JSON として返すようなサーバーサイドアプリを GAS(Google Apps Script) で書いてみました。いまさら何言ってんだ、いつのネタだよという感じだけど、がしがし開発するときにこういうのあるとやっぱり便利だった。

目的

こんな感じで spreadsheet に定義したデータが JSON で一括で欲しい。


f:id:dai4649:20140208155408p:plain


やること

  • spreadsheet 作成
    • 1行目は タイトル行(JSON のキーに該当)
    • 2行目以降は データ行
  • spreadsheet のスクリプトエディタに以下の gas を貼り付け
  • gas 内に以下を自分の環境に合わせて修正
    • spreadsheet のURL
    • spreadsheet のシート名
  • gas をウェブアプリケーションとして公開

確認

cURL で確認。

# curl -L 'GASの公開URL'
[{"name":"あああ","memo":"ジェッタシー"},{"name":"いいい","memo":"早寝早起き"},{"name":"ううう","memo":"ロングブレス"},{"name":"えええ","memo":"Aチーム"}]

ちなみに「curl 'GASの公開URL'」だとエラーになるので注意。


所感

GAS 便利。超便利。


Android Design のパターンを実装してみた(Patterns - Multi-pane Layouts)

Android Advent Calendar 2012 12/18(裏) 担当の @daichan4649 です。
よろしくお願いします。


経緯

Android Design のサイト、ありますよね。Android公式から提示されてるガイドライン。アプリはこう作ってねとか、画面遷移はこういう風に、とか、アイコンはこういうサイズで、とか。しかし、コード例へのリンクがない。理想のパターンを定義するなら一緒にコードも公開してくれたらいいのに。

なんて思いから 「自分ならこう実装する」 というのをやってみました。実際にはどう実装するのがベストプラクティスなのかわからないので、コード晒すのでみなさまぼこぼこにしてください。 #ドM


Multi-pane Layouts

Android Design にはいろんなパターンが書いてありますが、まずは Patterns の中から Multi-pane Layouts をチョイスしてみました。

Combining Multiple Views Into One
1. 同一apk で 小さい端末では 1pane 、画面大きめな端末では 2pane にする
2. 親(リスト) を選択すると 子(詳細) を表示する

よくある 2pane なアプリのパターンについて、ですね。とりあえず、このパターンのサンプルアプリ作ってみました。サイトではタブ表示になってるけども、今回そこは関係ないのであえて無視してます。


スクリーンショットです。


小さめの端末(Galaxy Nexus)

初期表示
f:id:dai4649:20121217235708p:plain

リスト選択
f:id:dai4649:20121217235722p:plain


大きめの端末(Nexus7)

初期表示
f:id:dai4649:20121217235746p:plain

リスト選択
f:id:dai4649:20121217235937p:plain


それではアプリの実装について説明してみます。


1. クラス構成

  • Activity 1つ
  • Fragment(リスト)
  • Fragment(詳細)

ここはシンプルに。


2. レイアウト

本当はもっと細かくサイズ指定すべきなんだろうけど、めんどくさいのでこんなかんじで2つ用意。

  • 画面小さめな端末向け(/layout)
  • 画面大きめな端末向け(/layout-large)



3. Fragment の表示/入れ替えロジック

画面小さめな端末(1pane)
  • 初期表示
    • コンテナ(android.R.id.container) を Fragment(リスト) で replace
  • リスト選択時
    • コンテナ(android.R.id.container) を Fragment(詳細) で replace
画面大きめな端末(2pane)
  • 初期表示
    • 左側に Fragment(リスト) を表示
    • 右側に コンテナ を表示(空)
  • リスト選択時
    • 右側のコンテナ を Fragment(詳細) で replace

xml ごとに構成を変えます。



で、この手の Activity1つ、Fragment複数、なパターンを使ったときにいつも悩むのが データの管理。コンフィグ変更(縦横切替等) で Activity が再生成された際の挙動を考慮して、こんな感じで考えてみました。


4. データ管理

CRUD

Fragment から Activity を参照(Fragment#getActivity)し、「必ず」 Activity 経由でデータをいじる。

保持

Activity 内でデータ保持


今回は 「コンフィグ変更(Activity#onDestroy)時にデータ破棄」 するような作りにしているため、Activity で単純にデータ保持しています。「コンフィグ変更時にデータ破棄」 させたくない場合は Fragment#setRetainInstance(true) な Fragment で管理すべき、だと考えてます。このあたりについては前に以下のエントリでまとめてみました。


Activity再生成時のデータの保存・復元(Fragment#setRetainInstance)



これを基にすると、各 Fragment でデータを表示するまでの流れはこのようになります。


5. Fragment でデータ表示するまでの流れ

  • Fragment(リスト)
    • Fragment のインスタンスは Activity 表示時 にレイアウトから生成
    • onActivityCreated で Activityからデータ取得
    • adapter に取得したデータを設定
  • Fragment(詳細)
    • Fragment のインスタンスは リスト選択時 にコードから生成(引数: 選択位置)
    • 生成時に引数で渡された 選択位置 を内部保持(Fragment#setArguments)
    • onCreateView で ↑の 選択位置 を基にして Activity から 表示用データ を取得(Fragment#getArguments)



アプリの構造、設計はざっくりこんなかんじです。コード本体は ここ(github) に置いてます。今回記載した以外のパターンについても実装したら(需要はおいといて)公開していこうと思っています。


最後に

去年 に引き続き、今年もこのようなすばらしい企画に参加させていただき、主催の @youten_redoさん、参加者の皆さま、本当にありがとうございます。来年も参加できたらいいな!明日 12/19 の担当は @androidsolaさん(表)、@furusin_oriverさん(裏) です!


追記

事件です。最新のADT (21.0.1 現在)では、マルチペインな雛形を自動生成できることが発覚しました!!m9(^Д^)
あれ、目から水が。

LruCache 使ってみた(android.util.LruCache)

GoogleI/O 2012 のセッションで LruCache の話が出てたので使ってみた。


Doing More With Less: Being a Good Android Citizen
pdf(右側の 「Session presentation」 リンク) の p.8 くらいから。


LruCache(android.util.LruCache)

  1. Least Recently Used アルゴリズム(wiki) を用いたキャッシュ管理クラス
  2. 中身は LinkedHashMap
  3. インスタンス生成時に保持キャッシュサイズの最大値を指定
  4. 要素がputされたときに↑を超えていたら既存要素を自動的に破棄
  5. API Level 12
  6. support library(v4) にもあるから APILevel が ↑以下でも使える


使い方は基本的にこんなかんじ。


実装方法

  1. LruCache の拡張クラスを作成
  2. コンストラクタ保持キャッシュサイズ最大値 を定義(↓のsizeOfの合計値上限)
  3. LruCache#sizeOf を override して 各要素のサイズ計算方法 を定義
  4. LruCache#entryRemoved を override して 各要素の破棄処理 を定義


サンプルコード。

private void testLruCache() {
    // cache要素最大数 10個
    LruCache lruCache = new BitmapLruCache(10);

    // cache要素追加
    String key = ~;
    Bitmap val = ~;
    lruCache.put(key, val);

    // cache要素取得
    Bitmap cache = lruCache.get(key);

    // cache解放処理(全要素)
    lruCache.evictAll();
}

private static class BitmapLruCache extends LruCache<String, Bitmap> {

    public BitmapLruCache(int maxSize) {
        super(maxSize);
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        // default では 1 を返却
        return super.sizeOf(key, value);
    }

    @Override
    protected void entryRemoved(boolean evicted, String key, 
        Bitmap oldValue, Bitmap newValue) {

        // cache解放処理
        if (!oldValue.isRecycled()) {
            oldValue.recycle();
            oldValue = null;
        }
    }
}


この実装の場合、こんな感じになる。

  • 保持キャッシュ(Bitmap)最大数は 10個
  • 保持キャッシュ数が 10個 を超えると要素が解放されていく(Bitmap#recycle)