カテゴリ:Android( 43 )

カメラのsetColorEffectの不具合の解決策

朗報です。

Androidのカメラアプリで、カラーエフェクトを変更後、元に戻すために、"none"を指定しても無視される、そして、アプリを終了しても保持される、という不具合の解決策を発見しました。

どうやら、カメラのプレビュー映像のフォーマットを合わせて設定すると直るようです。
ざっとこんな感じで、場合分け。

openがブログのセキュリティ上ダメって言われたので、全角にしてます。


public void setColorEffect(String effect) {

if ( effect != null ) {

if ( effect.equals(Camera.Parameters.EFFECT_NONE) ) {// エフェクトなしに戻す

if ( MANUFACT_KEIAN ) {//▼ 恵安タブレット

Camera.Parameters params = mCamera.getParameters();
params.setColorEffect( effect );
mCamera.setParameters(params);
// 追加で映像フォーマットを指定
params.setPreviewFormat( ImageFormat.NV21 );
mCamera.setParameters(params);

} else if ( TerminalState.isManufacturer(ParamCamera.MANUFACT_S5110B) ) {//▼ s5110b

// 解放⇒生成で直る
mCamera.close();
mCamera.OPEN(getHolder());
mCamera.startPreview();

} else {//▼ 一般端末

Camera.Parameters params = mCamera.getParameters();
params.setColorEffect( effect );
mCamera.setParameters(params);

}

} else {// エフェクトあり

Camera.Parameters params = mCamera.getParameters();
params.setColorEffect( effect );
mCamera.setParameters(params);

}

}

}

[PR]
by miries | 2015-01-30 15:19 | Android

カメラのカラーエフェクトのバグ

Androidでカメラアプリを作っているわけだが、カラーエフェクトのバグが回避できなくて困っている。
(もしかしたら、ホワイトバランス等、他でもそうなのかも)

カラーエフェクトの設定は、
CameraParameters.setColorEffect(String);
この関数で行う。

Stringの部分に、"negative"を指定すれば、白黒反転、"sepia"でセピアカラーといった具合だ。
そして、"none" でエフェクト無しに戻せる。

…はずなのだが。

ドコモ端末F-10D:問題なし。

S5110b:エフェクトの切り替えはできるが、noneを指定すると無視される&プレビューが停止。
カメラを一度破棄することで、元に戻せる

KEIANタブレット:同じく、エフェクトの切り替えできるが、noneを無視。二度と復帰不可能

どれくらい不可能かといえば、カラーエフェクトを適用して、アプリ終了。内蔵されているカメラアプリを起動すると、さっき設定したエフェクトが残ったまま、というくらい、深く刻み込まれている。
再起動すれば元に戻るのだが…。


----
追記:

解決策見つけました。
http://miries.exblog.jp/24072856/
[PR]
by miries | 2015-01-29 15:38 | Android

続・Activity切り替え

カメラアプリには必須っぽいActivity切り替え。
そこにも、まだまだ罠が潜んでいた。

今自作しているアプリは、独自フレームワークを使っている。
Activityを継承したフレームワーククラスを作り、なるべく共通な要素をそこで処理し、
ゲーム毎に行うべき処理を、ゲームのメインクラスとして定義し、
フレームワークでインスタンスを生成している。

がActivityを継承したフレームワークは、そもそもActivity切り替えなぞ考えてない設計だった。
Activityを切り替えるたびに、この初期化部分が呼び出される。

レイアウトを作り変えるのが、切り替えの目的だから、毎回初期化部分が呼び出されないと困るわけだが、
その他の初期化部分は、なるべく一度しか処理されないようにしなければならない。

まあ、タッチライブラリの初期化、など、各種ユーティリティの初期化しか行ってないので、基本問題は無い。
と思いきや、ゲームクラスの初期化に問題があった。

シングルトン形式で、インスタンスを1つしか生成しないようにしているため、2回目以降の初期化(生成処理)は通過しない。
この初期化の中で、

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

