2014年7月31日木曜日

本 : 『Steal Like an artist』 感想とレビュー


このエントリーをはてなブックマークに追加


クリエイティブの授業 STEAL LIKE AN ARTIST "君がつくるべきもの"をつくれるようになるために
(Kindle版は洋書のみ:Steal Like an Artist: 10 Things Nobody Told You About Being Creative。自分はこちらを読みましたが、難しい言い回しなどはなく、すごく読みやすかったです。)

とても良い本でした。自分が良いと思ったもの、好きなものから影響を受けた結果が自分自身や自分の作品になるんだ、だからいろいろ探そう、というお話。
You are, in fact, a mashup of what you chose to let into your life. You are the sum of influences. (...) We are shaped and fashioned by what we love.
創作以外の、生き方的な面でも参考になるので、軽く読める自己啓発本としても良いと思います。例えば7つの習慣などは正しすぎて少し重く、そうは言っても…と感じる面もあるのですが、好きなもの、尊敬できる人を真似よう、というやり方であればできる気が、やりたいと思える気がします。

印象的だった言葉を幾つか引用。訳は適当に自分で書いているので、雰囲気だけつかんで頂ければ。
if you copy from one author, it's plagiarism, but if you copy from many, it's research
(一人の著者からコピーしたらそれは盗用だが、たくさんからコピーすればそれは研究だ)
 たくさんの人の生き方を真似すれば、それが自分自身だ、とも言えます。
You don't like to look like your heroes. you want to see like you heros.
(ヒーローのように見えたいんじゃない。ヒーローのように見たいんだ。)
あの人だったらどう考えるんだろう、と考え続けるということ。
If you have two or three real passions, don't feel like you have to pick and choose between them. Don't discard. Keep all your passions in your life. (...) "Let them talk to each other. Something will begin to happen."
(本当に好きなことが2つか3つあるなら、どれかを選ばなくてはいけないなんて思う必要はない。捨てちゃダメだ。すべての情熱を持って生きるべきだ。(...) きっと何かが起こり始める。)
なにかを諦めて一つのことに専念するべき、という考え方もありますが、自分はこちらの考え方が好きです。好きなものがたくさんあるって良いことですよね。



全体の文量も長すぎず、一つ一つのメッセージの区切りが短いので読み返すにもちょうど良い感じです。おすすめ。

2014年7月29日火曜日

本 : 『The Functional Art』 感想とレビュー


このエントリーをはてなブックマークに追加


The Functional Art: An introduction to information graphics and visualization を読みました。インフォグラフィックスを広い範囲で解説している本で、インフォグラフィックスのそもそもの目的(示したい情報を明確にする事であって、単純化することではないし、もちろん装飾することでもない)や、どういう時にどういう図を使うべきか(値の比較をしたいなら、ほとんど常に棒グラフが優れている。円の面積や色の濃さによる表現は比較には向かない)などから、人がどのように視覚を認識しているかなどを扱っています。

個人的には 80 点くらい。こういう系統の他の本は特に読んだことがないので、なんとなくの採点です。こういう領域に興味があるのであれば、一読して損はしないと思いますが、あまり興味がなさそうな人に「これ面白いよ、勉強になるよ」と言って薦めるほどではない、という感じです。

Part 1 が、 foundation という事で、インフォグラフィックスとビジュアリゼーションの基礎や考え方の紹介です。もともとこの辺りが知ってみたかったので勉強になりました。結構理屈っぽいので、自分でインフォグラフィックスを作りたい、と思って読む人にとっては少しまどろっこしいかもしれませんが、いろいろ引用されていて、よく考えられて書かれている気がします。

Part 2 の cognition パートの、視覚と脳に関する話は、その辺りの知識がなかったけど興味があった、という人にとっては良いのかもしれません。Part 1 で紹介されている、どういう時にどういう図を使うべきか、どういう図がわかりやすいか、などの裏付けといった意味合いの気もするので、興味がなければ読み飛ばしても問題ない気がします。自分は他の本でこういう話は読んだ事があったので、軽く読み飛ばしました。

Part 3 は practice という事で、著者が扱ったプロジェクトについて。Part 1 の復習といった感じです。

Part 4 はインタビュー。

こうして振り返ると、Part 1 と Part 3 だけ切り出して、コンパクトにしてもう少し値段が手軽になっていればもっと良かったような気がします。Part 2 も Part 4 も価値がないわけではないのですが、削って安くできるならそうして欲しかったです。

3行まとめ

  • 伝えたいメッセージからインフォグラフィックスを作る。グラフィックス(見た目)を先行させない
  • インフォグラフィックスは情報を単純化するのではなく明確にする
  • 見る人の知性を信頼する

2014年7月27日日曜日

Java : Zip から複数ファイルを解凍して保存する


このエントリーをはてなブックマークに追加
  1. ZipInputStream を作る
  2. ZipInputStream.getNextEntry でエントリー(ファイル)を取得する
  3. ZipInputStream.read で中身を読み込む
  4. 読み込んだ中身を FileOutputStream に書き込む
  5. 2,3,4 の繰り返し
  6. ZipInputStream.close
以下、サンプルコード。Zip 内にディレクトリ構造がある事は想定していない、単純なコードにしています。
Android で外部ストレージ(SDカード)にエクスポートしたデータをアプリのディレクトリに戻すために使用したコードの一部。

