Androidのレイアウトで苦労しない為に – 重要なのはdpiとdp

レイアウトについて知識不足だった為、困ったことがありました。
Android2.3でのお話です。

最近、解像度が960*540のいわゆるHD端末がありますよね。
アスペクト比は現在主流の854*480とほぼ同じです。
どちらもhdpiです。

この2種類の端末で同じように表示されるレイアウトを作る必要がありました。
アスペクト比が同じですので一つのレイアウトでいけるじゃん!
と思いながらレイアウトエディタでAndroidの推奨する単位であるdpでレイアウトしました。
レイアウトした時のプレビュー画面は854*480の設定です。

完成したので表示すると…960*540でちょっと小さい!

dpを理解していれば当たり前でしょ、と言われるところです。

dpiが同じ場合、実際に表示されるピクセル数は同じです。
例えば10dpをhdpiで表示する時は10 * 1.5 = 15pxとなります。
なので、960*540・854*480どちらの端末でも15pxで表示されちゃうんです。

dpで吸収できないこの問題。
どうやって対応します?
レイアウトを2つ作る・dpを指定しているもの全てを表示前に拡大縮小処理(値の調整)する、のどちらかだと思います。

前者は対象が限定されていれば問題ないですが、どんな端末でも対応とはいきません。
また、レイアウトを出し分ける仕組みもうまく働かない為、自前でsetContentViewへ与えるレイアウトIDを出し分ける処理を書く必要があります。

後者はどんな端末でも対応できるとは思いますが、専用の処理を書く必要があります。
(専用の処理は結構めんどくさそう&重そう…)

前者の方法で今回は対応したのですが、Android環境はカオスすぎますね…
dpとはなんだったのでしょう。
どんな端末でも同じ大きさで表示してくれる単位だと思ってましたよ、googleさん…

AndroidでTitanium Mobileのアニメーション機能を使うのはまだ早い?

Titanium Mobile。
開発元がMacでiPhoneメインに作っているのか、Androidアプリを作ろうとすると色々問題があります…

今までに遭遇してしまった問題たち。

・WindowsでAndroidアプリ開発 > 画像出ない!
Titanium MobileでAndroidアプリ = 茨の道で書きましたが、fastDev機能使うと画像が出ません…(出す方法知ってる方がいたら是非教えてください!)

・WindowsでAndroid SDKが認識されない!
Android SDKをインストールしたパスに半角スペースが入っているとパラメータが勝手に区切られビルドできません。
Titaniumが悪い! とは言い切れないものの、内部処理でダブルクオーテーションで囲んでくれれば動くので直してもらえるとありがたいところですね。
諸悪の根源はProgram Filesフォルダェ…

・AndroidでImageViewのImagesを使ったアニメーションが重い!
アニメーションするView10個くらいまでは何事も無く動くのですが、それを超えた途端、一気にパフォーマンスが落ちます。(作りが悪かったらごめんなさい)
iPhoneでは標準のアニメーションでスムーズに動いていました。

色々試したりコードを眺めたりして、画像の切り替えを行う際の読み込みが重いのではと思い、独自にアニメーションさせる機能を作りました。(両対応させたかったのでJSのみの形で)
独自アニメーションの形であれば30体くらい出してアニメーションさせても問題無さそうでした。
アクションやシューティングはキツイものの、RPGくらいならいけそうですね。

・Androidの2DMatrixのアニメーションを行う際、anchorPointは無視される
常に左上を中心として動きやがります…
iPhoneで動かすとanchorPointの値は認識されています(指定しなくてもデフォルトの中心点が真ん中という優しさ)

・Androidのアニメーションでtop,leftとwidth, heightを設定してanchorPointの代用をしようとすると謎の動きをする
Viewの拡大・縮小をViewの真ん中を中心として行いたくて、top,leftとwidth,heightを計算して設定する形で作ってみました。
すると何ということでしょう、top,leftの移動とwidth,heightの拡大・縮小の速度が違うではありませんか。

内部の処理を見ると、top,leftとwidth,heightの値を設定するコードが違ったので、そのせいだと思われます。
iPhoneで動かすとキレイに動きます、不思議!

ダメ元でwidth,heightではなく、right,bottomを指定する形で作ってみました。
今度は、top,leftの値が変化していく>変化完了した直後にright,bottomの値が設定した値に!
指定するなということですね…(正しく動く方法、ほんと教えてください…)

