Android 自定義Camera(一)

公司一直在作一款好玩的應用名叫Funny, 是一個以萌和可愛爲主的圖片社區,P圖工具(歡迎你們下載玩耍哈),剛開始調用的相機是調用系統的相機, 這確定不能知足一款應用的需求,So我開始了從零的自定義相機,發現github csdn等等上面關於自定義相機的資料不是不少,固然也有比較好的,這裏我也是從開始能預覽到後面的一些細節處理踩了一些坑,我會一步一步的分享粗來下面就開始從頭開始作個本身的相機吧。java

獲取相機和釋放相機

首先相機實例只能有一個,拍照以後必定要釋放,那麼它也有了本身的生命週期, 首先獲取相機,這裏傳入一個ID,這個id能夠設前置攝像頭和後置攝像頭android

/** * 獲取Camera實例 * @return */
    private Camera getCamera(int id){
        Camera camera = null;
        try{
            camera = Camera.open(id);
        }catch (Exception e){

        }
        return camera;
    }

/** * 釋放相機資源 */
    private void releaseCamera(){
        if(mCamera != null){
            mCamera.setPreviewCallback(null);
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

/** * 預覽相機 */
    private void startPreview(Camera camera, SurfaceHolder holder){
        try {
        //這裏要設置相機的一些參數,下面會詳細說下
            setupCamera(camera);
            camera.setPreviewDisplay(holder);
            //親測的一個方法 基本覆蓋全部手機 將預覽矯正
            CameraUtil.getInstance().setCameraDisplayOrientation(this, mCameraId, camera);
// camera.setDisplayOrientation(90);
            camera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
複製代碼

使用以後釋放相機資源,這裏這樣寫是有緣由的,一步都不能少,我有時候會忘記寫mCamera.setPreviewCallback(null); 會報Method called after release 錯誤, 由於相機要實時的預覽,那普通的View就不能知足繪製的要求了,這裏要用的一個雙緩衝機制的SurfaceView,下面咱們要作的就是將Camera和SurfaceView綁定起來, 那就須要另外一個類了SurfaceHolder, **這裏要特別提的是CameraUtil.getInstance().setCameraDisplayOrientation(this, mCameraId, camera); **後面我會把源碼地址寫上, 因爲安卓系統默認預覽都是橫着的,基本操做的是camera.setDisplayOrientation(90);直接旋轉90度矯正, 可是本人在開發的時候就是遇到一些手機上預覽反過來了, 最後用這個方法解決了, 它的基本原理就是有的手機系統底層對預覽進行了矯正,有的沒有,那經過cameraInfo能夠判斷到。SO這個完美的解決了個人問題, 但願也能幫到你.git

設置SurfaceHolder

surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
mHolder = surfaceView.getHolder();
mHolder.addCallback(this);
複製代碼

這裏SurfaceHolder要添加一個回調方法,看註釋github

@Override
public void surfaceCreated(SurfaceHolder holder) {
 //在surface建立的時候開啓相機預覽
 startPreview(mCamera, holder);
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
 //在相機改變的時候調用此方法, 此時應該先中止預覽, 而後從新啓動
 mCamera.stopPreview();
 startPreview(mCamera, holder);
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
 //在destroy的時候釋放相機資源
 releaseCamera();
}
複製代碼

這裏要詳細說下setCamera方法,不加這個方法固然也能夠進行正常的預覽, 可是這個方法要設置相機的預覽尺寸,返回圖片尺寸,特別要注意的是,預覽尺寸返回圖片尺寸和surfaceView的尺寸比例必須是同樣的,要不預覽或者拍照返回的圖片會變形或者程序崩潰, 下面看代碼:bash

/**
 * 設置
 */
private void setupCamera(Camera camera) {
 Camera.Parameters parameters = camera.getParameters();

 List < String > focusModes = parameters.getSupportedFocusModes();
 if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
  // Autofocus mode is supported 自動對焦
  parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
 }

 //這裏第三個參數爲最小尺寸 getPropPreviewSize方法會對從最小尺寸開始升序排列 取出全部支持尺寸的最小尺寸
 Camera.Size previewSize = CameraUtil.getInstance().getPropPreviewSize(parameters.getSupportedPreviewSizes(), 1000);
 parameters.setPreviewSize(previewSize.width, previewSize.height);

 Camera.Size pictrueSize = CameraUtil.getInstance().getPropPictureSize(parameters.getSupportedPictureSizes(), 1000);
 parameters.setPictureSize(pictrueSize.width, pictrueSize.height);

 camera.setParameters(parameters);

 Log.d("previewSize.width===", previewSize.width + "");
 Log.d("previewSize.height===", previewSize.height + "");

 /**
  * 設置surfaceView的尺寸 由於camera默認是橫屏,因此取得支持尺寸也都是橫屏的尺寸
  * 咱們在startPreview方法裏面把它矯正了過來,可是這裏咱們設置設置surfaceView的尺寸的時候要注意 previewSize.height<previewSize.width
  * previewSize.width纔是surfaceView的高度
  * 通常相機都是屏幕的寬度 這裏設置爲屏幕寬度 高度自適應 你也能夠設置本身想要的大小
  */
 FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(screenWidth, screenWidth * previewSize.width / previewSize.height);
 //這裏固然能夠設置拍照位置 好比居中 我這裏就置頂了
 //params.gravity = Gravity.CENTER;
 surfaceView.setLayoutParams(params);
}
複製代碼

這裏還有個地方要特別注意,就是你設置預覽尺寸和返回圖片尺寸必需要是當前手機支持的尺寸,這裏經過getPropPictureSize和getPropPreviewSize獲取全部手機支持的尺寸,默認的相機預覽都是橫屏的, 這裏設置一個最小的預覽寬度1000, 只要大於1000的支持尺寸均可以。 這裏還有最後一步也是很是的重要, 就是Camera的生命週期必定要和當前Activity綁定起來, 因此咱們這樣作:ide

@Override
protected void onResume() {
 super.onResume();
 if (mCamera == null) {
  mCamera = getCamera(mCameraId);
  if (mHolder != null) {
   startPreview(mCamera, mHolder);
  }
 }
}

@Override
protected void onPause() {
 super.onPause();
 releaseCamera();
}
複製代碼

這裏在Activity要展現的時候啓動相機預覽,在Activity onPause的時候釋放相機資源就能夠了。那如今能夠預覽了也要能夠拍照哇,接下來來個拍照方法:工具

private void captrue() {
 //參數說明 
 mCamera.takePicture(null, null, new Camera.PictureCallback() {
  @Override
  public void onPictureTaken(byte[] data, Camera camera) {
   //將data 轉換爲位圖 或者你也能夠直接保存爲文件使用 FileOutputStream
   //這裏我相信大部分都有其餘用處把 好比加個水印 後續再講解
   Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
   CameraUtil.getInstance().setTakePicktrueOrientation(mCameraId, bitmap);
   //這裏打印寬高 就能看到 CameraUtil.getInstance().getPropPictureSize(parameters.getSupportedPictureSizes(), 200);
   // 這設置的最小寬度影響返回圖片的大小 因此這裏通常這是1000左右把我以爲
   Log.d("bitmapWidth==", bitmap.getWidth() + "");
   Log.d("bitmapHeight==", bitmap.getHeight() + "");
  }
 });
}
複製代碼

這裏有個地方要說一下,上面咱們對預覽進行了矯正, 那生成的圖片咱們也要矯正回來啊, 執行CameraUtil.getInstance().setTakePicktrueOrientation(mCameraId, bitmap); 固然也是經過android.hardware.Camera.CameraInfo來操做的。this

我以爲一篇文章不宜太長,太長看着頭疼哈哈,那到這裏一個簡單的demo差很少就能夠了,其實要注意的地方也挺多的,我都是一個坑一個坑踩過去的,後面我會將切換先後置攝像頭,閃光燈模式,延遲拍攝,正方形取景框,加水印效果等等吧陸續的寫出來.spa

下面是源碼地址github.com/jinguangyue…  Star一下並不虧.code

關注個人 Google Play 獨立開發公衆號, 我將把不少精彩的故事分享到公衆號,掃描下方二維碼和我一塊兒開發 APP 賺美圓吧!

image
相關文章
相關標籤/搜索