try {
    // infilePath で zip ファイルが指定されているとする
    ZipInputStream zipInputStream =
            new ZipInputStream(new FileInputStream(infilePath));
    ZipEntry zipEntry;
    int len;
    byte[] buffer = new byte[1024];

    while((zipEntry = zipInputStream.getNextEntry()) != null){
        // outFolder で出力先のディレクトリが指定されているとする
        FileOutputStream fileOutputStream = new FileOutputStream(outFolder + "/" + zipEntry.getName());
        while ((len = zipInputStream.read(buffer)) > 0) {
            fileOutputStream.write(buffer, 0, len);
        }
        fileOutputStream.close();
    }
    zipInputStream.close();

} catch (Exception e) {
    // エラー処理
}

Java : 複数ファイルを Zip に圧縮して保存する


このエントリーをはてなブックマークに追加
  1. ZipOutputStream を作る
  2. zip に入れるファイル毎に ZipOutputStream.putNextEntry で ZipEntry を追加する
  3. ZipOutputStream.write を書き込む
  4. 2, 3 の繰り返し
  5. ZipOutputStream.close
Android でアプリのデータを外部ストレージ(SDカード等)にまとめてエクスポートするために使いました。 以下サンプルコード。
try {
    // String outfilePath で出力先のファイルパスが指定されているとする
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outfilePath));
    byte[] buffer = new byte[1024];
    int len;

    // 引数などで String[] files として zip に追加したいファイルのファイルパスが指定されているとする
    for(String file : files) {
        InputStream in = new FileInputStream(file);
        out.putNextEntry(new ZipEntry(file.getName())); // path名からファイル名だけにしておく
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
        in.close();
        
    }
    out.close();

} catch(FileNotFoundException e){
    // エラー処理
} catch(Exception e){
    // エラー処理
}

アニメ : アルドノア・ゼロ 感想 第4話まで


このエントリーをはてなブックマークに追加
今期一番楽しい。3話以降、結構火星側の機体に弱点があることも見えてきているみたいなので、これからも頭脳戦でどうにかしていく展開に期待できそう。
あと音楽もすばらしい。サントラ期待。でも挿入歌は別なのかな?

地球側の人々

イナホが冷静すぎて常識人の範疇を超えてるけど、なんか背景あるのかなぁ。インコは普通に有能な常識人、という感じ。生き残って欲しい。死んだときの絶望感は多分半端ない。カームに、火星殲滅すべしという強硬派になるフラグが立ってるけど、イナホとかと敵対する未来もありうるかも?常識人っぽいので大丈夫だとは思うけど。あとは15年前の事件の生き残りの大佐とか、その事を気にかけている艦長とかこの辺りの伏線回収が楽しみ。

スレイン

精神的な安定感のなさが、イナホとの対比になっている感じ。出撃志願は、前線で、姫の保護か、暗殺者側を牽制しようとするとかかな。4話までは地球側チームの活躍がメインなので、何かを決意したっぽいこれからどう動くかに期待。

姫様と影武者

影武者の利用について「懐疑派の護衛隊長が無理やり…」と4話で従者の人が言っていたけど、この護衛隊長はどの陣営で、どうなったんだろう?暗殺者側で、後で本物の姫様を殺せばいいと思っていたのか、単純に地球人を信用していなかったから影武者を使うことにしたのか。

前者なら姫様はどうやって逃げたのか?という疑問が起こるので、正解は後者で、現在隊長含めて護衛隊が一人も姫様の側にいないのは、あの暗殺事件で全滅したとかかな(地球人による暗殺を警戒していたのなら、護衛隊の内何人かくらい本物の方に残していそうなもんだけど)。

本物の姫様が「あれは影武者でした」と報告できる前に戦争始まっていること含めて、この辺が伏線なのかご都合なのかはよくわからない。姫様に裏があったらそれはそれで面白いけど、どちらかといえば純粋に姫様であって欲しい。

敵機

ニロケラスもそうだったけど、機体本体にはそれなりに地球側の攻撃が通るし、パワー比べでも一瞬で負けるわけではないのですね。火星移住から40年くらいらしいので、オーパーツ(アルドノア)で得た力以外はそこまで圧倒的な差はないということでしょうか。単機で突っ込める程性能の優位性はない気がしますが、そこは火星騎士同士の領土争い優先?今後火星本国から量産機投入とかありえるのかな?地球側としては物量作戦で各個撃破が可能そうだけど、通信系が破壊されているから逆に各個(隊)撃破されているという感じか。

用語

APU  : 補助動力装置(Auxiliary Power Unit)。メインエンジンを起動するために必要な圧縮空気の供給、また駐機中における各装置(エアコンなど)への動力の供給に用いられる。

HE弾 : 榴弾(High Explosive)。弾の内部に火薬が詰められた砲弾を指す。

AP弾 : 徹甲弾(Armor-piercing shot and shell)。装甲に穴をあけるために設計された砲弾。

ライデンフロスト効果 : 液体をその沸点よりはるかに熱く熱した金属板などの高温固体に滴らすと、蒸発気体の層が液体の下に生じて熱伝導を阻害するために、液体が瞬時に蒸発してしまうのを妨げる現象。
要するに、銃弾の沸点をはるかに超えたビームサーベルによって、銃弾とビームサーベルの間に気体が発生し、銃弾がビームサーベルを横滑りして弾かれた、ということかな?こんなの普通知らないし、知識として知っていても思いつかないでしょう。。。

2014年7月26日土曜日

Android : startActivityForResult がすぐ Activity.RESULT_CANCELED を返してしまう(もしくは ConnectionResult.startResolutionForResult が)


このエントリーをはてなブックマークに追加
原因は、startActivityForResult を呼ぶ起動元の Activity の launchMode を singleInstance にしてしまっていた事でした。singleTask に変更して解決。

<activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:launchMode="singleInstance" > <!--これ-->
    (...)