Androidを無視すれば凄くいいアプリが手軽に作れますね。
私は全くObjective-Cを知らないので、TitanimuでiOSアプリを簡単に作れることに感動しています。
Androidの方はJavaで普通に書けるので、Titaniumありえない…Javaで書かせてくれ…というツライ状態です。

Androidの対応が進み、同じコードでiOS, Android両方ほぼ同じ動きをするようになれば、今後の開発はTitanium Mobileを利用することでハッピーになれることでしょう。

そんなことを夢見つつ、Androidで起こる問題の解決法をチマチマと探っていっています。

Titanium MobileでAndroidアプリ = 茨の道

Titanium Mobile。
JavaScriptでiOS・Androidのアプリが作れちゃう凄いヤツ。

JavaScript + xml(設定ファイル)で実行可能なアプリを作れます。
JavaScriptからネイティブコードを呼び出す形になっています。

iOSの方は詳しく見ていないのですが、Androidの方は設定から動的にActivityやらManifestやらを生成していました。
JavaScriptインタプリタで動作させるとのことで、ネイティブに比べると少し重いみたいです。

WindowsXPでTitanium Studio + Titanium Mobile SDK 1.7.2を導入しました。
導入から新規プロジェクト作成→ビルドまではうまくいったのですが…

画像が出ない!
Windowsを再起動して実行し直すと画像が初回のみ出る…

下記ページで応急処置的なものが書かれていました。
Titanium 1.7.0 ImageView not displaying remote images » Community Questions & Answers » Appcelerator Developer Center

SDカードを外した状態で動かすと画像が出るとのこと。
私の環境でも試したところ、無事画像が表示されました。
しかしSDカードを使うアプリを使う場合は、使えそうにないですね…

この画像が出ない症状を調査するだけで1日かかってしまいました…orz

原因と対処方法

・原因
fastDev機能(Androidエミュへのインストールが高速化される)をOnにするとWindows版の開発環境で画像が出なくなる。
(何故でなくなるかまでは調べきれていません…)

・対処方法
下記コードをtiapp.xmlに追加することでfastDev機能がOFFになり画像が出るようになります。
しかし、fastDevを使わない場合のエミュへのインストールはかなり遅いです…

<property name="ti.android.fastdev" type="bool">false</property>

Macでの開発だとこの画像が出ない症状は起こらないらしいです。
Windowsでの開発は難しいかもしれません^^;

以上です。

最近のAndroid巡回まとめ – 開発環境構築からゲームエンジンまで色々

Androidについてgoogleさんで検索してオッ、と思ったものをまとめました。

マンガで分かるAndroid開発環境構築方法 – Android Dev – CroCro
胡散くさい教師(ナルシストだろこれ)が、かわいい女の子にAndroidの開発環境構築方法を教えるマンガがあります。
マンガの出来にはノーコメントとして、環境構築についての解説は親切でわかりやすいのでオススメです。

Android開発環境の構築 Eclipse編
このサイト見ながら、会社のPCにAndroid開発環境導入してみました。
開発環境は、いれたときにちゃんと入れ方をメモっとかないとダメですねw(一回キリのことなので大体忘れてしまう)

ひとつ、AndroidSDK導入時にJDKが認識されない不具合があって手間取ったのでそこの解決法が載っているページも貼っておきます。
Android SDKのインストールでJDK not found. – Smart&Social会議室
戻って進むでうまくいくなんてどうかしてるよ…w

AndroidでJNIを使う方法 – Android(アンドロイド)情報-ブリリアントサービス
Android JNI: NativeからのJavaメソッドの呼び出し–日本語のドキュメント–Google Android 論壇
Android C言語を触る(JNI, NDK など基礎知識)|世界的日曜WEBプログラマー日記
JNI関連、詳しくは見てなかったり。
Androidのプログラミングでは高速化が重要なので、今後はJNI使う方法を覚えていきたいですねー。

e3roid – 2D OpenGL game engine for Android – Google Project Hosting
日本の方が作られている2Dゲームエンジンです。
動画を見ると何が出来るかわかりますね。
ゲーム作りたい! と思っている方は利用してみてはいかがでしょう。

AIRNovel
Androidでノベルゲームのゲームエンジン作れば儲かるんじゃないか? と思って検索したら既にありましたw
でもこれはAdobe AIRで動くとのことなので、形としてはFlashみたいですね。

