Unmotivated

やる気はない

Strings などのリソースを動的に取得する

Android において、静的にリソース名を指定せずに、動的にリソースIDを取得したいことが時々あります。

ある範囲のリソースが欲しいなど、一定の規則でリソース名が決まっている場合は以下の方法でリソースが取れます。

1
2
3
4
5
6
7
// prefix0, prefix1, prefix2, ... と言う名前のリソースを取得する
for(int i=0;; i++) {
    int resourceId = getResources().getIdentifire("prefix" + String.valueOf(i), "string", getPackageName());
    if(resourceId == 0) break;
    // リソースを取得
    String str = getResources().getString(resourceId);
}

全てのリソースが欲しい場合は Reflection でなんとかなります。
例えば Strings に定義されたリソースを全て取得したい場合は、以下で取得可能です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Field[] fields = R.string.class.getFields();
for(Field field:fields) {
    try {
        int resourceId = field.getInt(R.string.class);
        if(resourceId != 0) {
            // リソース名
            String resourceName = field.getName();
            // リソースを取得
            String str = getResources().getString(resourceId);
        }
    }catch(IllegalAccessException e){
        Log.d(TAG, e.getMessage());
    }
}

Google Glass レビュー

ついにねんがんの Google Glass をてにいれたぞ!(今更)

Google Glass Box

ざっと触ってみたところで、使用感などのレビューをしてみます。

目次

  • Glass の見え方
  • 操作
    • 音声入力
    • タッチパネル
    • その他の入力
  • デザイン
  • スペックやバッテリなど
  • まとめ

9-Patch のコンテンツ領域は伸縮しない

9-Patch でコンテンツ領域を指定することはよくあるけれど、実は NinePatchDrawable を Background とした View のサイズを変更しても、コンテンツ領域(というか Padding) はうまい感じに伸縮してくれない。

たとえば次のような 9-Patch の画像を用意してコンパイル、NinePatchDrawable として読み込んだ場合を考える。

star

XVim を Xcode 5.1 にインストール

vim キーバインドじゃないとなにもやる気が起きないので、 Xcode 5.1 に XVim を導入します。

インストール

1. リポジトリをクローン

  • clone
1
$ git clone https://github.com/JugglerShu/XVim.git
  • XCode 5.1 の場合は develop Branch を使うように指定があるので、 develop Branch を Checkout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ cd XVim
$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/bang
  remotes/origin/dev/buffer-refactor
  remotes/origin/develop
  remotes/origin/features/hlcolor#458
  remotes/origin/features/substitute
  remotes/origin/features/unittest
  remotes/origin/features/vs-sp#365
  remotes/origin/gh-pages
  remotes/origin/handlingMouseEvents
  remotes/origin/master
  remotes/origin/v2.0-dev/Logging#449
  remotes/origin/windowing
$ git checkout develop
$ git branch
* develop
  master

2. ビルド〜インストール

  • Xcode を開いて XVim.xcodeproj をロード
  • スキームを Xcode5 に変更 xvim 01
  • Edit Scheme を開いて xvim 02
  • Build Configuration を Release に変更 xvim 03
  • ビルド&実行すると自動的にプラグインに登録される
  • Xcode を再起動

.xvimrc を設置

  • ~/.vimrc の代わりに ~/.xvimrc が適用される
  • 使用可能コマンドの一覧はこちらを参照

Android の Resources における画像(Drawable)についての話

Android で Resources から getDrawable() などして画像をとった場合、一体それはいつどんなタイミングでメモリに読み込まれているのか? キャッシュはされているのか?

いい加減把握しておきたかったので、これも調べました。

API 17(4.2.2_r1)で見た結果なので、他のバージョンのSDKでも同様かはわかりませんが、多分同じ感じだと思います。

GestureDetector でリスナに渡ってくる MotionEvent が Null になる

タッチジェスチャを検出する場合は、 GestureDetector に次のようなリスナを渡して判定するのが常套手段ですが、onScroll()onFling() の第一引数の e1 にはジェスチャの開始地点の MotionEvent が入ります。
ところが、時々 e1 に null が渡ってくることがあって困りました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private class MyGestureListener extends SimpleOnGestureListener {

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        // なぜか e1 が null !!
        return true;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2,
            float velocityX, float velocityY) {
        // なぜか e1 が null !!
        return true;
    }

}

ググるといくつか stackoverflow が見つかったけど、どれも根本解決には至っていない様子。

というわけで、原因を調べてみました。

結論を先に言うと、 ACTION_DOWN のタッチイベント(MotionEvent)を GestureDetector#onTouchEvent() に渡せていないのが原因でした。

Android におけるストレージまとめ

Android で外部ファイルを保存する場合、どこに何を保存すれば良いのか?それぞれの違いは何なのか?
内蔵メモリなのか?SDカードなのか?

いい加減しっかり把握しておきたいと思ったので、まとめてみました。

View のキャプチャを撮る

Android で View のキャプチャ画像を取得する方法を二種類紹介します。

どちらも UI スレッド以外のスレッドから呼び出し可能でした。

