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
相關連結: