Android: ImageSwicherを使ってみる

画像ビューワを作るために、ImageSwitcherを使ってみようと思って実験してみたら、思いのほか良い結果となりました。今までで知っているやり方は、ViewFlipperにImageViewを数個入れてそれでスライドを検知っぽいやり方していたのだけど、画像と画像の間の微妙な隙間が生まれるので、Desireに付いているギャラリーみたいな見せ方ってどうやるねん!?って思っていたのですが、ImageSwitcherだったんですね、多分。

ApiDemosのImageSwitcherを参考にやっています。
最初は何も考えずにImageSwitcherを取得してすぐにsetImageResourceしていたのですが、例外で死んでしまって原因がわからなかったんですが、ApiDemosをみたらうまくいきました。やっぱりサンプルは偉大。この辺りの情報は本にも載ってないし。

package jp.cyclemate.android.phototest;

import jp.cyclemate.android.androidlib.util.AnimationHelper;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.Gallery.LayoutParams;
import android.widget.ViewSwitcher.ViewFactory;

public class PhotoView extends Activity implements ViewFactory {
	private final static String TAG = "PhotoView";

	private ImageSwitcher mSwitcher;
	// 画像を20枚準備
	private int[] images = { R.drawable.photo_01, R.drawable.photo_02,
			R.drawable.photo_03, R.drawable.photo_04, R.drawable.photo_05,
			R.drawable.photo_06, R.drawable.photo_07, R.drawable.photo_08,
			R.drawable.photo_09, R.drawable.photo_10, R.drawable.photo_11,
			R.drawable.photo_12, R.drawable.photo_13, R.drawable.photo_14,
			R.drawable.photo_15, R.drawable.photo_16, R.drawable.photo_17,
			R.drawable.photo_18, R.drawable.photo_19, R.drawable.photo_20 };
	// 現在表示中の画像の位置
	private int position = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.photo_view);

		mSwitcher = (ImageSwitcher) findViewById(R.id.imageswitcher);
		mSwitcher.setFactory(this); // この処理を行わないと例外で落ちた
		position = getIntent().getIntExtra("position", 0);
		mSwitcher.setImageResource(images[position]);

		// 今日はフリック実装する時間なかったので、
		// クリックされたら次の画像へスライドという形
		mSwitcher.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				showNext();
				// showPrevious();
			}
		});
	}

	private void showNext() {
		position += 1;
		if (position >= images.length) {
			position = 0;
		}
		// AnimationHelperは個人のライブラリです。あしからず。
		mSwitcher.setInAnimation(AnimationHelper.inFromRightAnimation());
		mSwitcher.setOutAnimation(AnimationHelper.outToLeftAnimation());
		mSwitcher.setImageResource(images[position]);
	}

	private void showPrevious() {
		position -= 1;
		if (position < 0) {
			position = images.length - 1;
		}
		// AnimationHelperは個人のライブラリです。あしからず。
		mSwitcher.setInAnimation(AnimationHelper.inFromLeftAnimation());
		mSwitcher.setOutAnimation(AnimationHelper.outToRightAnimation());
		mSwitcher.setImageResource(images[position]);
	}

	public View makeView() {
		// ApiDemos->Views->ImageSwitcherのソースからメソッドを丸々コピー
		ImageView i = new ImageView(this);
		i.setBackgroundColor(0xFF000000);
		i.setScaleType(ImageView.ScaleType.FIT_CENTER);
		i.setLayoutParams(new ImageSwitcher.LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
		return i;
	}
}

フリックを実装してないので、しょぼいサンプルになってしまいましたが、showNextメソッドを定義してその中でアニメーションを毎回切り替えることで、showNextが呼ばれたら進む方向にスライド、showPreviousが呼ばれたら戻る方向にスライドという形で動作することが確認できました。あとはフリックを実装するだけ(のはず)!


日記:コーディング進まず

今日はコーディング自体はほとんど進まなかった(ーー;)
現在、企画・開発などをグループで行う団体に参加しているのですが、そこでAndroidアプリ作成を担当することになったのだけれど、色々と慣れないことが多くて大変です。プロジェクト管理ツールにTracを使っているのですが、これもまだ使った事ないし…。個人的にはRedMineのほうが何倍も使いやすいような気がするのですが…。慣れの問題なんでしょうかね〜。

一旦、SVNの環境を整備して、それを周知し、自分のまだ全然できてないソースを一旦コミット。
家では自宅サーバとの間でgitで運用しているので、subversionは久しぶり。まぁ会社では使ってるんですけどね。もろもろの準備をしていただけでコーディングは全然進まず。まぁ、そういう準備があってこそコーディングに入れるわけだから、焦ってはいかんです。

でも今日は収穫がありました。
twitter上でレシーバの使い方についてのやり取りを行うことができて、正しい使い方がわかってきたからです。@yokmamaさん、@akai_tさん、ありがとうございました。

  • レシーバは10秒以上かかる処理ができないので、レシーバでサービスを起動して処理はサービスに任せる
  • レシーバを定義してintent-filterを正しく設定すれば、サービス側でBroadcastReceiverを定義しなくてもよい
  • サービスは処理が終わった後に何もやることがないと、GCで消滅するので生存に信頼性がない