</activity>

launchMode については Y.A.M の雑記帳 : Android launchMode の違いに詳しく解説されているのでそちらを参照です。要するに、singleInstance の Activity はそれ単体でタスク(Activity のスタック)になるという設定なので、singleInstande のアクティビティから startActivityForResult を呼んで別の Activity を積むことができない、という事でした。

startActivityForResult で呼ぶ起動先のアプリが singleTask, singleInstance の場合も同じ問題が起こりそうです(singleTask, singleInstance のアクティビティはタスクのルートとなるので)。

Google Drive Android API を使おうとサンプルにそっていた際に、ConnectionResult.startResolutionForResult でこの問題に遭遇していたのでその辺りを探してしまっていましたが、問題はもっと基本的なところにありました。
無事、接続・認証の表示が出るようになり、これで先に進めそうです。

参考 : Android - startActivityForResult immediately triggering onActivityResult

2014年7月25日金曜日

Android : ActionBar の overflow アイコンを変える(ActionBar に入りきらなかったメニューをリスト表示するアイコン)


このエントリーをはてなブックマークに追加

アプリのテーマの Style で android:actionOverflowButtonStyleで指定します。styles.xml で以下のように設定。

<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light">
    <!-- Customize your theme here. -->
    <item name="android:actionOverflowButtonStyle">@style/ActionBarOverFlow</item>
</style>

<style name="ActionBarOverFlow" parent="@android:style/Widget.Holo.ActionButton.Overflow">
    <item name="android:src">@drawable/overflow</item>
</style>

ここで指定する src に指定するアイコン画像のサイズは、ActionBar のアイコンに指定するサイズ(32x32dp。xhdpi なら 64x64px、xxhdpi なら 96x96px。)と合っている必要があるようです。大きいサイズの画像を指定したところ、overflow メニューアイコンの表示が大きくなってしまいました。scaleType を fitCenter にしたところ画像のサイズは正常になったものの、アイコンサイズ自体は大きなままに。width 系のパラメータを設定しても変えられず。

ちなみに標準の overflow アイコンは ic_menu_moreoverflow_normal_holo_dark、ic_menu_moreoverflow_normal_holo_light、ic_menu_moreoverflow とかその辺りです。

参考 : Changing overflow icon in the action bar

Android : ActionBar のタイトルを2行表示にする(サブタイトルを表示する)


このエントリーをはてなブックマークに追加

この画像でいうところの「サマリー」がタイトル、All Web Site Data が「サブタイトル」になりますが、
サブタイトルは ActionBar クラスの setSubtitle 関数で設定する事ができます。
いろいろな種類のフラグメント、ビューを持つアプリの場合は、setTitle, setSubtitle を使って ActionBar に現在の状態を表示するようにしておくと良さそうです。

参考 : Action Bar Title with more than one line?

2014年7月23日水曜日

Android : Eclipse のプロジェクトを Android Studio に移行(インポート)する - Android Studio Beta


このエントリーをはてなブックマークに追加
検索の上の方にでてくる方法(EclipseのプロジェクトをAndroid Studioへインポートするとか)や、公式っぽいページ(Migrating from Eclipse)では Eclipse 側で変換が必要的なことが書かれていますが、Android Studio Beta (0.8.2) 現在では、Eclipse での変換は必要なく、ただ Android Studio を開いて File -> Import Project から Eclipse のプロジェクトのフォルダを選択すれば変換までしてくれるみたいですね。


そういえば、Google I/O 2014 - What's new in Android development tools でそんなことを言っていたような気もします。変換後は、変換のサマリーが出力されます。
以下、その他変換後に行ったこと。

文字コードの修正

変換前のソースファイルの文字コードが UTF-8 でなかったせいで、ソースコード中のコメントや文字列が文字化けしてビルドできなくなってしまっていました。
ひとまず、文字コード変換ツール「KanjiTranslator 1.6」 とやらで一括で UTF-8 に変換したら治りました。

