公司一直在作一款好玩的應用名叫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
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 賺美圓吧!