Help us understand the problem. What is going on with this article?

Androidでタッチフォーカスを実装する

More than 3 years have passed since last update.

!注意!

現在推奨されているCamera2 APIではなく、少し古いCamera APIを使用した方法です

やりたいこと

Androidのカメラアプリで、タッチした箇所にフォーカスを合わせたい
(注:使用するAPIの都合上、以下の方法はAndroid 4.0以降でのみ可能)

やること

  1. タッチした座標を特定する
  2. フォーカスをタッチした箇所に合わせる

タッチした座標の特定

onTouchListenerの利用

カメラプレビューをタッチした時に呼ばれるリスナー内でタッチした座標が分かる
以下のコード参照

// --変数宣言--
SurfaceView mSurfaceView;  // カメラプレビューを表示するビュー
Camera mCamera;            // カメラ

// --onCreate内--
// 初期化
mSurfaceView = (SurfaceView)findViewById(/* 人による */);
// タッチリスナーを登録
mSurfaceView.setOnTouchListener(new OnTouchSurfaceListener());

// onTouchSurfaceListenerの定義
class OnTouchSurfaceListener implements OnTouchListener {
    public boolean onTouch(View v, MotionEvent event) {
        // このメソッドは、ビューを押した時、ドラッグした時、離した時など、色々な時に呼ばれる
        // 今回は押した時にだけ処理するように下のif文を付ける
        if( event.getAction == MotionEvent.ACTION_DOWN ) {
            // このブロックを(A)と命名する。後で使う
            // event.getX() でタッチしたx座標が分かる
            // event.getY() でタッチしたy座標が分かる
        }
        return false;
    }
}

フォーカスをタッチした座標に合わせる

使用するAPI

Android 4.0からフォーカスを合わせるのに使用する領域を決められるようになったらしい
この領域としてタッチした座標付近を指定すればタッチフォーカスが実装できる。やったぜ。

まず、大前提として、端末の方がフォーカス合わせの領域指定に対応している必要がある
対応しているかどうかは、getMaxNumFocusAreas()戻り値が0より大きいかどうかで判断できる
0以下だったら諦める

対応しているなら、setFocusAreas( List<Camera.Area> )で領域を設定する
この引数として渡される矩形領域がフォーカスを合わせるのに使われる領域となる
Listなので複数渡せるが、今回は1つだけに限ったので2つ以上の時は正直よく分かってない

Camera.Areaは、矩形領域を表すRect rectint weightからなる
rectにフォーカスを合わせたい矩形領域をセットする。値域が[-1000, 1000]となっており、カメラ中央を(0,0)、左上を(-1000,-1000)、右下を(1000,1000)としている
ここでいう上下左右は横置きを基準としているので、端末を縦置きにしてカメラを扱う場合は注意
weightは2つ以上の領域がある時に強弱を付けるために使う…? とりあえず固定値にしてある

ちなみに、現在セットされてる領域が知りたいならgetFocusAreas()でList<Camera.Area>が取得できる

サンプルコード

// --上のサンプルコードで(A)と命名した所に書く--
Parameters params = mCamera.getParameters();
// 対応しているかチェック
if( params.getMaxNumFocusAreas() > 0 ) {
    int x, y;
    // 画面縦置きの場合
    x = (int)event.getY();
    y = (mSurfaceView.getWidth()-(int)event.getX());
    // 画面横置きの場合はこうなる…?(未検証)
//  x = (int)event.getX();
//  y = (int)event.getY();

    // タッチした座標を[-1000,1000]の範囲に落としこむ
    int fx = x * 2000 / params.getPreviewSize().width - 1000;
    int fy = y * 2000 / params.getPreviewSize().width - 1000;

    // 上記の(x,y)を中央とした100x100の矩形領域を設定することにする
    // 矩形の端が[-1000,1000]を出ないように調整
    if( fx < -950 ) fx = -950;
    if( fx > 950  ) fx =  950;
    if( fy < -950 ) fy = -950;
    if( fy > 950  ) fy =  950;

    // 矩形領域をセット
    List<Camera.Area> area = new ArrayList<Camera.Area>();
    area.add(new Camera.Area(new Rect(fx-50, fy-50, fx+50, fy+50), 1));
    params.setFocusAreas(area);
    mCamera.setParameters(params);

    // 最後にオートフォーカス呼び出し
    mCamera.autoFocus(/* 2度押し禁止とかしなきゃならんので何かしらのリスナーを入れることになると思うが割愛 */);
}

最後に

サンプルコードの最後にも書いたが、2連続でタッチするとオートフォーカス中にオートフォーカスが呼ばれ、 アプリが落ちる原因となる
これに関する対策は色々な所に書かれてるので省略 (例えばこことか)

参考

Android Developers
Y.A.Mの雑記帳: Android 4.0 Platform

追記

(2014/7/10) 2つ目のサンプルコードの「int x = ・・・」の式の右辺に-1を掛けました。
これにより、端末が横向きでも縦向きでも左上が(-1000,-1000)になります。
(2014/7/12) 2つ目のサンプルコードを修正しました。よく考えたら間違ってました

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away