摘要:文章提供了可以工作的例項,介紹了在應用中使用安卓Camera SurfaceView拍照的實現細節。
這是使用Fragment在Android Studio開發相機應用系列文章5篇中的第4篇。如果你沒有讀過之前的文章,先從GitHub上覆制一份我的工程,這篇教程主要討論”NativeCameraFragment“。
作者語:由於裝置廠商龐多繁雜,不同版本相機API不同,一個完整健壯的相機應用是很複雜的。如果你想實現一個完整可行的,可以考慮以CWAC Camera為基礎進行開發。我的目標僅僅是演示相機開發的一些基礎。
除了安卓的原生相機應用,你也可以在自己的專案中使用相機功能。這涉及到直接利用安卓相機介面並把結果投射到“預覽”介面或者SurfaceView上。
開啟相機
一旦Fragment檢視物件被建立,首先做的就是把相機和相機預覽介面關聯起來。你可以在谷歌的開發者網站上閱讀相關的文件,下面是一個參考實現:
1 2 3 4 5 6 7 8 9 10 |
private boolean safeCameraOpenInView(View view) { boolean qOpened = false; releaseCameraAndPreview(); mCamera = getCameraInstance(); qOpened = (mCamera != null); mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera); FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_preview); preview.addView(mPreview); return qOpened; } |
該函式用來獲取一個相機的例項物件並和我們的CameraPreview物件關聯起來。
相機預覽介面
例子中的CameraPreview 物件繼承了SurfaceView 並實現了SurfaceHolder.callback介面,預覽介面將顯示相機的輸入。在你看到正在拍攝的畫面的同時,它將處理SurfaceHolder的生命週期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
/** * Surface on which the camera projects it's capture results. */ class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { SurfaceHolder mHolder; Camera mCamera; public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the preview. try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } public void surfaceDestroyed(SurfaceHolder holder) { // empty. Take care of releasing the Camera preview in your activity. } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // If your preview can change or rotate, take care of those events here. // Make sure to stop the preview before resizing or reformatting it. if (mHolder.getSurface() == null){ // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e){ // ignore: tried to stop a non-existent preview } // set preview size and make any resize, rotate or // reformatting changes here // start preview with new settings try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e){ e.printStackTrace(); } } } |
相機拍照回撥
Camera.PictureCallback是安卓用來判斷何時相機拍攝了照片,並且獲取了輸出結果的回撥介面。在我的示例專案工程中實現如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
private Camera.PictureCallback mPicture = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { File pictureFile = getOutputMediaFile(); if (pictureFile == null){ Toast.makeText(getActivity(), "Image retrieval failed.", Toast.LENGTH_SHORT) .show(); return; } try { FileOutputStream fos = new FileOutputStream(pictureFile); fos.write(data); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }; |
免責宣告和補充
文章一發布我就從安卓相機跨裝置開發專家那得到了反饋,我的目標是提供高質量的內容,所以不說清楚下面的這些問題文章就不完整 。
在我的例子中假定裝置支援jpeg格式,這種假定存在風險,真實的實現需要檢查是否支援jpeg格式。另一個問題 CameraHolder.PreviewCallback介面直到API版本18許多廠商才支援,這使得API 18以前PreviewCallback 將難以起作用。
此例中,我使用的預設CameraPreview 格式是NV21,已經有人推薦我使用YV12(從API 12開始支援),一定要檢查下相機支援的CameraPreview格式。實現格式因裝置廠商不同而不同,是不一致行為的潛在根源。
還有包括閃光燈和聚焦模式在內的很多細節問題沒有探討,這些可能需要設定一些相機的引數,我已經在GitHub上更新了程式碼。
以上就是我直接使用安卓原生相機的例子,隨便獲取例子程式碼,同樣,如果你覺得這篇文章對你有幫助歡迎分享。非常感謝。