Execution failed for task * . Duplicate files copied in APK META-INF/* の解決

META-INF/LICENSE, META-INF/LICENSE.txt, META-INF/DEPENDENCIES, META-INF/NOTICE, META-INF/NOTICE.txt などがこの Duplicate 云々で怒られました。 libs に apache-mime や httpclient を置いていたのですが、何かこの辺りのライブラリのファイルがコンフリクトしていたようです。[Android]AndroidStudio0.4がご機嫌斜めです を参考に、build.gradle の packagingOptions に exclude をいろいろ追加して解決しました。
android {
    compileSdkVersion 19
    ...
    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }
}

build.gradle に versionCode, versionName を追加

versionCode などは AndroidManifest.xml に入ったままで、build.gradle の方にはなかったようなので defaultConfig の中に versionCode と versionName を手動で追加しました。

2014年7月16日水曜日

Android : SQLite で ORDER BY と LIMIT を使用する


このエントリーをはてなブックマークに追加
ORDER BY と LIMIT を使う場合は

public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)

を使用します。
orderBy句には "コラム名 ASC(もしくはDESC)"、でソートに使う列を、 limit句には "1" のように文字列の形で取得制限数を入れます。 以下は、一番古いエントリーの日付を取得する、というサンプルです。 (日付のエントリーとして使っている値は独自形式でただの int です。)
int getFirstRecord(long id){
    SQLiteDatabase db = getReadableDatabase();
    String[] columns = new String[]{ COLUMN_DATE };
    assert db != null;
    Cursor cursor = db.query(TABLE_NAME, columns,
            String.format("%s = %d", COLUMN_ID, id), null, null, null, String.format("%s ASC", COLUMN_DATE), "1");
    int dateEntryValue = -1;
    if(cursor.moveToNext()){
        dateEntryValue = cursor.getInt(0);
    }
    cursor.close();
    db.close();
    return dateEntryValue;
}

Android : SQLite でレコード数を調べる - COUNT を使用する


このエントリーをはてなブックマークに追加
SUM, TOTAL の場合と同じく、rawQuery を使用して1行1列の Cursor を取得し、その結果を cursor.getInt(0) で取得する、という流れです。以下サンプル。SQLiteOpenHelper 内を想定して getReadableDatabase() を使っています。


int getRecordCount(long id){
    SQLiteDatabase db = getReadableDatabase();
    Cursor cursor = db.rawQuery(
        String.format("SELECT COUNT(*) FROM %s WHERE %s = %d", TABLE_NAME, COLUMN_ID, id), null);
    int count = 0;
    if(cursor.moveToNext()){
        count = cursor.getInt(0);
    }
    cursor.close();
    db.close();
    return count;
}

2014年7月15日火曜日

Android : SQLite で SUM (TOTAL) を使って合計を出す


このエントリーをはてなブックマークに追加
rawQuery を使うようですね。
rawQuery で SUM もしくは TOTAL を使った場合、行、列ともに 1 の Cursor が返ってくるそうです。
テーブル名の指定方法や WHERE 句を使うかは場合によりますが、
以下サンプルコードです。
(SQLiteOpenHelper を継承したクラス内を想定して getReadableDatabase() を使用しています。)

SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery(
    String.format("SELECT SUM(%s) FROM %s WHERE %s = ? ",
    COLUMN_POINT, TABLE_NAME, COLUMN_ID), new String[]{ String.valueOf(id) });
int point = 0;
if(cursor.moveToNext()){
    point = cursor.getInt(0);
}
cursor.close();
db.close();

ちなみに SUM と TOTAL の違いは
  • カラムの値が NULL だけだった場合、SUM は NULL を返す。TOTAL は 0.0 を返す。
  • カラムの値が整数値のみだった場合、SUM は整数を返す。TOTAL は浮動小数点を返す。
という事らしいです。
cursor.getInt() は column の値が NULL だった場合 Exception を投げるような気がするので、NULL の可能性がある列で SUM を撮る場合は try catch で囲ったほうが良いのかもしれませんね。(そういう列で SUM を取っていないので未確認。)

参考 : Fetching a SQLite SUM in Java on Androidsum関数 / total関数

超幻想郷級のダンガンロンパ 感想と考察 Part 15


このエントリーをはてなブックマークに追加
前回(Part 14)の感想と考察
次回(Part 16)の感想と考察

Part 15 にしてついに最初の殺人が…。こう来るとは予想していなかった…。
とりあえず Part 15 で気になった場所だけ挙げておきます。
「第一の殺人まで」としてまとめたものもどっかでやりたいなぁ。Part 16 までにできるかなぁ。。。


第一の殺人


事件発生前後の動きは
  1. 0:55 射命丸が霍青娥の部屋に到着。部屋に鍵はかかっていない。
  2. 0:55~ から射命丸が個室の部屋に鍵を掛け、部屋の中を捜索
  3. 0:59直前 ルーミアが霍青娥の部屋のドアの前にきて「人が殺されている」と伝える。
  4. 0:59  ビデオ録画開始
  5. 1:00 ルーミアの暗闇発動(停電であればカメラのライト等が入りそうなので、フェイクではないでしょう)
  6. 1:00~ 部屋の扉が開く。鍵、マスターキー?ただし部屋に入ってきた気配はしない。シャワー室の扉が開く音。人が入ってきた気配はしない。鈍器で人を殴り倒したような音。再びシャワー室の扉の音。足音は聞こえるのに気配を全く感じない。個室のドアの音。誰かが出て行った?
  7. 1:03 暗闇終了。部屋に異常はなし。シャワー室の扉、部屋の扉は両方閉まっている。
  8. 1:04 シャワー室の扉を開く。誰もいない。振り向くと誰かが立っていた。文の第一声は「あれ、どうしたんですか?」。凶器が振り下ろされて文が殺される。
「霍青娥の目的、役割」、「ルーミアの行動の目的、役割」、「気配がせず音だけが聞こえていたという状況」、「扉の鍵を開けた方法」辺りが気になりますね。

特にルーミア。パーティー前にチルノに「願い事を決めて」と言っていたのは、「絶望を殺す」という意思表示でしょう?ドアの前で「人が殺されている」と叫んだのは絶望であると思っていた霍青娥をおびき寄せるため、霍青娥が絶望であれば、殺した時点で勝利なので「人が殺されている」と言って呼びだそうとした事が周りに知られていても関係なくなる、ということで、ルーミア本人が霍青娥を殺すためにとった行動と考えても、ここまでは違和感はないのですが。。。

しかし、そう考えると、暗闇を発動して霍青娥の部屋の扉を開けたそれ以降の行動の意味が上手く繋がらないんですよね。おびき出すのに失敗したから突っ込みにいったという推測は、気配がしなかったという情報で潰されそうな気がしますし、『超高校級の暗闇』はルーミア自身も視界を奪われるという制限があるので、自由に行動するには暗視ゴーグルが必要なんですが、暗視ゴーグルはマミゾウが隠していて再回収の対象になっておらず、取得できていない状況な気が(これはかなり確度の低い推測ですが)。

あと凶器の模擬刀は重いのでルーミアは使用不可ですから、少なくとも文殺害の実行犯ではないですね(そもそも振り向いた時にルーミアがいたら文は警戒するでしょう)。暗闇の中でしたという、鈍器で人を殴り倒した音、というがリアルタイムだとしてもルーミアではないと言えます。
ルーミアが誰かと共謀するとすればチルノと思われますが、チルノも模擬刀使用不可なので実行犯にはなれませんし。実は凶器は氷でワンチャン…?うーん、ダメですね、まとまりません。なんとなくルーミアは誰かに利用されているだけのような気がするのですが、さてそれが誰で、どうやってかとなるとさっぱり。扉を開けた後の行動の謎を解かないことにはどうしようもなさそうです。

次に霍青娥。この状況で何も噛んでいないという事はなさそうですが…。文の警戒対象でもありますし、実行犯とは考えにくいです。ルーミアとの共謀も、ルーミア側が信用しなさそうですし、そもそも共謀には原作同様メリットがないです(あるとすれば絶望を殺す場合のみ)。わからないなー。狛枝化していくのかなー。

扉を開けた方法で考えられる方法は二通りで、まずはマスターキー。こちらはシャッフル済みなので誰が持っているかは不明。二番目は霍青娥の部屋の鍵そのもの。魔理沙なら盗めるので可能性はそこまで低くない。現時点では情報がなさそうなので保留。

文があの状態で警戒しない対象は…と考えてみたんですが…「あれ、どうしたんですか?」くらいだったら言い方次第な気もするので、ここで変に絞っても良くなさそうです。警戒度が低そうなのは霊夢、マミゾウ、さとり、魔理沙、早苗辺りかなぁ。意外にも、さとりを含めても模擬刀使用不可の小柄な者リストに彼女らは含まれていませんね


パーディ前とパーティ中の行動


まずはパーティ前。
  • ルーミアの、チルノに「願い事を決めて」と言っていたのは前述の通り絶望を殺す宣言だと思います(が、その後の行動を上手く説明できていないので、もしかしたら違うのかも)。
  • ナズーリンとレミリアが3時間衣装選びをしていたというのは、何かのアリバイになるのかどうか。勇儀と霍青娥もずっと一緒にいた事になっていますね。
  • テイが購買部で何か手に入れている
パーティ中
  • パーティに最後に来たのはさとり
  • ジェラルミンケースが開いている
  • テイが、少なくとも勇儀と妹紅にぶつかっている
マスターキーの説明書きは「ゲームの会場にあるすべての扉を開くことが出来る鍵」で、極上の凶器が入っていたジェラルミンケースは多分「蓋」ですが。。。部屋の鍵で開けられる以上、マスターキーで開けられると言われても違和感ないですね。マスターキー持ちの誰かか、早苗の鍵を難なく盗める魔理沙か。魔理沙の能力は万能すぎてどうも疑ってしまいますね。。。
あと、さとりが最後に来た、という情報も敢えて触れられていたのも気になります。


昨日の夕食後の行動


霍青娥がくれたリストの情報より。
  • 麻雀ではさとりが大勝ちし、霍青娥、ナズーリンはほぼ±0。テイが一人負け
  • ボードゲームでは魔理沙が圧倒的な一位。早苗が終盤で最下位に転落
  • ルーミアとチルノのバスケはルーミアが圧倒的に優勢。
  • バーでは勇儀が所持メダルを全て使いきり、マミゾウから借りたメダルも全て使用した。
奇跡、幸運がともに運が絡むゲームで最下位で、奇跡と幸運は絶望でない限り任意解除不可のスキルなんですよね。テイの場合は貸し出しが可能ですが、その時点から貸していたとすれば誰に、何の目的で、何と引き換えに…?(テイがパーティで人にぶつかっていたのは幸運貸し出しによる不運なんでしょうか?あとテイに関して、幸運の貸し出しと返却の手続きが不明瞭なのもそろそろ気になります。)
早苗の方は、魔理沙が奇跡の及ばないレベルのイカサマを仕掛けたと考えれば一応の説明はつきますが、果たして。

それから、メダルを貸すって結構リスキーな行為だと思いますけどね。一日の支給枚数(=総量)が決まっていますし、これから何が入荷されるかもわかりませんし。この証拠とバスケの証拠をどう考えるべきかは今のところよくわからないです。


You have 10 Days Left watch up!


さとり曰く偽のメッセージ。字について言及があるのは霍青娥だけですが、これも何とも言えません。Part 10 ~ 13 の考察(というかただの当てずっぽう)でさとりを一章の『クロ』と予想していたこともあって、さとりを信用すべきなのかどうか微妙という心境でもあります。


告発状


それ自体は割りとどうでもよいのですが、マミゾウの強かさが垣間見えましたね。霍青娥がマミゾウにも渡した、とバラせばそれまでではあるので、そこまで深い意味はなさそうですが。あとは咲夜が破って捨てたというのが微妙に気になる程度でしょうか。
Part 14 によると最初の殺人者は告発状持ちですが、文、霊夢以外だいたい持っていそうな雰囲気です。
(内容が全員同じかどうか裏がとれていないことが引っかかる程度)



まだ何かあった気がしますが、相変わらず長くなりすぎたのでここまで。。。

しかし、最終的にこれだけシリアスになりながら、そこまでしっかり笑いもとってきていて、
ストーリーの進め方が上手いですね本当に。Part 16. も楽しみです。
(主人公誰になるんだろう)

…そういえば殺されたキャラが出たことで初めて『追放』が発生しますね。
元に戻らないとはどういう意味なのか、もいよいよ明らかになりそうです。
(『追放』状態の文が主人公続行とかもありえる?)

次回(Part 16)の感想と考察

2014年7月12日土曜日

Android Studio : Live Template の使い方(追加方法、変数の設定方法)


このエントリーをはてなブックマークに追加
Live Template についての公式ドキュメントはこちら(英語)。よく使うコードなどを登録して簡単に挿入できるようにできます。登録したテンプレートは何もせずともコード入力中にサジェストに出現します。Ctrl + j を押すことで明示的にテンプレート呼び出しを行うこともできます。


Live Template の追加方法


Ctrl + Shift + A で Live Template と入力して設定画面を開きます。
右の方の緑の + ボタンで「Template Group」と「Live Template」を追加可能です。
Template Group は管理用のグループで、Live Template がテンプレートそのものです。

自分は Android 系のテンプレートは Android というグループを作ってその中に Live Template を入れています。このグループが Live Template の保存先のファイルになるので、人と共有するようなものは分けておくとよいと思います。登録した Live Template のグループはドラッグ&ドロップで後で変更可能です。(Windows では <ホームディレクトリ>\.<製品名><バージョン>\config\templates\<グループ名>.xml というファイルに Live Template が保存されます。)

また、Live Template はコードエディタでコードを選択した状態で Save as Live Template を実行(Ctrl + Shift + A で live template と入力すれば見つかります)しても追加することができます。




Live Template で設定できるテキストボックスの意味はそれぞれ以下の通りです。
  • Abbreviation : テンプレートの呼び出し時に使う文字列
  • Description : テンプレート呼び出し時に右側に表示される説明
  • Template Text : 展開されるコード



Template text の下にある Applicable in Java: statement. という箇所は、どのファイル(xml や Javaなど)の、どんな状況でこのテンプレートを有効にするかを選択する箇所です。
スニペットのように使うのであれば Java: statement で問題ありません。

右側のチェックボックスの一番下、Shorten FQ names の FQ とは fully qualified names という意味です。チェックをつけておくと、このテンプレートを展開したときに import に android.util.Log が追加されて、展開された場所では Log.d からコードが始まるようになります。


Live Template 内の変数定義(公式ドキュメントはこちら(英語)


Template Text 内で変数を使う場合、$変数名$ と、$マークで囲みます。
($そのものを文字として使用する場合は $$ と二回重ねて書きます。)
変数名として $END$$SELECTION$ は予め意味が定義されていてそれぞれ以下のようになっています。
  • $END$ : テンプレートが展開された後のカーソル位置
  • $SELECTION$ : "surrount with" タイプのテンプレートで使用され、選択中のテキストを指す
変数を定義している場合、Edit Variable ボタンでデフォルト値を編集できます。
デフォルト値には、テンプレートを展開した位置のクラス名や関数名、行番号を取得して挿入できる Expression という項目と、Expression が空の場合や失敗した場合に使用される Default Value という項目があります。

例えば、上の画像の Log.d を挿入するテンプレートの変数は以下のようにして、Expression で定義されたメソッドを使用して、呼び出した場所のクラス名とメソッド名が自動で入るようにしています。Skip if defined にチェックをしておくと、テンプレートを展開したときに自動で挿入された変数は飛んで、次の入力に移ります。この場合、このテンプレートでは MSG のみ入力することになります。


また、ここで文字列(Expression 以外)をデフォルトに設定する場合は、" (ダブルクオーテーション)で囲む必要があります。" は展開された時には挿入されません。


それでは、良い Android Studio 生活を。

2014年7月11日金曜日

Android : 『色つき星取表』的なアプリのリリース


このエントリーをはてなブックマークに追加
久しぶりに新しい Android アプリ『星取り活動記録表』をリリースしました。


活動の習慣化の支援、もしくは習慣的な活動の記録を目的としたアプリです。

もしかしたら名前からなにか感じ取られた方もいるかもしれませんが、インスパイア元は『色つき星取表』です。この『色つき星取表』を上手く使いこなして活用できていればそれで良かったのですが

  • そんなにたくさんの種類の活動はしていない
    • 活動毎の比較ではなく、それぞれの活動を最近きちんとできているかを把握したい
  • 表計算ソフト(Excel、Google Spread Sheet)を立ち上げるのが面倒くさい
  • スマートフォンで入力を済ませたい
  • テキスト入力したくない
などの諸々の理由から、レイアウトを検討したりと機能を削ったりと調整してアプリとして作成しました。最終的には色つき星取表というよりは、カレンダーにチェックをつけるアプリの、チェックが3段階になった、という感じになりました。

公開時のバージョンは 0.8.0 です。
現在は入力のしやすさと活動毎の活動量の把握に重きを置いた単一のビューしかありませんが、色つき星取表のような、活動毎の比較や、日付、曜日ごとの頑張った度合いを見られるようなビューも、別ビューとして追加しようかな、と検討中です。

何か要望や質問がありましたら、アプリ内の『開発者にメールを送る』メニュー、もしくはここからどうぞ。

2014年7月10日木曜日

Eat one’s own dog food (dogfooding) の由来


このエントリーをはてなブックマークに追加
英語版の Wikipedia の Origin of the term によると

  • 1970 年代、Alpo dog food という会社の TV広告で、Lorne Greene (俳優)が自分のペットに Alpo を与えているといると言った
  • 1988 年、マイクロソフトのマネージャーだった Paul Maritz が "Eating our own Dogfood" がというタイトルのメールで、テストマネージャーの Brian Valentine に自社製品をもっと使うよう迫り、そこから広まった
という 2つの説が書かれていました。
前者が、今ソフトウェア業界で広く使われるようになったきっかけとはあまり思えないので、
後者が直接的な由来な気がしますね。(じゃあなんで Paul さんは "Eating our own Dogfood" というタイトルを使ったのか?という疑問は残りますが…。当時の自社製品を卑下して Dogfood と呼んだんでしょうか?)

結構古かったこと、Microsoft が元であったということに驚きました。

2014年7月8日火曜日

Android : BOOT_COMPLETED を受け取る BroadCastReceiver のデバッグ - adb shell を使って BOOT_COMPLETED intent を発行する


このエントリーをはてなブックマークに追加
Android の platform-tools ディレクトリでコマンドプロンプトを開き(Shift 押しながら右クリック + w)、

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED

と実行すると BOOT_COMPLETED イベントが発行されます。
Logcat の出力内容を見るなどして、BroadcastReceiver がきちんと処理をしているか等を確認します。

adb shell

で shell を起動してから、

am broadcast -a android.intent.action.BOOT_COMPLETED

でも同じことができます。
am は Activity Manager の略で、ここでは -a オプションで指定した intent action (BOOT_COMPLETED) の broadcast を投げていることになります。他のコマンドなどを含めて、詳しくはリンク先を参照して下さい。

2014年7月7日月曜日

Android : ListView と GridView の違い - 画面サイズに応じて ListView と GridView を切り替えるパターンで GridView の場合にパフォーマンスが低下する


このエントリーをはてなブックマークに追加
ListView と GridView の違いは、GridView なら複数列使用できる、というくらいだと思っていたのですが、そうではなかったというお話です。GridView の方が getView の呼び出しがかかる事が多いです。

参考 : Multiple calling of getView() in GridView

AndroidStudio では、New -> Fragment -> Fragment (List) というメニューがあり、これを選択して、「Switch to grid view on large screens?」というオプションにチェックを入れておくと、画面サイズに応じて ListView と GridView を切り替えてくれる Fragment と xml ファイルが追加されます。
(Use Layout Aliases というドキュメントにある手法を使っています。)

これは便利、と思っていたのですが、Nexus7 (GridView) でデバッグしていると Nexus5 (ListView) でデバッグしているときと比較して明らかにパフォーマンスが落ちることに気がつきました。調べていくと、謎のタイミングで getView() が呼ばれていることがわかりました。

  • 最初のレイアウトの時点で、子の View の数以上の getView が呼ばれている
  • 子の View をクリックした際に呼ばれている
    • notifyDatasetChanged() 等は使っておらず、ListView の時は getView が起こらないことを確認済み
  • GridView の高さが変わった時に呼ばれている
    • 広告が表示されたタイミングで GridView の高さ(Padding の大きさ)を再設定するようにしていたのですが、ここでも、GridView の場合のみ getView が呼ばれていました。

グリッドを正しく複数行に表示するための処理と思われますが、なかなか厄介です。
参考先では、先頭の View で getView が  33回呼ばれていた、というような絶望的な報告もありました。View をキャッシュしておくことで対応したそうです。getView の中身が相当軽い場合はそのままでよいかもしれませんが、GridView を使う場合は view の内容をキャッシュする、というテクニックは覚えておいた方がよさそうです。(メモリは使うでしょうが、単純に getView の中身が重い場合の ListView にも使えそうです。)

2014年7月6日日曜日

Android : TimePickerDialog で onTimeSet に登録した関数が Cancel 時にも呼ばれる(そして2回呼ばれる)


このエントリーをはてなブックマークに追加
Android 4.1 (Jelly Beans)以降からのバグで、
Dialog の onStop 時にも onTimeSet が呼ばれるようです。
Back キーなどでキャンセルした場合でも呼ばれてしまうという厄介な挙動。

参考1: TimePickerDialog and Jelly Bean, onTimeSet fires on cancel
参考2: Android 4.1.2 dialogs are called twice

参考先では onTimeSet が呼ばれた回数をカウントする方法が紹介されていましたが、
この先どういう挙動になるかもわからないので、
AlertDialog の View に TimePicker をセットして
setPositiveButton の callback で TimePicker の値を取得する方法をとることにしました。
TimePickerDialog でバグを回避する方法を頑張るよりもわかりやすい気がしますね。
setNegativeButton の有無によるキャンセルボタンの有無や、タイトルの設定もしやすいです。

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("My Time Picker Dialog");
final TimePicker tp = new TimePicker(getActivity());
// 初期値を設定したい場合はここで。
// tp.setCurrentHour(0);
// tp.setCurrentMinute(0);
builder.setView(tp);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialogInterface, int i) {
        int hour = tp.getCurrentHour();
        int minute = tp.getCurrentMinute()
        // 処理をする…
    }
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.create().show();

2014年7月3日木曜日

Android : Android Studio で Admob を使用し広告を表示できるようにする


このエントリーをはてなブックマークに追加

ライブラリの追加 : build.gradle

builde.gradle の dependencies に play-services への依存を追加します。
AdMob は Play Services の一部になっています。
(以下の例の、play-services 以外はプロジェクトのデフォルトで設定されていたものです。)

dependencies {
    compile 'com.android.support:support-v13:19.+'
    compile 'com.android.support:support-v4:19.+'
    compile 'com.google.android.gms:play-services:+'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

追加後、Sync Project With Gradle ボタンを押します。


以降は、Google Mobile Ads SDK - スタートガイドに沿って AndroidManifest.xml の設定を行い、バナー広告1 に進み AdView の追加と広告のリクエストを行えば広告が表示できます。
AdView の API リファレンスはこちら
以下、必要な事を簡単にまとめておきます。


AndroidManifest.xml

INTERNET と ACCESS_NETWORK_STATE のパーミッションを manifest タグ下に、
meta-data を application タグ下に、
AdActivity を application タグ下に、
それぞれ追加します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.addmob" >

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <meta-data android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version"/>
        <activity
            android:name="com.example.addmob.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.google.android.gms.ads.AdActivity"
            android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
    </application>
</manifest>


AdView の追加と広告の表示

xml で AdView のレイアウトを追加する場合は以下の様な形式になります。
UNIT_ID は AdMob のページで作成した ID です。(ca-app-pub-XXXX....)
<com.google.android.gms.ads.AdView
        xmlns:ads="http://schemas.android.com/apk/res-auto"
        android:id="@+id/adView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        ads:adUnitId="UNIT_ID"
        ads:adSize="BANNER"/>

AdView を加えただけでは広告は表示されないので、コード中で広告をリクエストする必要があります。

AdView mAdView;
...
mAdView = (AdView)findViewById(R.id.adView);
AdRequest adRequest = new AdRequest.Builder().build();
mAdView.loadAd(adRequest);

また、AdView の処理の中断と再開、終了をきちんとおこなうため、Activity(Fragment) の onPause, onResume, onDestroy で AdView の pause, resume, destroy も呼んでおきます。

@Override
protected void onResume() {
    super.onResume();
    mAdView.resume();
}

@Override
protected void onPause() {
    mAdView.pause();
    super.onPause();
}

@Override
protected void onDestroy() {
    mAdView.destroy();
    super.onDestroy();
}

2014年7月2日水曜日

Android : メールを送る Intent


このエントリーをはてなブックマークに追加
色々方法はあるようですが、Stack Overflow : Send Email Intent によると下記の方法が安定して動くようです。

Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(
                            "mailto", "abc@gmail.com", null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "SUBJECT");
emailIntent.putExtra(Intent.EXTRA_TEXT, "TEXT BODY");
startActivity(Intent.createChooser(emailIntent, "Send email..."));

Android : PreferenceFragment (PreferenceScreen) にボタンを追加する


このエントリーをはてなブックマークに追加
参考 : How to add a button to a PreferenceScreen (Android)

ボタンそのものを追加というよりも、ボタンのように、押したら決められた動作(例えばダイアログを開いたり、メールを開くための Intent を飛ばす)を行う項目の追加ですが。

xml 中に Preference を作成して、PreferenceFragment で addPreferencesFromResource を行った後、findPreference でその Preference を取得して OnPreferenceClickListner を登録、という流れで行います。

<Preference android:title="Acts like a button"
            android:key="@string/button_pref_key"
            android:summary="This will act like a button"/>

Preference button = findPreference(getString(R.string.pref_button_key));
button.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
    @Override
    public boolean onPreferenceClick(Preference pref) { 
        // ここでやりたいことをやる。
        return true;
    }
});

2014年7月1日火曜日

Android : PreferenceFragment で Preference の変更内容を summary に反映させる


このエントリーをはてなブックマークに追加
参考 : Android Preference の summary を動的に変更

SharedPreferences.OnSharedPreferenceChangeListener で Preference の変更を検知し、
そこで渡された key から、 findPreference(key) で Preference を、
sharedPreferences.getXXX(key, default_value) から値を取得する。
setSummary(summary) などで表示を更新する。
public class SettingFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener{

    public SettingFragment() {
        
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.setting);
    }

    @Override
    public void onResume() {
        super.onResume();
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);

        Preference p = findPreference("TEST_PREFERENCE_STRING_KEY");
        String value = PreferenceManager.getDefaultSharedPreferences(getActivity()).getString("TEST_PREFERENCE_STRING_KEY", "default_value");
        p.setSummary(value);
    }

    @Override
    public void onPause() {
        super.onPause();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        Log.i("onSharedPreferenceChanged", key);
        if(key.equals("TEST_PREFERENCE_STRING_KEY")){
            Preference p = findPreference(key);
            String value = sharedPreferences.getString(key, "default_value");
            p.setSummary(value);
        }
    }
}

Android : Fragment で menu を追加する


このエントリーをはてなブックマークに追加
Fragment の onCreate() 内で setHasOptionsMenu(true); を呼び、
onCreateOptionsMenu 内で追加したいメニューのリソースを inflate する。
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    inflater.inflate(R.menu.fragment_menu, menu);
}

Android : ListPreference.OnDialogClosed で NullPointerException


このエントリーをはてなブックマークに追加
参考 : PreferenceActivity gives a NullPointerException when option selected from ListPreference
(PreferenceActivity でも PreferenceActivity でも同じです。)

ListPreference に設定する android:entryValues に integer-array を設定していると
エラーが発生します。
entry も entryValues も両方 string-array である必要があります。

entryValues で string 以外が使えるようにしてほしい、という要望はかなり前から上がっていますが、対応はされていないようです。
Issue 2096: Expand ListPreference to support alternate array types

Android : リソース id を文字列変数から取得する、コード上で生成する、xml で生成する


このエントリーをはてなブックマークに追加

文字列からの取得

Resources クラスの getIdentifier メソッドを使います。
リソース id の文字列と、種類(id, string, drawable 等) を入力に、id を返します。
int id = getResources().getIdentifier(
String.format("test_id_%d", 0), "id", getActivity().getPackageName());

コード上で生成

API Level 17 (Android4.2) 以上限定。
int viewId = View.generateViewId();

xml リソースで生成

<resources> タグの中で <item> タグに type="id" を指定した項目を作成します。
strings.xml 等の中に作っても良いのですが、
res/values/ids.xml のように別ファイルにしておいた方が管理しやすいと思います。
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="test_id_0" type="id"/>
    <item name="test_id_1" type="id"/>
</resources>