というあたりでしょうか。
レシーバやサービスに対する認識不足を痛感しました。
BatteryCrystalを直したくなってきたけど、あれはあれで安定稼働しているので、今は放置します。まぁバグがあるわけではないですからね…。メモリやバッテリー消費が多少多くなっているかもしれないというだけで。それも問題だけど…。
時間ができたら、対応してみたいと思います。


日記:デュアルディスプレイ化

連休でしたが、これといって何事もなく過ごしました。
プログラムやってたり、掃除したり、DVD見たり、至って普通の休み。
うーん、寂しいような…、寂しいような…(>_<;)

あぁ、そうそう。Mac用に21.5インチのFullHDのディスプレイ買いました。
現在のディスプレイは25.5インチのやつを使っているのですが、これはWindowsとPS3用です。MacもDVIで繋ごうと思えば繋げるのですが(要ディスプレイポート)、いかんせんでか過ぎて机の上のほとんどを占領するので、諦めてもう一台買いました。小さいほうがいいときもありますね…。

まぁ本当は21.5インチのは小さい上に、ばらすとかなりコンパクトなので、実家に持ち帰って作業するとか、どっか移動して作業(まだ参加したことないけど、ハッカソンやAndroid温泉とか)するのにも便利そうだなと思ったのです。実際、コンパクトだけど、解像度は高いのですごく便利だなと思ってます。しかも安かったんですよね〜。15,000円以下でした。接続コネクタがDVIとD-Subしかないので、完全にサブディスプレイ用に用途を絞ってあるのだと思いますが、そういう用途だったので別にこれで構いません。

今週末は、うちでAndroid勉強会@愛媛 2回目を行います。
また新しい参加者が現われてくださったので、ぜひとも愛媛のAndroidを盛り上げていきたいところです!人数が多くなれば、また他のところでとか考えないといかんかなと…。まぁ、無理すれば、8人くらいまでは入るだろうけど…。


日記:アプリアップデート!

昨日、DesireがFroyoにアップデートされました!
Softbankさん、さすが!?日本ではいち早く対応。
他のキャリアでも出てくるけど、まだ新しい端末は販売されてないですからねぇ。

ようやくアプリをSDカードにインストールできるようになったなぁと思って、でかいアプリを移していこうと思ったら、移せるアプリと移せないアプリがあるんですね…。権限はこちらにあるかと思ってたんだけど。移せるものに関してはなるべく移したんですが、それでも新しくSoftbankメールアプリをインストールしたら、元のサイズに戻ってしまいました。あんまり意味がない(><;)

ウィジェットやライブウォールペーパー、IMEなどのアプリは本体にインストールしておくほうが安全なので、それらは本体で、画像をよく使うアプリなどはなるべくSDカードへのインストール対応をしたほうがいいと。そういうことなので、音声エディタをSDカードにインストールできるようにしてみようと決意。

やり方は簡単で、Androidプロジェクトで使うAPI Levelを8に設定後、マニフェストにandroid:installLocation=”preferExternal”を設定するのみ。minSDKVersionは4で、API Level4でしか使わないメソッドに限定することが条件でした。Desireにインストールした後、IS01でインストールして使用してみて問題なかったので、これで大丈夫でしょう!
BatteryCrystalはウィジェットなので、これはできません(^^;)

BatteryCrystalをステータスバーに常駐させるのは先日のブログで書いてたんですが、電源管理機能をつけてリリースしました。バージョン2.0.0でリリース。気に入っていただけるといいなぁ〜。

そういえば、私がリリースしたアプリの総ダウンロード数が5,000を超えました!!マーケットにアプリをリリースしだしてから2ヶ月なので、結構いいペースなんでしょうか?もっと皆さんの生活を役立てられるアプリを作っていきたいです!これからも宜しくお願いします!


日記:とりあえず常駐化はできた

BatteryCrystalのステータスバーの常駐化はとりあえずできた。
昨日作ったPythonで画像に文字を合成するプログラムを使って0から100までの画像を作成してそれを読み込むという形です。さすがに画像数が202枚になったのでアプリのサイズが一気に1MBくらいになってしまいました…。綺麗さを重視して、48×48で作ってるからなぁ…。

最初に電源管理をつけようとしたため、ソースがごっちゃになっているので、このままではリリースできないと判断してまだリリースはしていません。あと、NotificationのcontextViewを設定するなどして、現在の状態などを表示するというのもまだできていないのでそちらもやる必要あり。BatteryCrystalは多言語化しているので、その調整も。まぁ単語で済むので難しくはないんだけれども。

Bluetoothに関しては、backport-android-bluetoothを使うと、IS01でBluetoothを管理画面に移動せずにON/OFFすることができたのでいけるかなと思ったのですが、Desireでは強制終了するようになってしまいましたorz
あと、backport-android-bluetoothでBluetoothAdapter.enable()とBluetoothAdapter.disable()を使ってもintent-filterのBluetoothAdapter.ACTION_STATE_CHANGEDに引っ掛からなかったんですよねぇ…。てっきりこれで取れるのだろうと思っていたのですが、これだとBluetoothがONになりきる前の中間画像とかが出せないので(出す必要はあるのか?と言われれば、Desireではそうだから出したいという話)、本当に頓挫中です。

やっぱり設定画面を表示するという程度でリリースするのが現実的かと思ってきてます。そうしようかな。そうなるとあとはレイアウトなんですが、レイアウトでも苦戦中なんですよね…。凝ったレイアウトにしようと思うとうまくいかず。早くリリースしたいなぁ〜。