これで、画面を横方向に固定している。
どちらの画面方向を使うかは、ゲームによって異なるので、フレームワークには入れなかった。

しかし、この処理は、毎回必要らしい。


初回起動・フレームワーク初期化、ゲームクラス初期化
 ↓
別のActivity切り替え
 ↓
元のActivityに戻る
 ↓
フレームワーク初期化・ゲームクラスはインスタンスがあるため、初期化スルー

とすると、画面設定が、任意方向可に戻るのだ。
面倒だのう。

あと、もう1つ。
これは、アプリ制作では、周知の事実だけど、Activity切り替え(View切り替え)を使うと、顕在化する。

AndroidOSにある、1つ戻るボタン(機能)。
これは、ホーム画面に戻る、ではなく、1つ前のVIEWに戻る、という機能なんだな。

ゲームで1つのVIEWしか使って無いと、ゲーム中に1つ戻ることで、ホーム画面に戻る。
が、Activity切り替えを導入し、一度切り替えると…。

1つ戻るボタンでは、永久に、アプリは終わらなくなる…。
[PR]
by miries | 2015-01-25 10:16 | Android

カメラとActivity切り替え

まだまだカメラアプリと格闘中。

中華タブレットから、F-10Dに戻ってアプリを起動すると…。
エラーの嵐!

なん…だと…。

ログ出力を見てみると、アプリ起動直後に、onDestroyが呼ばれ、その後、自動で再起動している。
そんなバカな。

以前は問題なく動いたはずだ。
何もなく Destroyが呼ばれるはずが無い。
改変の途中で、何かしらいけないことをやっているということか?

しかし、差分は大きい。探し出すのは不可能にちかい…。

いやまて。
通常、Destroyが呼ばれる=アプリ終了だ。
大抵、ぬるぽだったりするはずだ。

それが、そのままActivityが再起動するだと?
どこかでその現象みたことがあるぞ!

そう、画面の回転だ。
Androidは、画面が回転するだけで、Activityの破棄・生成が起きる。
しかし、それを回避するマニフェストは書いてある。

android:configChanges="orientation|keyboardHidden"

そう、これだ。
しかし、これと似たようなところに、原因があるはずだ!

…と目星をつけて、ぐぐると…見つけやしたぜ!

android:configChanges="orientation|screenSize|keyboardHidden"

なんと、APIレベル13以上から、orientation以外に、screenSizeもつける必要があるらしい。
そういえば、先日、API8⇒API16に上げたわ。

お 前 が 原 因 か!

つか、似たような原因じゃなくて、もろ回転じゃねーか!

ということで1つ解決したが、まだまだ残ってる。
IntentによるActivity切り替え後のカメラプレビューが表示されなかったり、
プレビューを開始すると落ちたりするのだ。

先は長い。
[PR]
by miries | 2015-01-23 23:33 | Android

続・Androidカメラアプリ

以前作ったカメラアプリを引き継いで、ブラッシュアップしているわけですが。

前回動作していたオートフォーカスが全然動作しないっすよ!
パーミッション許可していないとか、初歩的なミスはしていないっ!

…。
…。

そもそも、この中華タブレット(s5110b)に、オートフォーカス機能が無いだけだったorz

端末ごとに機能が違うから、こういうことが起こるんだわ。

オートフォーカスくらいデフォルト実装…ってのが思い込み。
起動時に、すべての端末機能を検出するように大幅修正。

カメラの個数も、複数の場合があるわけで、切り替えられるようにしなければならない。

そこで、ふと思う。

バックカメラとフロントカメラ、いまどきは、各1つずつくらいは搭載しているはず。
このカメラが、それぞれで対応プレビューサイズが異なった場合(正確には、アスペクト比が異なった場合)、やばいんじゃないの?

1つ前の記事で、画面サイズが微妙に変更になった場合は、プレビュー画像の対応をonMeasure()で計算したわけだが、カメラを切り替えても、Viewに関するイベントは何も発生しない。