throw Life – Dalvik VMのGarbage Collection概要
Android内の仮想マシン「Dalvik VM」のガベージコレクションの詳しいお話。
短寿命なオブジェクトを生成しまくってるとGC走りまくるよ、というお話でした。

個人でau one Marketでアプリを販売する方法

au one Market。
このマーケットでは、決済方法で携帯料金引き落としに合わせて支払う形(かんたん決済)を選ぶ事が出来ます。
これによりクレジットカードを持っていないユーザの方々に課金してもらえる可能性があります。

Android Market、au one Market、どちらも登録するっきゃない!
と思ったのですが、auのサイトを見ると…

>なお、au one Marketのお申し込みは法人さまに限定させていただいておりますので、ご了承ください。
引用元:Android™向けアプリケーション開発者さま用技術情報 | au by KDDI

法人限定…

個人で気軽に登録は出来ないということですね。
でも何とか個人でも登録したい…ネットを検索してみるとひとつの方法が見つかりました。

アンドロイダー「au one Market」登録販売代行について | Android(アンドロイド)アプリの人力レビューサイト【アンドロイダー】
アンドロイダーさんが、登録を代行してくれるとのことです。
詳細は上記リンク参照で。

以上です。
個人でau one Marketに登録したい! でもどうすれば…と悩んでいる方のお役に立てたらと思い書きました。

Androidアプリでのデータ保存方法

Androidアプリでデータの読み込み・保存を行う場合、以下の3つの方法があります。

・Preferenceを使う
・ローカルフォルダを使う
・SQLiteを使う

どんなデータを扱うかで使い分ける形になると思います。
読み込み・保存ともに簡単なので、単純な値の保存であればPreferenceがオススメ。

以下詳細。
Read the rest of this entry »

Javaで乱数を使う

現在行っているゲーム開発で必要になったので、乱数の使い方を調べました。

参考にしたサイト
Java Math.randomやRandom による乱数の生成 – Java入門

Math.random()は、0以上1.0未満(1が含まれないのが重要)のdouble値が返ってきます。

配列の要素をランダムに取得したかったので、以下のような形で実装しました。

private int[] array = new int[10];
private int getRandVal() {
    int index = (int) Math.floor(Math.random() * array.length);

    return array[index];
}

ビジネスアプリでは乱数使わないですね。
アプリのボタン押下時、1/65535の確率でフリーズ>アプリ製作時のデスマ回想シーン>BONUS確定!! とかこっそりいれてみたいものです。

ウィジェットで独自フォントを頑張って使う方法

ネットの情報をかき集めてAndroidプログラミングしています、ぐるぽです。

ウィジェットで常駐する時計アプリを制作していました。
そこで、通常のフォントではなく独自のフォントを使おうとしていたのですが、ウィジェットを調べていくと普通の方法では使えない事が判明…
(ウィジェットのアイテムのフォント指定部分には最初から用意されている3つのフォントしか選べない)

どうにかフォント利用できないかと調べていたら、

1.フォントをCanvasに描画
2.画像として保存
3・ウィジェットで画像表示

という方法を見つけました。

私の制作したアプリでは、設定画面(PreferenceActivity)でサイズと色を変更>設定画面終了時に画像生成 としました。

キモである設定画面のコード(必要な部分以外はカットしています)

/**
 * 設定画面
 * @author ぐるぽ
 */
public class SettingMenuActivity extends PreferenceActivity {

	@Override
	protected void onDestroy() {
		super.onDestroy();

		// 指定された設定で画像を生成してローカルフォルダに保存
		for (int i = 0; i < TokikakeClockWidgetProvider.IMG_NAMES.length; i++) {
			Bitmap bmp = createCharacterBitmap(
					TokikakeClockWidgetProvider.IMG_NAMES[i], size);
			saveBitmapToPng(String.valueOf(i), bmp);
		}
	}

	/**
	 * 指定された文字・フォントサイズで画像を生成する
	 * @param chara 文字
	 * @param size フォントサイズ
	 * @return Bitmap形式の画像
	 */
	private Bitmap createCharacterBitmap(char chara, int size) {
		// フォントを指定
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setTypeface(Typeface.createFromAsset(getAssets(), "timeleap100.ttf"));
		paint.setTextSize(size);

		// フォントサイズに合った大きさの画像を生成する
		FontMetrics fm = paint.getFontMetrics();
		int height = (int) (fm.bottom - fm.top);

		float[] widths = new float[1];
		paint.getTextWidths(String.valueOf(chara), widths);
		int width = (int) widths[0];

		Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
		Canvas canvas = new Canvas(bmp);
		canvas.drawColor(Color.argb(0, 0, 0, 0));

		canvas.drawText(String.valueOf(chara), 0.0f, -fm.ascent, paint);

		return bmp;
	}