View の描画キャッシュを使用する方法

View の描画のためのキャッシュを利用する方法。

1
2
3
4
5
6
View view = findViewById(R.id.view);

view.setDrawingCacheEnabled(true);      // キャッシュを取得する設定にする
view.destroyDrawingCache();             // 既存のキャッシュをクリアする

Bitmap bmp = view.getDrawingCache();    // キャッシュを作成して取得する

描画キャッシュが有効になっているかどうかや、最新のキャッシュが存在するかどうかは状況によるので、自分で設定してやることで View のキャプチャを取得することができます。

Canvas に View を描画する方法

自分で用意した Canvas に View を描画する方法。

1
2
3
4
5
6
7
View view = findViewById(R.id.view);

Bitmap bmp = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Config.ARGB_8888);
                                        // view のサイズで Bitmap を作成
Canvas canvas = new Canvas(bmp);        // bmp をターゲットにした Canvas を作成

view.draw(canvas);                      // canvas に view を描画する

View のサイズで作った Bitmap を Canvas の書き込み先にし、draw() メソッドで書き込んでやることで、 View が描画された Bitmap を取得することができます。

[おまけ] getDrawingCache() まわりの豆知識

今までなんとなく使っていたけれど、View のキャプチャに buildDrawingCache() を必ず呼ぶようにしているスニペットがあったり、 getDrawingCache() で null が返ってくることがあったりと挙動を不審に思っていたので軽く調査(Android 4.2.2)。

  • 直前に setDrawingCacheEnabled(true) さえすれば、 getDrawingCache() の前に自分で buildDrawingCache() を呼ぶ必要はない
    • View のフラグに PFLAG_DRAWING_CACHE_ENABLED が立っていれば getDrawingCache() で buildDrawingCache() を呼んでいる
  • キャッシュを確実に更新したい場合は View を invalidate() するか destroyDrawingCache() でキャッシュをクリアする
    • フラグに PFLAG_DRAWING_CACHE_VALID が立っている、かつ DrawingCache が確保されていると buildDrawingCache() が呼ばれてもキャッシュが更新されない
    • invalidate() するだけだと Bitmap が recycle() されなそうなので、連打するとメモリ不足に陥るかも
  • setDrawingCacheEnabled() するだけでは destroyDrawingCache() が呼ばれないことがある
    • フラグが “変わった” ときだけ destroyDrawingCache() が呼ばれるようなので、オーバーヘッドも少ないし自分で destroyDrawingCache() を呼んだ方が無難そう
  • そもそも何に使われている “キャッシュ” なのか?
    • draw() 時に caching の条件を満たしていれば、 canvas に直接 drawBitmap() するために使われているので、まさに描画キャッシュのように見える
    • 条件については今回は追いきれず(LayerType や HardwareAccelerated など Android の描画周りの知識が足りなすぎた..)
    • ご存知の方がいたら教えて下さい!

Android Library Project と JAR の違いを理解する

Android でライブラリを使用する際、プロジェクトを作成する必要のあるライブラリプロジェクトと、クラスライブラリの JAR と2種類存在するけれど、それぞれどう違うのか軽く調べました。

ライブラリプロジェクトと JAR の違い

最も根本的な違いは Resources を内包しているかどうかのようです。
ライブラリプロジェクトは Resources を保持できるので、 JAR のように静的に APK に組み込むことができません。 そのためプロジェクトとして作成した上で、組み込みたいプロジェクトに依存させて、ビルド時に R.java を一緒に作成する必要があります。

Android APK のビルドの流れを軽く理解する

完全に上記参考サイトの受け売りですが、Android APK のビルドの流れも要点だけ追っておきます。 (これを以前追っておいたおかげで、 Gradle のビルドで詰まったときなどにとても役立ちました)

もっと詳しく理解する場合は、リンク先をご確認下さい。

Android APK のビルド

  • リソースと ID の対応づけを持った R.java を作る
  • .java を .class にコンパイル
  • .class(bin/classes) をまとめて Dalvik ByteCode である .dex にコンパイル
  • .dex を APK に追加する

JAR を含んだ Android APK のビルド

  • Android APK のビルドにおいて、 .dex を作るときに JAR Library(実体は.class) を .dex に一緒にまとめてコンパイルするだけ

Android Library を含めた Android APK のビルド

  • 依存関係を計算
  • R.java を作る
    • Library Project の AndroidManifest.xml は無視される
1
2
3
4
5
6
aapt package –f –m –auto-add-overlay
    –M /absolute/path/to/app/AndroidManifest.xml
    –S /absolute/path/to/app/res
    -S /absolute/path/to/library-project/res
    -I /absolute/path/to/android.jar
    -J /absolute/path/to/app/gen
  • Android Library を含めた .java を .class にコンパイル
1
2
javac <other options> –sourcepath
    /absolute/path/to/app/src;/absolute/path/to/app/gen/;absolute/path/to/library-project/src
  • .class を .dex にコンパイル
  • resources を Library Project の分もまとめてマージしてパッケージ
  • マージされた resources と、マージされた .dex を .apk に追加する