今までは、ディスプレイサイズとサポートしているプレビューサイズから、使用するサイズを決定していたが、複数カメラに対応する場合、すべてのカメラでアスペクト比が変わらないように、選択する必要もありそうだ。

ちなみに、s5110bは、カメラを1つしか搭載していないので、もう1つのKEIANタブレットで調べてみると…。

176,144
240,160
320,240
352,288
640,480
640,480
320,240

対応サイズが7つって検出されるんですけど、下2つは、被ってませんかね。
いや、マジで、こういう red herring はやめて欲しいんですけど。
[PR]
by miries | 2015-01-23 14:42 | Android

カメラのプレビューとsurfaceViewのサイズ

カメラのプレビューサイズと、SurfaceViewのサイズについては、カメラアプリを作るときに必須なので、すでに多くのサイトで解説がなされているので、さらっとおさらい。

●カメラのプレビューサイズは、任意に決定できない。
サポートされているサイズセットの中から選ぶ

●プレビューサイズは、端末の画面サイズと同じとは限らない
(というか、一致しない方がデフォ)

●プレビューサイズを設定しても、最終的には、SurfaceViewのサイズになる。
(viewのサイズに、ViewGroup.LayoutParams.WRAP_CONTENTを指定した場合)

なので、16:9のアスペクト比を持つ画面いっぱいのViewに、4:3のプレビューを表示すると、横に伸びた映像になる。
この辺りを、どう解決するかで、みんながググる事になる。

一般的な解決方法は、SurfaceViewを作って、レイアウトにaddView() する際に、
プレビューの比率に合わせてサイズを指定することである。

しかしこのSurfaceViewのサイズ、一旦決めたら、変更するのがなかなか難しい。
もし、画面サイズが変わる端末があったら、プレビューが見切れたりしてしまう。

そんな、恐ろしい端末が、s5110bである!

b0163324_12252714.png
b0163324_12253662.png


画面下のアクションバーを(アプリ起動中でも)いつでも出し入れできるのだ。
アクションバーが消えているときは、画面一番下を横にスワイプすることで出てくる。

ちなみに画面サイズは、[800x480] [800x432]で、アクションバーの高さは48ピクセル、
割合にして、10%も変化してしまう。

アクションバーを出した状態で、カメラアプリを起動し、アクションバーを消すと、変な空白が生まれるわけだ。

この微妙な見た目に対応するためには、どうにかしてSurfaceViewのサイズを変更する必要がある。

画面サイズが変更されるので、onSizeChanged()が呼ばれるが、
名前の通り、サイズが変更"された"ときに呼ばれるメソッドだ。
ここで何をやっても何も起こらない。

そこで、見つけたのが、onMeasure() だ。

これは、画面サイズ変更時(だけではないが)に呼ばれ、
各Viewのサイズを算出して設定するメソッドだ。
ここで取得できるサイズは、変更後の新サイズで、試しに100x100を設定したら、きちんと反映された。

まあ、自力で算出しろってことだ。

そんなこんなで、実際のコード。
カメラプレビューは、アスペクト比4:3から変更するつもりがないので、固定で書き込んでいる。

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthMode= MeasureSpec.getMode(widthMeasureSpec);
int heightMode= MeasureSpec.getMode(heightMeasureSpec);

int w = MeasureSpec.getSize(widthMeasureSpec);
int h = MeasureSpec.getSize(heightMeasureSpec);

int dw = w;
int dh = h;

switch(widthMode) {
case MeasureSpec.EXACTLY:// 確定
break;
case MeasureSpec.UNSPECIFIED:// 自由
dw = h *4 /3;
break;
case MeasureSpec.AT_MOST:// 以下
dw = h *4 /3;
if ( dw > w ) {
dw = w;
}
break;
}
switch(heightMode) {
case MeasureSpec.EXACTLY:// 確定
break;
case MeasureSpec.UNSPECIFIED:// 自由
break;
case MeasureSpec.AT_MOST:// 以下
dh = dw *3 /4;
if ( dh > h )
dh = h;
break;
}

//サイズ反映
setMeasuredDimension(dw, dh);

}