	/**
	 * BitmapをローカルフォルダにPNG形式で保存する
	 * @param fileName ファイル名
	 * @param bmp Bitmap形式の画像
	 */
	private void saveBitmapToPng(String fileName, Bitmap bmp) {
		try {
			OutputStream out = openFileOutput(fileName + ".png",MODE_PRIVATE);
			bmp.compress(CompressFormat.PNG, 100, out);
			out.flush();
			out.close();
			Log.i("list", fileName + ".png" + " create success!");
		} catch (IOException e) {
			e.printStackTrace();
			Log.i("list", fileName + ".png" + " can't create");
		}
	}
}

・PreferenceActivityのonDestroy()
設定画面を閉じた時に画像を生成したいので、PreferenceActivityのonDestroy()内で画像生成処理を呼び出します。

・createCharacterBitmap()
ここで独自フォントの画像をBitmap形式で生成しています。
(フォントを関数内で指定していますが、引数に与える形の方がいいですね…)
独自フォントはAssetsフォルダに置いています。

・saveBitmapToPng()
生成した画像をpng形式でアプリのローカルフォルダに保存します。
ローカルフォルダの読み込み・保存は簡単なので便利^^

ローカルフォルダ読込:InputStream is = context.openFileInput(“ファイル名”);
ローカルフォルダ保存:OutputStream out = context.openFileOutput(“ファイル名”,モード);

以上です。
Assetsフォルダとローカルフォルダを利用すれば、結構なんでもいけそうですね。

dpiとdip

Android端末の様々な大きさの画面に対応する為に、アプリの設定ではpxではなくdipという単位を使います。

dip。
Density Independent Pixel。
この値を使うと、端末のdpiによってサイズが切り替わります。
dpiによってサイズが変わることで、ボタンやテキストなどの画面上でのサイズを統一することができます。
(pxで直接指定してしまうと、dpiの低い端末では大きく、高い端末では小さくなってしまいます。)

端末のdpiの分類
・xhdpi(320dpi) ※2.3から
・hdpi(240dpi)
・mdpi(160dpi)
・ldpi(120dpi)

mdpi(160dpi)の時、1dip = 1pxとなります。

画像を扱う場合は、drawableフォルダに各dpiに合わせた画像を置いておけば、dpiによって読み込む画像が変わります。
(drawable-mdpiフォルダにいれておけば、mdpiの端末で読み込まれる)

dpiごとに画像を用意しなかった場合は、他dpiに用意した画像を拡大・縮小して表示する形になります。
(drawable-mdpiフォルダに画像を置いた状態で、hdpi端末から動かすと画像が拡大されて表示される)

dpiごとの倍率
・xhdpi:2 ※2.3から
・hdpi:1.5
・mdpi:1
・ldpi:0.75

拡大・縮小で画像が汚くなってしまうのを防ぐ場合には、dpiごとの倍率をかけたサイズの画像を用意しましょう。

以上。

Androidのレイアウトで画面いっぱいに表示したい時の設定

画面いっぱいに表示させる方法。

表示させたいアイテムのプロパティの

・Layout height
・Layout width

を「fill_parent」にすることで画面いっぱいに表示させることが出来ます。

ただ、この方法では複数のアイテムが並んでいる形の場合にうまくいきません。

LinearLayout
-Button
-TextView
-Button

と縦に並んでいる形のときに真ん中のTextViewを画面いっぱい表示させたいと考えます。

・TextViewのLayout heightを「fill_parent」にした場合
下のボタンが表示されていない

画面いっぱいに表示できてはいるのですが、下のButtonの表示スペースが無くなってしまっています。

解決方法。
TextViewのLayout weightプロパティの値を「1」にする事で解決できました。

・TextViewのLayout weightを「1」にした場合
下のボタンも表示される

Layout weightは余白がある場合に、どの程度利用するかの重みを設定します。
(複数のアイテムに同じ値を設定した場合、余白を均等に利用する形になり、差をつけた場合は大きい値のアイテムが余白を多く利用する形になります。)
未設定の状態は値が「0」、余白を利用しない設定です。