ImageReader
拍照前, 需先設定ImageReader, 在參數中設定拍照解析度. 並設定setOnImageAvailableListener.
imageReader=ImageReader.newInstance(maxWidth, maxHeight, ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener(imageReaderReady, cameraHandler); captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureRequest.addTarget(imageReader.getSurface());
當照片完成後, 會先調用onImageAvailable方法. 請注意, 在開始讀取照片資料時, 需先停止一段時間, 不然會閃退. 這是一個坑, 原因不明.
private ImageReader.OnImageAvailableListener imageReaderReady=new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader imageReader) {
//此處有坑, 不停頓的話會當機, 網路上說要停1秒, 原因不明
SystemClock.sleep(300);
Image image=imageReader.acquireNextImage();
if(image!=null) {
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
Bundle bundle = new Bundle();
bundle.putByteArray("bitmapByteArray", bytes);
Message msg = new Message();
msg.what = MahalCamera2.CAMERA_BITMAP_READY;
msg.setData(bundle);
customHandler.sendMessage(msg);
}
}
};
啟動拍照
啟動拍照, 需使用cameraCaptureSession.capture方法, 但在啟動前, 需將session停止, 分別是使用cameraCaptureSession.stopRepeating及cameraCaptureSession.abortCaptures, 若沒有停止, 會造成意外閃退.
拍完照後, 並不是直接調用callback, 而是先執行setOnImageAvailable, 等執行完畢, 才會調用callback的onCaptureCompleted方法.
拍完照片後, 再進入preview模式
try { cameraCaptureSession.stopRepeating(); cameraCaptureSession.abortCaptures(); if (cameraFlash) { captureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } else { captureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); } captureRequest.set(CaptureRequest.JPEG_QUALITY, (byte)100); cameraCaptureSession.capture(captureRequest.build(), new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { Camera2Engine.this.captureResult=result; stillAvailable = true; startPreview(); } }, cameraHandler); } catch (CameraAccessException e) {}
拍照品質
在執行capture前, 需先設定照片品質
captureRequest.set(CaptureRequest.JPEG_QUALITY, (byte)100);
存檔
ImageReader讀取照片後, 是以Image格式存放. 網路上都是說明由Image取得byte[] array後, 放入Bitmap中, 再由Bitmap.compress將圖片存放在sd卡中. 但這種方法需花費大量時間, 以一張5M左右的照片, 約需使用3~4秒中.
為了解決這個issue, 建議把byte[] array傳出給主執行緒. 而後在主執行緒中直接使用FileOutputStream將byte[] 資料儲存即可, 如下
byte[] bytes=bundle.getByteArray("bitmapByteArray"); File file=new File(G.appPath+"/"+System.currentTimeMillis()+".jpg"); OutputStream os=new FileOutputStream(file); os.write(bytes); os.close();
Thumbnail
經由上面的方式儲存後, 並沒有縮圖. 若要顯示縮圖, 需再調用MediaScannerConnection.scanFile
MediaScannerConnection.scanFile(MainActivity.this, new String[] { file.getAbsolutePath() }, null, new MediaScannerConnection.OnScanCompletedListener() { public void onScanCompleted(String path, Uri uri) { MainActivity.this.sendBroadcast(new Intent("android.hardware.action.NEW_PICTURE" , uri)); MainActivity.this.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri)); } } );
請注意, 若未調用MediaScannerConnection, 除了沒有縮圖外, 照片品質還會相當的差, 原因不明.尤其用不是很高級的相機所拍出的照片, 在圖片放大後的差異會更大.
下圖是有進行縮圖的處理
下圖為沒有處理過的