[PR]
by miries | 2015-01-22 13:00 | Android

Intentによる、Activityの切り替え

カメラアプリを作っているのだが、定石として、SurfaceViewを2枚重ねる方法をとる。
1枚目は、カメラのプレビュー映像を表示、
2枚目は、カメラの操作や、画像フレームなどの表示、である。

ここまでは以前作っていたのだが、カメラアプリ単体として作っていた。
今は、ミニゲームの1つに、カメラを使うものがる、という体で作っているので、
ずっとカメラのプレビューsurfaceを出していくわけにもいかない。

が、プログラムの途中で、SurfaceViewを消したり、出したりなどのレイアウトにかかわる処理は、そう簡単にできないようになっている。

というわけで、これまたカメラアプリでは定石っぽい、Intentで、Activityを切り替える方法を使った。
通常アプリ用にレイアウトを組んだActivityと、カメラ用のレイアウトのActivityを切り替えるわけだ。

今回初めて使ったんだが、いや、面倒だわ(苦笑
Intentの切り替え自体は、めっちゃ楽にできるよ。でも、シーンのつなぎのほうがね…。

Activityのライフサイクルとして、onStartやOnPause、onResumeなどがいろいろあるが、
基本、onStopが呼ばれるまで動作している。

Intent切替を実行すると、次のActivityが起動して、onStart、onResumeなどが呼ばれるのだが、その後しばらくしてから、切り替え元の、onStopが呼ばれるのだ。

つまり、ある一定期間は、両方のスレッドが動いていることになる。
そのせいで、表示がぼろぼろに…。

というわけで、Intent切替を行うと同時に、切り替え元・先の両方で、一切何も表示せず、かつ、切り替え元が動作を停止するまで待機、という処理を入れてやったら、うまくいった。

…んだけど、Intentの切り替えに、たまに、5秒以上かかるときがあるんだよね。
速いときは1秒もかからないのに、この差はどこから出てくるのか。

なので、待機中のアイコンを出したいのだが…。
[PR]
by miries | 2015-01-21 14:56 | Android

イージングの実装

近年作ったプログラムパーツの中で、タイマーほど役に立ったものはないだろう。
まあ、大抵の人は、作っているんだけど…(苦笑

簡単にいうと、

Timer mTimer = new Timer(2.0);

と作成しておき、

float r = mTimer.getRate();

とすると、生成から2秒間で、0.0 ~ 1.0 まで自動的に変化する値が取得できるというもの。
(機種・環境によっては、自分で経過時間:elapsedTimeを加算することが必要)

少しつけ加えて、

Point pos = mTimer.getRate(startPos, endPos);

とすると、カーソルが開始座標から、目標座標まで動くようにできる。
この辺り、昔はラインアルゴリズムというのを使っていた。
ラインアルゴリズムの考案者(?)は、著書で、シューティングの敵弾の軌道にも使えると言っていが、実際には使えない。

もともとラインアルゴリズムは、
「開始座標から終了座標まで、一ドット幅のラインを隙間を空けずに描く」
というのが主旨で、その過程は考慮していないのだ。

で、タイマーは、どう考慮するのか、というと、経過時間による戻り値が、0.0~1.0である、というところに、イージングを設定できるのが素晴らしい。

イージングを使うと、移動にかかる時間は同じ2秒だが、最初ゆっくりで、後半速くなる、などの加速が使えるようになる。

b0163324_939992.png


イージングについては、いくつかの解説、および、ソースコードを載せているサイトがあるが、
大抵が、上記のようになっている。

:最初速くて、後半ゆっくり(OUT)
:最初ゆっくりで、後半速い(IN)
:最初と最後がゆっくり、中間が速い(IN-OUT)
()内は名称

ちなみに図にはないが、加速のない一定速度は(LINEAR)。

かける時間を、D
開始から現在の経過時間をTとすると、

LINEARは簡単で、
return T/D;

INは、
T /= D;
return T*T*T;


OUTは、
T /= D;
T = T-1;
return (T*T*T +1);

IN-OUTは、
T /= D/2;
if ( T<1 )
return 1.0f/2.0f *T*T*T;
T = T-2;
reurn 1.0f/2.0f *(T*T*T +2);


と書ける。
これは、解説サイトにあったソースコードを簡略化して使っているものだが、
イージングの強度の好みは、個人それぞれ違うと思う。

ただ、なぜかどの解説サイトも、これだけしか解説していないんだよな。
ゲームには、もう1種類必要なのだ。

b0163324_9512250.png

:最初と最後が速く、中間がゆっくり(OUT-IN)

これのコードがないので、作ってみた。

T /= d/2;
T = T -1;
return 1.0f/2.0f *(T*T*T+1);


ちなみに、実際のタイマーでは、これ以外にも、
三角関数の波形や、1.0~0.0になる逆数、連携用に常時0のまま or 1のままなんてのも実装している。
[PR]
by miries | 2015-01-20 09:56 | Android

SurfaceViewの重ね順

久しぶりにカメラアプリを作ろうと、数年前のプロジェクトを見直す。

写真にフレームイラストを合成しようとすると、どうしても、Viewを重ねる必要が出てくる。
下にカメラのプレビュー映像を流し、その上に(プリクラ的な)フレームを重ねるわけだ。

で、この辺も過去にしっかり作っていたんだけど。
ググっていると、気になる一文を見つけた。

ホームキーを押すなどして、一旦アプリを停止してから、レジュームすると、
SurfaceViewの順番が入れ替わるらしいのだ。

過去には、そこまで細かくテストしてなかった気がするので、早速テストしてみると、
確かに、フレームイラストの上に、プレビューが来てしまうではないか。

これを回避するには、SurfaceViewに対して、onResume時に、bringtoFront() をすればいいらしい。
ということで追加。

…。
直らんやんけ!

カメラの初期化に時間を食ってるからか?
色々実験してみるが、全く直る気配なし。

再び、グーグル先生に詳しく聞いてみる。

なんと!
SurfaceViewのソート順は、内部で勝手に決められていて、任意に設定することは出来ないとな!

任意ではないが、ある程度の設定はできる。

setZOrderOnTop(true);

これで、最前面のプライオリティになるようだ。
が、コレだけではダメ。
onResume時に、bringToFront()がやっぱり必要なのだ。

つまり、この2つは必須セット。
おのれ、bringToFront()だけとか、中途半端な情報に騙されたわ!
[PR]
by miries | 2015-01-20 01:59 | Android

タブレットのエアタップ

タブレット(画面をタッチして操作する機器)で、何もしてないのに、勝手にタッチされる現象をエアタップというらしい。

先日まで正常に動作していた、ウチのVita型Androidタブレット、s5110b にもエアタップ現象が発生。

現象の隙をついて、開発者オプションの「タップ位置を表示」オプションをONにしてみると…。
画面最下部(s5110bでは、ステータスバーが表示されているところ)に、ものすごい勢いで●マークが出現。

今自作しているアプリは、5点同時タップで、ソフトウェアリセットがかかるようにしてあるのだが、
これが発動するくらい、すごい勢いでタップされている。

もうこうなると開発はおろか、普通に使うのも無理。
なんとかならないものか…と、再起動はもちろん、画面を吹いたり、分解して掃除してみたりしたのだが影響なし。
最悪なことに、画面最上部にもいくつか出るようになってきた。

そんななか、ドコモの特定の機種で、この現象が発生した場合、
バッテリーが完全になくなるまで使い切ったあと、再起動すると、キャリブレーションが行われる、という噂を発見。
早速、バッテリーが無くなるまで放置し、さらに丸一日放置。

起動してみると…。
おお!発生しなくなってる!

ごくたまに発生するようだが、元々このくらいの頻度で暴走していたので、よしとしよう。


----
追記

とりあえず、一日開発して、エアタップは発生しなかった。
バッテリー使いきり再起動は、方法の1つとして有効なようだ。
[PR]
by miries | 2015-01-16 11:59 | Android