Android 相機1 之Camera1的最簡單的使用(預覽、拍照、變焦、特效)
Android兩個相機的API的個人總結
API1的方法較少、命名規則等都比較簡單,如果是針對目前市面上的手機,API1是足夠而且使用起來非常方便,尤其是它的setParameter方法,相較於API2的要自己去填key和value來說,它不僅很容易能找到相機支援的(使用parameter.getSupportedXXX可以直接獲得相應引數)尺寸特效白平衡等(key),value值也是比較一目瞭然的;而API2不僅類多,方法多,名字之間很相似,狀態之間調來調去,需要花費較多時間去了解才能正確使用。
但API2也是有好處的,我以為最大的好處就是內部封裝了一個HandlerThread,從而在開啟相機、預覽、尤其是處理圖片的時候速度會快很多。話不多說,這篇文主要是API1.
API1的簡單用法
Camera1這個貌似過時,實際大家都在用,用法超級簡單,在Activity onCreate的時候初始化SurfaceView或者TextureView,如果使用SurfaceView要得到它的holder,並設定holder的Type,然後在onCreate或者onResume方法中開啟並初始化相機引數,如對焦模式等,監聽SurfaceView Holder或Textureview的狀態變化回撥方法,都是在surfaceCreated或者onSurfaceTextureAvaliable方法中設定相機的previewSurface或者previewTexture,再呼叫相機的startPreview方法即可實現實時預覽。這裡首先記住要開啟相機的許可權,另外,如果是急性子要多次執行看結果的,記得現在就在onPauce或者onDestory方法中釋放相機資源,免得下次打開不了相機。
//設定全屏顯示
private void setFullScreen() {
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setFlags(flag,flag);
}
//開啟相機
private void initCamera() {
cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
if(camera==null){
camera = Camera.open(cameraId);
}
}
或者最簡單的一個不傳參,預設開啟後攝像頭
private void initCamera() {
if(camera==null){
camera = Camera.open();
}
}
// 設定初始相機引數
@Override
protected void onResume() {
super.onResume();
setCameraDisplayOrientation();
param = camera.getParameters();
param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
param.setPictureFormat(ImageFormat.JPEG);
param.setJpegQuality(100);
param.setPreviewSize(param.getSupportedPreviewSizes().get(0).width,param.getSupportedPreviewSizes().get(0).height);
Log.e(TAG,”param.getSupportedPreviewSizes()”+param.getSupportedPreviewSizes().get(0).width+” X “+param.getSupportedPreviewSizes().get(0).height);
param.setPictureSize(param.getSupportedPictureSizes().get(0).width,param.getSupportedPictureSizes().get(0).height);
camera.setParameters(param);
}
預覽時最好是設定對焦模式為Camera.Parameters.FOCUS_MODE_CONTINOUS_PICTURE,這樣一般的拍攝字型之類的都會比較清晰而且會持續對焦;如果有拍照需求,在佈局檔案新增button實現點選監聽方法中直接呼叫camera.takePciture(null,null,pictureCallBack)方法,第一個null是拍攝聲音為靜音,第二個拍照為raw格式,這裡也為null,第三個是要重寫回調方法,裡面直接呼叫takePicture方法有引數為byte[]data,這就是傳遞過來的照片資料,可以直接寫入檔案儲存得到照片,也可以用bitmapFactory得到照片並顯示。這裡儲存要開啟sdcard的寫入許可權。
這時候有了照片了,但有的相機可能因為沒有設定previewsize和picturesize圖片非常的小和模糊,但如果是隨便設定size有可能會導致黑屏不顯示預覽。Camera1裡有一系列的parameter.getSupportXXX方法,可以得到相機支援的各種引數,然後再設定進去。
同樣,放大縮小方法,直接呼叫parameter.setZoom(zoomValue)即可,還有各種特效、白平衡等,最後一定記得要camera.setParameter(parameter)把設定好了的各種引數再傳遞給相機才能生效。
另外還有鏡頭翻轉的問題,可以直接camera.setDisplayOrientation(90);粗暴解決,可以適合一部分機型,這裡還有比較穩妥的解決預覽角度不對問題的程式碼、以及設定合適previewSize、合適pictureSize、放大縮小、特效白平衡、儲存圖片的程式碼,可以直接放在合適的地方使用。
//設定相機預覽展示正確角度
private void setCameraDisplayOrientation() {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = 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;
}
camera.setDisplayOrientation(result);
}
SurfaceView以及它的回撥方法也在MainActivity中了,邏輯較少,所以耦合比較高,後面會上傳分開的程式碼: protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setFullScreen(); setContentView(R.layout.activity_main); initCamera(); initView(); } private void initView() { surview = findViewById(R.id.surview); holder = surview.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); btn_zoom = findViewById(R.id.btn_zoom); btn_effect = findViewById(R.id.btn_effect); btn_capture = findViewById(R.id.btn_capture); } private void initCamera() { cameraId = Camera.CameraInfo.CAMERA_FACING_BACK; if(camera==null){ camera = Camera.open(cameraId); } } private void setCameraDisplayOrientation() { android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); android.hardware.Camera.getCameraInfo(cameraId, info); int rotation = 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; } camera.setDisplayOrientation(result); } private void setFullScreen() { getWindow().requestFeature(Window.FEATURE_NO_TITLE); int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; getWindow().setFlags(flag,flag); } @Override public void surfaceCreated(SurfaceHolder holder) { this.holder = holder; try { camera.setPreviewDisplay(holder); camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { this.holder = holder; } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override protected void onResume() { super.onResume(); setCameraDisplayOrientation(); param = camera.getParameters(); param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); param.setPictureFormat(ImageFormat.JPEG); param.setJpegQuality(100); param.setPreviewSize(param.getSupportedPreviewSizes().get(0).width,param.getSupportedPreviewSizes().get(0).height); Log.e(TAG,"param.getSupportedPreviewSizes()"+param.getSupportedPreviewSizes().get(0).width+" X "+param.getSupportedPreviewSizes().get(0).height); param.setPictureSize(param.getSupportedPictureSizes().get(0).width,param.getSupportedPictureSizes().get(0).height); camera.setParameters(param); } @Override protected void onPause() { super.onPause(); if(camera!=null){ camera.release(); camera=null; } } private Camera.PictureCallback pictureCallBack = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { savePicture(data); } }; private void savePicture(byte[] data) { String root = Environment.getExternalStorageDirectory().getPath(); Log.e(TAG,"Environment.getExternalStorageState();"+root); File filedir = new File(root,"CAMERAV1"); Log.e(TAG,"new File(root,\"CAMERAV1\");"+filedir.getPath()); if(!filedir.exists()){ if(!filedir.mkdirs()){ Toast.makeText(this,"不能在sdcard上建立相關檔案",Toast.LENGTH_SHORT).show(); } } String temp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); File objPic = new File(filedir,"IMG_"+temp+".jpg"); BufferedOutputStream bos = null; try { bos = new BufferedOutputStream(new FileOutputStream(objPic)); bos.write(data); bos.flush(); Toast.makeText(this,"拍照成功",Toast.LENGTH_SHORT).show(); } catch (IOException e) { e.printStackTrace(); }finally { if(bos!=null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } } } public void doClick(View view) { switch (view.getId()){ case R.id.btn_capture: camera.takePicture(null,null,pictureCallBack); break; case R.id.btn_effect: param = camera.getParameters(); //測試的 param.setColorEffect(param.getSupportedColorEffects().get(1)); camera.setParameters(param); break; case R.id.btn_zoom: param = camera.getParameters(); param.setZoom(param.getZoom()+5); camera.setParameters(param); break; } }