[Android] 相機自動定焦拍照(Camera Preview)

Android裡如果要使用相機,並不是直接引用控制項,而是利用SurfaceView來接收相機的預覽的資料。所以在View裡先放一個SurfaceView,另外準備一個ImageView來放等等要拍的圖片;另外要開啟使用相機的權限以及啟用自動定焦功能

開發環境:Android 2.2

Android裡如果要使用相機,並不是直接引用控制項,而是利用SurfaceView來接收相機的預覽的資料。所以在View裡先放一個SurfaceView,另外準備一個ImageView來放等等要拍的圖片;另外要開啟使用相機的權限以及啟用自動定焦功能。

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

 

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/frameLayout1" android:layout_height="fill_parent"
	android:layout_width="fill_parent">
	<SurfaceView android:id="@+id/cameraSurfaceView"
		android:layout_width="fill_parent" android:layout_height="fill_parent"
		android:layout_centerInParent="true"></SurfaceView>
	<LinearLayout android:id="@+id/linearLayout"
		android:orientation="vertical" android:layout_width="fill_parent"
		android:layout_height="fill_parent">
		<ImageView android:id="@+id/cameraImage"
			android:layout_width="fill_parent" android:layout_height="wrap_content"></ImageView>
	</LinearLayout>
	<TextView android:layout_width="wrap_content" android:id="@+id/helpText"
		android:text="請將條碼放置於鏡頭範圍內進行掃描。" android:layout_height="wrap_content"
		android:layout_gravity="bottom|center_horizontal"></TextView>
</FrameLayout>

 

為了確保Activity不因為轉向一直重啟,所以我把他的畫面利用setRequestedOrientation固定下來,另外因為相機的畫面一開始是橫向,為了讓他看起來跟手持時一樣,所以我先轉了90度:

public class CameraPreviewActivity extends Activity implements SurfaceHolder.Callback
{
	private SurfaceHolder surfaceHolder;
	private Camera myCamera;
	private SurfaceView surfaceView;
	private ImageView imageView;
	private CamerTimerTask camerTimerTask;
	private Timer timer;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.camera);
		//定向
		this.setRequestedOrientation(1);
		findControl();
	}

	private void findControl()
	{
		surfaceView = (SurfaceView) findViewById(R.id.cameraSurfaceView);
		surfaceHolder = surfaceView.getHolder();
		surfaceHolder.addCallback(this);
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	}
	

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
	{
		Camera.Parameters parameters = myCamera.getParameters();
		parameters.setFocusMode("auto");
		myCamera.setParameters(parameters);
		myCamera.startPreview();
		isCameraOpen = true;
		
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder)
	{
		
		try
		{
			myCamera = Camera.open();
			myCamera.setPreviewDisplay(surfaceHolder);
			//鏡頭的方向和手機相差90度,所以要轉向
			myCamera.setDisplayOrientation(90);

		}
		catch (IOException e)
		{
			myCamera.release();
			myCamera = null;
		}

	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder)
	{
		myCamera.stopPreview();
		isCameraOpen = false;
		myCamera.release();
		myCamera = null;

	}

}

 

以上就可以看到預覽畫面了,至於定焦的事件則可以利用Camera.autoFocus的事件來處理,這個事件要加在startPreview之後,當定焦之後進行事件處理:

(autoFocus只會執行一次,所以如果你要一直讓他去對焦,就要透過其它方法,例如Timer去觸發進行對焦)

AutoFocusCallback autoFacusCallback = new AutoFocusCallback()
{

    @Override
    public void onAutoFocus(boolean success, Camera camera)
    {
        Log.i(Config.Tag, "onAutoFocus:" + success);
        if (success && isCameraOpen)
        {
            camera.setOneShotPreviewCallback(previewCallback);

        }
    }
};

不過我們得實作一個符合Camera.PreviewCallback的類別來接資料流~還有個要特別注意的,圖片格式是NV21,需要先經過轉換才可以使用,在2.2有提供了YuvImage可以進行轉換,在2.2之前就得自己轉啦~

 

private class CameraPreviewCallback  implements Camera.PreviewCallback
{
	@Override
	public void onPreviewFrame(byte[] data, Camera camera)
	{
		if (data != null)
		{
			Camera.Parameters parameters = camera.getParameters();
			int imageFormat = parameters.getPreviewFormat();
			Log.i("map", "Image Format: " + imageFormat);

			Log.i("CameraPreviewCallback", "data length:" + data.length);
			if (imageFormat == ImageFormat.NV21)
			{
				// get full picture
				
				Bitmap image = null;
				int w = parameters.getPreviewSize().width;
				int h = parameters.getPreviewSize().height;
				  
				Rect rect = new Rect(0, 0, w, h); 
				YuvImage img = new YuvImage(data, ImageFormat.NV21, w, h, null);
				ByteArrayOutputStream baos = new ByteArrayOutputStream();
				  
				if (img.compressToJpeg(rect, 100, baos)) 
				{ 
					image =  BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.size());
					imageView.setImageBitmap(image);
				}
		
			}
		}
	}
}

我曾經試著去設定Preview接回來的格式是JPEG :parameters.setPreviewFormat(ImageFormat.JPEG),不過直接利用BitmapFactory去接回來轉圖的時候竟然還是null,後來利用函式查了一下支援格式,發現只有NV21可以用,所以設了等於白設。相關討論可以看一下這篇:onPreviewFrame BitmapFactory.decodeByteArray always return null

 

相關連結:

YuvImage

Camera

 

Dotblogs 的標籤: ,