無理やりキャラセットを変更する場合
DefaultHttpClient httpClient = new DefaultHttpClient(); HttpParams params = httpClient.getParams(); params.setParameter("http.protocol.content-charset", CHARSET);
注意しながら行なうこと!
できる限り、他の方法で文字化けを解消するようにする。
無理やりキャラセットを変更する場合
DefaultHttpClient httpClient = new DefaultHttpClient(); HttpParams params = httpClient.getParams(); params.setParameter("http.protocol.content-charset", CHARSET);
注意しながら行なうこと!
できる限り、他の方法で文字化けを解消するようにする。
環境
OS:4.4.4
device: Nexus 7 (2012)
カメラの使用許可を求めなかったときに発生した。
対処
AndroidManifest.xml へ
<uses-permission android:name="android.permission.CAMERA"/>
を追加する。
7、画像のサイズを設定
ユーザが指定する画像サイズを、設定する。
これも、機種に依存する。サポートしない画像サイズをぶっこむとどうなるかわからない。また、縦にプレビューしていても、横にしか保存されない。
保存するクラス、もしくはメソッドで縦のプレビューを横にするとか実装しなければならない。
まぁ、このメソッドで考えてもしょうがないことだから、ここまでにしておく。
8、カメラへ設定値を適用
プレビューに必要な設定値を使用するカメラに適用する。
9、プレビューの開始
設定変更が終了したので、カメラのプレビューを再開する。
例えれば、「工場長、機械を止めて設定終了しましたから、機械を動かします。ちなみに、停止していた間の映像は、消えてどこにもありませんからご了承ください」
10、例外処理
エラーが発生したら、とりあえずログに出力しておく。
機種に依存するため、例外の種類や復帰するための情報が乏しいので、今のところは何もしない。
camera = null として例外発生後は処理しないようにと思ったけど、スレッドでどんなことが起こっているかわからないから、暴挙は止めておこう。
補足として、エラーログについて書いておく。
/** * ログのタグをクラス名とする */ private final String LOG_TAG = this.getClass().getSimpleName();
Log.e(LOG_TAG, “Can not preview. ” + e.getMessage()) の LOG_TAG は、クラスの名前としている。
3、カメラの設定値を取得
使用しているカメラの情報を取得しておく。
設定を変更したらそれらを設定しなおさなきゃいけないからね。
4、プレビューサイズを選択
さてと、これがいろいろ問題を引き起こすところです。
CameraInformation に、カメラのオブジェクトとプレビューする幅と高さをわたして、カメラがサポートしているプレビューのサイズを取得する。
実装の説明は、CameraInformation の記述で書きます。
今のところ、要件として、サポートされているビューサイズの中から適切なサイズを取得してほしい。
将来、ズームアップしたプレビューなどの要件が追加されても、このメソッドが使用できるように CameraInformation で工夫しないといけないかも・・・
5、プレビューのサイズを設定
選択したプレビューサイズを、設定する。
6、画像サイズを取得
プレビューデータを保存するとき、そのサイズを設定しておかなければならないらしいが、ひとまず、ここで、サポートされている画像サイズの中から指定する画像サイズを取得する。
画像サイズの指定は、ユーザがどれどれにしたいと要望を唱えるはずだから、外部から設定する仕組みを実装しなければならない。
それが、
/** * 画像サイズの設定値 画像サイズのインデックス 0:最大 値が増えると画像サイズは小さくなる<br> * 画像サイズは機種に依存するので、アプリケーションの設定などから取得、設定される。 */ private int pictureSize = 0; /** * 画像サイズを取得 * * @return 画像サイズのインデックス 0:最大 値が増えると画像サイズは小さくなる */ public int getPictureSize() { return pictureSize; } /** * 画像サイズを設定 * * @param pictureSize * 機種に依存する<br> * サポートされている画像サイズを知っておく必要がある。サポートされていない画像サイズを設定すると、プレビュー開始時に例外が発生する。<br> */ public void setPictureSize(int pictureSize) { this.pictureSize = pictureSize; }
メンバー変数の pictureSize である。
メンバー変数の命名について、少し触れておく。サンプルソースに private int mSize などと、mのSizeですと宣言されているのが良く見かける。
C++で m_ をメンバー変数につけてメンバー変数であることがわかりやすいと、規約されていたことがあったが、もしかして、その m ?
もしそうであれば、私はあえてそうはしない!
だって、メンバー変数かどうか、ソースの可読性を良くする手段とはおもえないから!
メソッド内で宣言している以外の変数であればメンバー変数でしょう。C++ではグローバル変数やマクロ変数などクラスやメソッド内で使用される変数のスコープが曖昧である。そのために、g_ だとか m_ だとか区別していた。
Javaでは必要ないでしょう。もし、必要であるなら、クラス設計そのものがおかしい!
それと、やたら public 変数が多い。
get 、setをコーディングするのが面倒? いやいやツールが自動生成してくれる。
参考書として説明を書いていくとページ内で収まらない? いやいやそんなことするから、使えないSE、プログラマを量産する、仕組みを構築していくのだ!
あくまでも参考書というのは、初心者やそれを学ぼうとしている者が読むものである。
報告書類、論文や研究成果の発表ならばいいけど・・・
なので、私は m には なりたくないから m を つけない。
わき道がたくさんあるので、次へとつづく・・・ To Be Continued…
サーフェイス変更イベントについて
/* * (non-Javadoc) サーフェイス変更イベントの処理 * * @see android.view.SurfaceHolder.Callback#surfaceChanged(android.view.SurfaceHolder, int, int, int) */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { CameraInformation info = new CameraInformation(); Size previewSize = null; Size pictureSize = null; Log.d(LOG_TAG, "DefaultDisplay Rotation=" + getDefaultDisplayRotation()); Log.d(LOG_TAG, "format=" + format + " width=" + width + " height=" + height); try { if (null != camera) { // プレビューの停止 camera.stopPreview(); // ローテーションを設定 info.setRotation(getDefaultDisplayRotation()); // カメラの設定値を取得 Camera.Parameters params = camera.getParameters(); // プレビューサイズを選択 previewSize = info.getPreviewSize(camera, width, height); // プレビューのサイズを設定 params.setPreviewSize(previewSize.width, previewSize.height); // 画像サイズを取得 pictureSize = info.getPictureSize(camera, this.pictureSize); // 画像のサイズを設定 params.setPictureSize(pictureSize.width, pictureSize.height); // カメラへ設定値を適用 camera.setParameters(params); // プレビューの開始 camera.startPreview(); Log.d(LOG_TAG, "preview width=" + previewSize.width + " height=" + previewSize.height); Log.d(LOG_TAG, "picture width=" + pictureSize.width + " height=" + pictureSize.height); } } catch (Exception e) { Log.e(LOG_TAG, "Can not preview. " + e.getMessage()); } }
カメラのプレビュー表示につて、説明をします。
1、プレビューの停止
使用しているカメラを camera.stopPreview() で停止する。
プレビューの停止しなくてもいいけど、したほうが安全である。
なぜか?
例えば、工場の機械を設定してくれよとお客様から要望があったとする。
「機械は止めちゃだめだよ。止めたら損害賠償を請求するからね!」なんて無茶な要件と同じである。
コンベアに巻き込まれて、手や足が挟まれてぐちゃぐちゃになってもいい、動いている機械の内部に入って設定するぞ!
なんて、猛者ならいいですよ。
自分にはできない・・・けど、金銭保障するからお願いと、懇願されたらするかも・・・
2、 ローテーションを設定
スマホやタブレットを立てて使用しているか横向きで使用しているかを設定します。
ほとんどのサイトで、横で使用するのが標準だけど立てて使用する。(呪縛)
標準が横なら横でいいじゃん!
使う人が、横で使おうがねっころがって逆さまで使おうがいいのでは?
回転させたくなかったら、使う人が回転をロック(固定)すればいいだけでしょう?
なので、「横でも縦でも好きにしてくれ!」要件を実装します。
さてと、回転の情報をどこから取得するかというと、ウインドウマネージャに聞くといいらしいので、getDefaultDisplayRotation() を実装します。
/** * 回転の状態を取得 * * @return Surface.ROTATION_0 or Surface.ROTATION_90 , or Surface.ROTATION_180, or Surface.ROTATION_270. */ public int getDefaultDisplayRotation() { return getWindowManager().getDefaultDisplay().getRotation(); } /** * ウインドウマネージャーを取得 * * @return */ protected WindowManager getWindowManager() { return (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE); }
ウインドウマネージャーを呼び出して、デフォルトのディスプレイ情報から回転情報を取得します。
そんじゃ、ウインドウマネジャーはどこから呼び出せばいいかというと、コンテキストからサービスを指定して取得します。
ウインドウマネジャーは、よそものから使われたくないので protected にしておきます。
参考書やよくあるサイトだと
public static void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); android.hardware.Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay() .getRotation(); int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } camera.setDisplayOrientation(result); }
「このソースを参考にして回転情報の取得メソッドを実装します。」とか書かれてるけど、なんでやねん!
難しいこと書かんで、いいでしょう!
「コンテキストからサービスを指定して、ウインドウマネージャーを呼び出し、デフォルトのディスプレイ情報から回転情報を取得します。」
これでいいのだ!
長くなりそうなので、次へと続く・・・ To Be Continued…
クラスの記述
/* * (non-Javadoc) サーフェイス生成イベントの処理 * * @see android.view.SurfaceHolder.Callback#surfaceCreated(android.view.SurfaceHolder) */ @Override public void surfaceCreated(SurfaceHolder holder) { CameraInformation info = new CameraInformation(); try { // カメラの初期化 if (null == camera) camera = info.openCamera(cameraFacing); // カメラにサーフェイスホルダーを設定 if (null != camera) camera.setPreviewDisplay(holder); } catch (Exception e) { Log.e(LOG_TAG, "Camera not open. " + e.getMessage()); } } /* * (non-Javadoc) サーフェイス変更イベントの処理 * * @see android.view.SurfaceHolder.Callback#surfaceChanged(android.view.SurfaceHolder, int, int, int) */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { CameraInformation info = new CameraInformation(); Size previewSize = null; Size pictureSize = null; Log.d(LOG_TAG, "DefaultDisplay Rotation=" + getDefaultDisplayRotation()); Log.d(LOG_TAG, "format=" + format + " width=" + width + " height=" + height); try { if (null != camera) { // プレビューの停止 camera.stopPreview(); // ローテーションを設定 info.setRotation(getDefaultDisplayRotation()); // カメラの設定値を取得 Camera.Parameters params = camera.getParameters(); // プレビューサイズを選択 previewSize = info.getPreviewSize(camera, width, height); // プレビューのサイズを設定 params.setPreviewSize(previewSize.width, previewSize.height); // 画像サイズを取得 pictureSize = info.getPictureSize(camera, this.pictureSize); // 画像のサイズを設定 params.setPictureSize(pictureSize.width, pictureSize.height); // カメラへ設定値を適用 camera.setParameters(params); // プレビューの開始 camera.startPreview(); Log.d(LOG_TAG, "preview width=" + previewSize.width + " height=" + previewSize.height); Log.d(LOG_TAG, "picture width=" + pictureSize.width + " height=" + pictureSize.height); } } catch (Exception e) { Log.e(LOG_TAG, "Can not preview. " + e.getMessage()); } } /* * (non-Javadoc) サーフェイス解放イベントの処理 * * @see android.view.SurfaceHolder.Callback#surfaceDestroyed(android.view.SurfaceHolder) */ @Override public void surfaceDestroyed(SurfaceHolder holder) { if (null != camera) { // プレビュー終了 camera.setPreviewCallback(null); camera.stopPreview(); camera.release(); } camera = null; }
今回は、ビューを生成、表示、終了までです。
1、surfaceCreated(SurfaceHolder holder)
カメラを初期化して、カメラにサーフェイスホルダーをぶっこみます。
2、surfaceChanged(SurfaceHolder holder, int format, int width, int height)
ビューが生成されたもしくは変更されたときに、カメラのプレビュー機能を使用してビューに表示します。
3、surfaceDestroyed(SurfaceHolder holder)
ビューが削除されるときに呼び出されるので、カメラを使用していたら解放してあげます。
camera.release() これを忘れるとメモリリークするらしい、検証していないので定かではないが、まぁ画像処理ではメモリを大量に使って高速に処理しなければならないからな。
さて、機種に依存するため、詳細な機能を実装するためにはそれらをラップしていきます。
手始めに、CameraInformation こいつでカメラの設定情報を取得したり変更したりします。
それと外部からの要件を満たすために、cameraFacing と pictureSize を実装します。
プレビューサイズは? と疑問をもつ?
surfaceChanged() で表示するサーフェイスホルダーとフォーマット、幅と高さをわたされるでしょ!
そこで処理するので外部からの要件は満たしています。
前回に、「おちているソースや参考書などが読みにくいと感じた」と書いたのことがなんとなくわかった気がする。
要件と機能が明確に記述されていないからだ!
また、おいおいそれはそのクラスに必要ない機能だろうと思われるのに、そのクラスで実装している。
オブジェクト指向の前に、要件と機能を明確にしろよと、遠いむかしに部下へよく言ったもんだ・・・
クラスの記述
/** * CameraView カメラとサーフェイスホルダーをラップしたビュークラス * * @author Ryan's Factory * @since 2014/09/10 */ public class CameraView extends SurfaceView implements SurfaceHolder.Callback, Camera.PictureCallback { /** * サーフェイスホルダー */ private SurfaceHolder holder = null; /** * カメラ */ private Camera camera = null; /** * コンストラクタ * * @param context */ public CameraView(Context context) { super(context); // サーフェイスホルダーの生成 holder = getHolder(); // 実装したコールバックを設定 holder.addCallback(this); // プッシュバッファの設定 setSurfaceHolderPushBuffers(); } /** * プッシュバッファの設定 * This constant was deprecated in API level 11. * this is ignored, this value is set automatically when needed. */ @SuppressWarnings("deprecation") private void setSurfaceHolderPushBuffers() { // OSバージョン判定 if (Build.VERSION.SDK_INT < 11) { holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } }
コンストラクタを実装するだけで、いくつか問題が出てきた。
ひとつめの問題がこれ holder.setType (SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
OSのバージョンによっては例外が発生するらしい。
どこかのブログに「ワーニングが出るけどどうしたらいいだろう」なんて記述があったけど、@SuppressWarnings(“deprecation”) とすればよいだけだ。
急速にOSバージョンがあがり、しかもいろんな機種が存在しそれらに対応しなければいけないとは、厄介なものに手を出した気がする。
ふたつめは、カメラのオブジェクトをメンバーとして含んでいるが、自分としては気にくわない。
カメラはフロントやリアもしかしたらUSBで外付けのカメラなどが出てくるかもしれない。素のクラスをそのまま使うと、今までの経験上苦労するのはまちがいない。
早い時期に本格的なカメラのクラスを実装したほうが良いだろう。
みっつめがサーフェイスホルダー。
なにこれ?
よくわからん
スマホやタブレットで写した写真をツイッターを介在してサイトに投稿するアプリケーションを開発することにしたが、わからないことだらけである。
ただ、落ちてるソースや参考書を見ていると25年ほど前にXWindowのXlibだけで開発していたころを思い出してしまった。
CPUパワーも開発環境もだいぶ進化したのだが、やっていることは同じだ。
参考書やおちているサンプルソースは、なぜ読みにくいのだろう・・・
AndroidManifest.xml に <uses-permission android:name=”android.permission.CAMERA”/> を 追加する